From bf055556f16216d941dfb0f3caccafde44337c51 Mon Sep 17 00:00:00 2001 From: woodser Date: Mon, 14 Apr 2025 20:45:15 -0400 Subject: [PATCH 01/74] fix offers being deleted after minimum version update --- core/src/main/java/haveno/core/offer/OpenOfferManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/haveno/core/offer/OpenOfferManager.java b/core/src/main/java/haveno/core/offer/OpenOfferManager.java index e5bf40b241..1dec0aff56 100644 --- a/core/src/main/java/haveno/core/offer/OpenOfferManager.java +++ b/core/src/main/java/haveno/core/offer/OpenOfferManager.java @@ -2023,7 +2023,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe originalOfferPayload.getAcceptedCountryCodes(), originalOfferPayload.getBankId(), originalOfferPayload.getAcceptedBankIds(), - originalOfferPayload.getVersionNr(), + Version.VERSION, originalOfferPayload.getBlockHeightAtOfferCreation(), originalOfferPayload.getMaxTradeLimit(), originalOfferPayload.getMaxTradePeriod(), From c87b8a5b45c0ff513c07cd0562dbf66fc61eb58b Mon Sep 17 00:00:00 2001 From: woodser Date: Mon, 14 Apr 2025 21:14:46 -0400 Subject: [PATCH 02/74] add note to keep arbitrator key in the repo to preserve signed accounts --- docs/deployment-guide.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/deployment-guide.md b/docs/deployment-guide.md index 67b5236d33..e53de210ed 100644 --- a/docs/deployment-guide.md +++ b/docs/deployment-guide.md @@ -194,6 +194,9 @@ The arbitrator is now registered and ready to accept requests for dispute resolu 1. Start the arbitrator's desktop application using the application launcher or e.g. `make arbitrator-desktop-mainnet` from the root of the repository. 2. Go to the `Account` tab and click the button to unregister the arbitrator. +> **Note** +> To preserve signed accounts, the arbitrator public key must remain in the repository, even after revoking. + ## Set a network filter on mainnet On mainnet, the p2p network is expected to have a filter object for offers, onions, currencies, payment methods, etc. From 60ceff66955f0be9a9659477f380dce6bfb6a61f Mon Sep 17 00:00:00 2001 From: woodser <13068859+woodser@users.noreply.github.com> Date: Tue, 15 Apr 2025 16:57:19 -0400 Subject: [PATCH 03/74] fix redundant key image notifications --- .../haveno/core/xmr/wallet/XmrKeyImagePoller.java | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/haveno/core/xmr/wallet/XmrKeyImagePoller.java b/core/src/main/java/haveno/core/xmr/wallet/XmrKeyImagePoller.java index 1cde84152c..3bebb71b7f 100644 --- a/core/src/main/java/haveno/core/xmr/wallet/XmrKeyImagePoller.java +++ b/core/src/main/java/haveno/core/xmr/wallet/XmrKeyImagePoller.java @@ -144,7 +144,6 @@ public class XmrKeyImagePoller { if (!keyImageGroups.containsKey(groupId)) keyImageGroups.put(groupId, new HashSet()); Set keyImagesGroup = keyImageGroups.get(groupId); keyImagesGroup.addAll(keyImages); - refreshPolling(); } } @@ -159,8 +158,13 @@ public class XmrKeyImagePoller { if (keyImagesGroup == null) return; keyImagesGroup.removeAll(keyImages); if (keyImagesGroup.isEmpty()) keyImageGroups.remove(groupId); + Set allKeyImages = getKeyImages(); synchronized (lastStatuses) { - for (String lastKeyImage : new HashSet<>(lastStatuses.keySet())) lastStatuses.remove(lastKeyImage); + for (String keyImage : keyImages) { + if (lastStatuses.containsKey(keyImage) && !allKeyImages.contains(keyImage)) { + lastStatuses.remove(keyImage); + } + } } refreshPolling(); } @@ -171,10 +175,10 @@ public class XmrKeyImagePoller { Set keyImagesGroup = keyImageGroups.get(groupId); if (keyImagesGroup == null) return; keyImageGroups.remove(groupId); - Set keyImages = getKeyImages(); + Set allKeyImages = getKeyImages(); synchronized (lastStatuses) { for (String keyImage : keyImagesGroup) { - if (lastStatuses.containsKey(keyImage) && !keyImages.contains(keyImage)) { + if (lastStatuses.containsKey(keyImage) && !allKeyImages.contains(keyImage)) { lastStatuses.remove(keyImage); } } @@ -265,6 +269,7 @@ public class XmrKeyImagePoller { // announce changes if (!changedStatuses.isEmpty()) { + log.info("Announcing " + changedStatuses.size() + " key image spent status changes"); for (XmrKeyImageListener listener : new ArrayList(listeners)) { listener.onSpentStatusChanged(changedStatuses); } From 22db354cb2254925ec5799d9578fd5ed2d238671 Mon Sep 17 00:00:00 2001 From: woodser <13068859+woodser@users.noreply.github.com> Date: Wed, 16 Apr 2025 10:06:03 -0400 Subject: [PATCH 04/74] do not re-filter offers for every offer book change --- .../haveno/core/offer/OfferBookService.java | 28 +++++++++---------- .../offer/offerbook/OfferBookViewModel.java | 8 ++++-- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/core/src/main/java/haveno/core/offer/OfferBookService.java b/core/src/main/java/haveno/core/offer/OfferBookService.java index 16faa81e57..50981a8fa6 100644 --- a/core/src/main/java/haveno/core/offer/OfferBookService.java +++ b/core/src/main/java/haveno/core/offer/OfferBookService.java @@ -149,6 +149,20 @@ public class OfferBookService { Offer offer = new Offer(offerPayload); offer.setPriceFeedService(priceFeedService); announceOfferRemoved(offer); + + // check if invalid offers are now valid + synchronized (invalidOffers) { + for (Offer invalidOffer : new ArrayList(invalidOffers)) { + try { + validateOfferPayload(invalidOffer.getOfferPayload()); + removeInvalidOffer(invalidOffer.getId()); + replaceValidOffer(invalidOffer); + announceOfferAdded(invalidOffer); + } catch (Exception e) { + // ignore + } + } + } } }); }, OfferBookService.class.getSimpleName()); @@ -298,20 +312,6 @@ public class OfferBookService { synchronized (offerBookChangedListeners) { offerBookChangedListeners.forEach(listener -> listener.onRemoved(offer)); } - - // check if invalid offers are now valid - synchronized (invalidOffers) { - for (Offer invalidOffer : new ArrayList(invalidOffers)) { - try { - validateOfferPayload(invalidOffer.getOfferPayload()); - removeInvalidOffer(invalidOffer.getId()); - replaceValidOffer(invalidOffer); - announceOfferAdded(invalidOffer); - } catch (Exception e) { - // ignore - } - } - } } private boolean hasValidOffer(String offerId) { 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 821b081454..9a52902f3e 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 @@ -174,9 +174,11 @@ abstract class OfferBookViewModel extends ActivatableViewModel { tradeCurrencyListChangeListener = c -> fillCurrencies(); // refresh filter on changes - offerBook.getOfferBookListItems().addListener((ListChangeListener) c -> { - filterOffers(); - }); + // TODO: This is removed because it's expensive to re-filter offers for every change (high cpu for many offers). + // This was used to ensure offer list is fully refreshed, but is unnecessary after refactoring OfferBookService to clone offers? + // offerBook.getOfferBookListItems().addListener((ListChangeListener) c -> { + // filterOffers(); + // }); filterItemsListener = c -> { final Optional highestAmountOffer = filteredItems.stream() From bbfc5d5fedaf9015105696b5237b56ccf60b3c16 Mon Sep 17 00:00:00 2001 From: woodser <13068859+woodser@users.noreply.github.com> Date: Wed, 16 Apr 2025 11:11:31 -0400 Subject: [PATCH 05/74] remove max version verification so arbitrators can be behind --- .../src/main/java/haveno/core/offer/OpenOfferManager.java | 8 -------- 1 file changed, 8 deletions(-) diff --git a/core/src/main/java/haveno/core/offer/OpenOfferManager.java b/core/src/main/java/haveno/core/offer/OpenOfferManager.java index 1dec0aff56..c857ce533e 100644 --- a/core/src/main/java/haveno/core/offer/OpenOfferManager.java +++ b/core/src/main/java/haveno/core/offer/OpenOfferManager.java @@ -1574,14 +1574,6 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe } } - // verify the max version number - if (Version.compare(request.getOfferPayload().getVersionNr(), Version.VERSION) > 0) { - errorMessage = "Offer version number is too high: " + request.getOfferPayload().getVersionNr() + " > " + Version.VERSION; - log.warn(errorMessage); - sendAckMessage(request.getClass(), peer, request.getPubKeyRing(), request.getOfferId(), request.getUid(), false, errorMessage); - return; - } - // verify maker and taker fees boolean hasBuyerAsTakerWithoutDeposit = offer.getDirection() == OfferDirection.SELL && offer.isPrivateOffer() && offer.getChallengeHash() != null && offer.getChallengeHash().length() > 0 && offer.getTakerFeePct() == 0; if (hasBuyerAsTakerWithoutDeposit) { From 8eccbcce4315c64dfaa0f7aa986fa84d7a9cc699 Mon Sep 17 00:00:00 2001 From: woodser <13068859+woodser@users.noreply.github.com> Date: Wed, 16 Apr 2025 12:37:04 -0400 Subject: [PATCH 06/74] skip offer signature validation for cloned offer until signed --- .../haveno/core/offer/OpenOfferManager.java | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/core/src/main/java/haveno/core/offer/OpenOfferManager.java b/core/src/main/java/haveno/core/offer/OpenOfferManager.java index c857ce533e..a928494c1d 100644 --- a/core/src/main/java/haveno/core/offer/OpenOfferManager.java +++ b/core/src/main/java/haveno/core/offer/OpenOfferManager.java @@ -1101,17 +1101,20 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe } else { // validate non-pending state - try { - validateSignedState(openOffer); - resultHandler.handleResult(null); // done processing if non-pending state is valid - return; - } catch (Exception e) { - log.warn(e.getMessage()); + boolean skipValidation = openOffer.isDeactivated() && hasConflictingClone(openOffer) && openOffer.getOffer().getOfferPayload().getArbitratorSignature() == null; // clone with conflicting offer is deactivated and unsigned at first + if (!skipValidation) { + try { + validateSignedState(openOffer); + resultHandler.handleResult(null); // done processing if non-pending state is valid + return; + } catch (Exception e) { + log.warn(e.getMessage()); - // reset arbitrator signature - openOffer.getOffer().getOfferPayload().setArbitratorSignature(null); - openOffer.getOffer().getOfferPayload().setArbitratorSigner(null); - if (openOffer.isAvailable()) openOffer.setState(OpenOffer.State.PENDING); + // reset arbitrator signature + openOffer.getOffer().getOfferPayload().setArbitratorSignature(null); + openOffer.getOffer().getOfferPayload().setArbitratorSigner(null); + if (openOffer.isAvailable()) openOffer.setState(OpenOffer.State.PENDING); + } } } From 58590d60df8c9b6e96596efffee6a6c2e5e0c332 Mon Sep 17 00:00:00 2001 From: woodser <13068859+woodser@users.noreply.github.com> Date: Wed, 16 Apr 2025 15:58:28 -0400 Subject: [PATCH 07/74] add arbitrator2 mainnet config to Makefile --- Makefile | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/Makefile b/Makefile index 8f32c3ed41..7bca61db60 100644 --- a/Makefile +++ b/Makefile @@ -485,6 +485,31 @@ arbitrator-desktop-mainnet: --xmrNode=http://127.0.0.1:18081 \ --useNativeXmrWallet=false \ +arbitrator2-daemon-mainnet: + ./haveno-daemon$(APP_EXT) \ + --baseCurrencyNetwork=XMR_MAINNET \ + --useLocalhostForP2P=false \ + --useDevPrivilegeKeys=false \ + --nodePort=9999 \ + --appName=haveno-XMR_MAINNET_arbitrator2 \ + --apiPassword=apitest \ + --apiPort=1205 \ + --passwordRequired=false \ + --xmrNode=http://127.0.0.1:18081 \ + --useNativeXmrWallet=false \ + +arbitrator2-desktop-mainnet: + ./haveno-desktop$(APP_EXT) \ + --baseCurrencyNetwork=XMR_MAINNET \ + --useLocalhostForP2P=false \ + --useDevPrivilegeKeys=false \ + --nodePort=9999 \ + --appName=haveno-XMR_MAINNET_arbitrator2 \ + --apiPassword=apitest \ + --apiPort=1205 \ + --xmrNode=http://127.0.0.1:18081 \ + --useNativeXmrWallet=false \ + haveno-daemon-mainnet: ./haveno-daemon$(APP_EXT) \ --baseCurrencyNetwork=XMR_MAINNET \ From 821ef16d8ff67df7569147d1cefe85da1b0ce583 Mon Sep 17 00:00:00 2001 From: woodser Date: Thu, 17 Apr 2025 16:15:26 -0400 Subject: [PATCH 08/74] refresh polling when key images added --- .../src/main/java/haveno/core/xmr/wallet/XmrKeyImagePoller.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/haveno/core/xmr/wallet/XmrKeyImagePoller.java b/core/src/main/java/haveno/core/xmr/wallet/XmrKeyImagePoller.java index 3bebb71b7f..73332dc4fa 100644 --- a/core/src/main/java/haveno/core/xmr/wallet/XmrKeyImagePoller.java +++ b/core/src/main/java/haveno/core/xmr/wallet/XmrKeyImagePoller.java @@ -144,6 +144,7 @@ public class XmrKeyImagePoller { if (!keyImageGroups.containsKey(groupId)) keyImageGroups.put(groupId, new HashSet()); Set keyImagesGroup = keyImageGroups.get(groupId); keyImagesGroup.addAll(keyImages); + refreshPolling(); } } @@ -269,7 +270,6 @@ public class XmrKeyImagePoller { // announce changes if (!changedStatuses.isEmpty()) { - log.info("Announcing " + changedStatuses.size() + " key image spent status changes"); for (XmrKeyImageListener listener : new ArrayList(listeners)) { listener.onSpentStatusChanged(changedStatuses); } From 8f778be4d9e26d4abc614ce719354ddd5522ec91 Mon Sep 17 00:00:00 2001 From: woodser Date: Thu, 17 Apr 2025 16:40:25 -0400 Subject: [PATCH 09/74] show scrollbar as needed when creating offer --- .../desktop/main/offer/MutableOfferView.java | 47 +++++++++++++++---- 1 file changed, 38 insertions(+), 9 deletions(-) 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 31c02bdc0d..e28ecff72b 100644 --- a/desktop/src/main/java/haveno/desktop/main/offer/MutableOfferView.java +++ b/desktop/src/main/java/haveno/desktop/main/offer/MutableOfferView.java @@ -380,8 +380,6 @@ public abstract class MutableOfferView> exten } private void onShowPayFundsScreen() { - scrollPane.setVbarPolicy(ScrollPane.ScrollBarPolicy.AS_NEEDED); - nextButton.setVisible(false); nextButton.setManaged(false); nextButton.setOnAction(null); @@ -445,13 +443,7 @@ public abstract class MutableOfferView> exten // temporarily disabled due to high CPU usage (per issue #4649) // waitingForFundsSpinner.play(); - payFundsTitledGroupBg.setVisible(true); - totalToPayTextField.setVisible(true); - addressTextField.setVisible(true); - qrCodeImageView.setVisible(true); - balanceTextField.setVisible(true); - cancelButton2.setVisible(true); - reserveExactAmountSlider.setVisible(true); + showFundingGroup(); } private void updateOfferElementsStyle() { @@ -986,6 +978,7 @@ public abstract class MutableOfferView> exten gridPane.setVgap(5); GUIUtil.setDefaultTwoColumnConstraintsForGridPane(gridPane); scrollPane.setContent(gridPane); + scrollPane.setVbarPolicy(ScrollPane.ScrollBarPolicy.AS_NEEDED); } private void addPaymentGroup() { @@ -1179,6 +1172,40 @@ public abstract class MutableOfferView> exten cancelButton1.setManaged(false); } + protected void hideFundingGroup() { + payFundsTitledGroupBg.setVisible(false); + payFundsTitledGroupBg.setManaged(false); + totalToPayTextField.setVisible(false); + totalToPayTextField.setManaged(false); + addressTextField.setVisible(false); + addressTextField.setManaged(false); + qrCodeImageView.setVisible(false); + qrCodeImageView.setManaged(false); + balanceTextField.setVisible(false); + balanceTextField.setManaged(false); + cancelButton2.setVisible(false); + cancelButton2.setManaged(false); + reserveExactAmountSlider.setVisible(false); + reserveExactAmountSlider.setManaged(false); + } + + protected void showFundingGroup() { + payFundsTitledGroupBg.setVisible(true); + payFundsTitledGroupBg.setManaged(true); + totalToPayTextField.setVisible(true); + totalToPayTextField.setManaged(true); + addressTextField.setVisible(true); + addressTextField.setManaged(true); + qrCodeImageView.setVisible(true); + qrCodeImageView.setManaged(true); + balanceTextField.setVisible(true); + balanceTextField.setManaged(true); + cancelButton2.setVisible(true); + cancelButton2.setManaged(true); + reserveExactAmountSlider.setVisible(true); + reserveExactAmountSlider.setManaged(true); + } + private VBox getSecurityDepositBox() { Tuple3 tuple = getEditableValueBoxWithInfo( Res.get("createOffer.securityDeposit.prompt")); @@ -1326,6 +1353,8 @@ public abstract class MutableOfferView> exten }); cancelButton2.setDefaultButton(false); cancelButton2.setVisible(false); + + hideFundingGroup(); } private void openWallet() { From 695f2b8dd35bee2f8220c6fce2d39094ce482351 Mon Sep 17 00:00:00 2001 From: woodser Date: Thu, 17 Apr 2025 19:58:12 -0400 Subject: [PATCH 10/74] fix startup error with localhost, support fallback from provided nodes --- .../haveno/core/api/XmrConnectionService.java | 98 +++++++++++-------- .../java/haveno/core/api/XmrLocalNode.java | 11 ++- .../haveno/core/app/HavenoHeadlessApp.java | 2 +- .../java/haveno/core/app/HavenoSetup.java | 14 +-- .../java/haveno/core/xmr/nodes/XmrNodes.java | 13 ++- .../resources/i18n/displayStrings.properties | 3 +- .../haveno/desktop/main/MainViewModel.java | 40 ++++++-- 7 files changed, 115 insertions(+), 66 deletions(-) diff --git a/core/src/main/java/haveno/core/api/XmrConnectionService.java b/core/src/main/java/haveno/core/api/XmrConnectionService.java index dc38547df6..53eba276a0 100644 --- a/core/src/main/java/haveno/core/api/XmrConnectionService.java +++ b/core/src/main/java/haveno/core/api/XmrConnectionService.java @@ -75,9 +75,10 @@ public final class XmrConnectionService { private static final long KEY_IMAGE_REFRESH_PERIOD_MS_LOCAL = 20000; // 20 seconds private static final long KEY_IMAGE_REFRESH_PERIOD_MS_REMOTE = 300000; // 5 minutes - public enum XmrConnectionError { + public enum XmrConnectionFallbackType { LOCAL, - CUSTOM + CUSTOM, + PROVIDED } private final Object lock = new Object(); @@ -97,7 +98,7 @@ public final class XmrConnectionService { private final LongProperty chainHeight = new SimpleLongProperty(0); private final DownloadListener downloadListener = new DownloadListener(); @Getter - private final ObjectProperty connectionServiceError = new SimpleObjectProperty<>(); + private final ObjectProperty connectionServiceFallbackType = new SimpleObjectProperty<>(); @Getter private final StringProperty connectionServiceErrorMsg = new SimpleStringProperty(); private final LongProperty numUpdates = new SimpleLongProperty(0); @@ -129,6 +130,7 @@ public final class XmrConnectionService { private Set excludedConnections = new HashSet<>(); private static final long FALLBACK_INVOCATION_PERIOD_MS = 1000 * 30 * 1; // offer to fallback up to once every 30s private boolean fallbackApplied; + private boolean usedSyncingLocalNodeBeforeStartup; @Inject public XmrConnectionService(P2PService p2PService, @@ -156,7 +158,13 @@ public final class XmrConnectionService { p2PService.addP2PServiceListener(new P2PServiceListener() { @Override public void onTorNodeReady() { - ThreadUtils.submitToPool(() -> initialize()); + ThreadUtils.submitToPool(() -> { + try { + initialize(); + } catch (Exception e) { + log.warn("Error initializing connection service, error={}\n", e.getMessage(), e); + } + }); } @Override public void onHiddenServicePublished() {} @@ -270,7 +278,7 @@ public final class XmrConnectionService { accountService.checkAccountOpen(); // user needs to authorize fallback on startup after using locally synced node - if (lastInfo == null && !fallbackApplied && lastUsedLocalSyncingNode() && !xmrLocalNode.isDetected()) { + if (fallbackRequiredBeforeConnectionSwitch()) { log.warn("Cannot get best connection on startup because we last synced local node and user has not opted to fallback"); return null; } @@ -283,6 +291,10 @@ public final class XmrConnectionService { return bestConnection; } + private boolean fallbackRequiredBeforeConnectionSwitch() { + return lastInfo == null && !fallbackApplied && usedSyncingLocalNodeBeforeStartup && (!xmrLocalNode.isDetected() || xmrLocalNode.shouldBeIgnored()); + } + private void addLocalNodeIfIgnored(Collection ignoredConnections) { if (xmrLocalNode.shouldBeIgnored() && connectionManager.hasConnection(xmrLocalNode.getUri())) ignoredConnections.add(connectionManager.getConnectionByUri(xmrLocalNode.getUri())); } @@ -458,15 +470,20 @@ public final class XmrConnectionService { public void fallbackToBestConnection() { if (isShutDownStarted) return; - if (xmrNodes.getProvidedXmrNodes().isEmpty()) { + fallbackApplied = true; + if (isProvidedConnections() || xmrNodes.getProvidedXmrNodes().isEmpty()) { log.warn("Falling back to public nodes"); preferences.setMoneroNodesOptionOrdinal(XmrNodes.MoneroNodesOption.PUBLIC.ordinal()); + initializeConnections(); } else { log.warn("Falling back to provided nodes"); preferences.setMoneroNodesOptionOrdinal(XmrNodes.MoneroNodesOption.PROVIDED.ordinal()); + initializeConnections(); + if (getConnection() == null) { + log.warn("No provided nodes available, falling back to public nodes"); + fallbackToBestConnection(); + } } - fallbackApplied = true; - initializeConnections(); } // ------------------------------- HELPERS -------------------------------- @@ -578,8 +595,8 @@ public final class XmrConnectionService { setConnection(connection.getUri()); // reset error connecting to local node - if (connectionServiceError.get() == XmrConnectionError.LOCAL && isConnectionLocalHost()) { - connectionServiceError.set(null); + if (connectionServiceFallbackType.get() == XmrConnectionFallbackType.LOCAL && isConnectionLocalHost()) { + connectionServiceFallbackType.set(null); } } else if (getConnection() != null && getConnection().getUri().equals(connection.getUri())) { MoneroRpcConnection bestConnection = getBestConnection(); @@ -602,8 +619,10 @@ public final class XmrConnectionService { // add default connections for (XmrNode node : xmrNodes.getAllXmrNodes()) { if (node.hasClearNetAddress()) { - MoneroRpcConnection connection = new MoneroRpcConnection(node.getAddress() + ":" + node.getPort()).setPriority(node.getPriority()); - if (!connectionList.hasConnection(connection.getUri())) addConnection(connection); + if (!xmrLocalNode.shouldBeIgnored() || !xmrLocalNode.equalsUri(node.getClearNetUri())) { + MoneroRpcConnection connection = new MoneroRpcConnection(node.getHostNameOrAddress() + ":" + node.getPort()).setPriority(node.getPriority()); + if (!connectionList.hasConnection(connection.getUri())) addConnection(connection); + } } if (node.hasOnionAddress()) { MoneroRpcConnection connection = new MoneroRpcConnection(node.getOnionAddress() + ":" + node.getPort()).setPriority(node.getPriority()); @@ -615,8 +634,10 @@ public final class XmrConnectionService { // add default connections for (XmrNode node : xmrNodes.selectPreferredNodes(new XmrNodesSetupPreferences(preferences))) { if (node.hasClearNetAddress()) { - MoneroRpcConnection connection = new MoneroRpcConnection(node.getAddress() + ":" + node.getPort()).setPriority(node.getPriority()); - addConnection(connection); + if (!xmrLocalNode.shouldBeIgnored() || !xmrLocalNode.equalsUri(node.getClearNetUri())) { + MoneroRpcConnection connection = new MoneroRpcConnection(node.getHostNameOrAddress() + ":" + node.getPort()).setPriority(node.getPriority()); + addConnection(connection); + } } if (node.hasOnionAddress()) { MoneroRpcConnection connection = new MoneroRpcConnection(node.getOnionAddress() + ":" + node.getPort()).setPriority(node.getPriority()); @@ -632,6 +653,11 @@ public final class XmrConnectionService { } } + // set if last node was locally syncing + if (!isInitialized) { + usedSyncingLocalNodeBeforeStartup = connectionList.getCurrentConnectionUri().isPresent() && xmrLocalNode.equalsUri(connectionList.getCurrentConnectionUri().get()) && preferences.getXmrNodeSettings().getSyncBlockchain(); + } + // set connection proxies log.info("TOR proxy URI: " + getProxyUri()); for (MoneroRpcConnection connection : connectionManager.getConnections()) { @@ -666,29 +692,16 @@ public final class XmrConnectionService { onConnectionChanged(connectionManager.getConnection()); } - private boolean lastUsedLocalSyncingNode() { - return connectionManager.getConnection() != null && xmrLocalNode.equalsUri(connectionManager.getConnection().getUri()) && !xmrLocalNode.isDetected() && !xmrLocalNode.shouldBeIgnored(); - } - - public void startLocalNode() { + public void startLocalNode() throws Exception { // cannot start local node as seed node if (HavenoUtils.isSeedNode()) { throw new RuntimeException("Cannot start local node on seed node"); } - // start local node if offline and used as last connection - if (connectionManager.getConnection() != null && xmrLocalNode.equalsUri(connectionManager.getConnection().getUri()) && !xmrLocalNode.isDetected() && !xmrLocalNode.shouldBeIgnored()) { - try { - log.info("Starting local node"); - xmrLocalNode.start(); - } catch (Exception e) { - log.error("Unable to start local monero node, error={}\n", e.getMessage(), e); - throw new RuntimeException(e); - } - } else { - throw new RuntimeException("Local node is not offline and used as last connection"); - } + // start local node + log.info("Starting local node"); + xmrLocalNode.start(); } private void onConnectionChanged(MoneroRpcConnection currentConnection) { @@ -768,7 +781,7 @@ public final class XmrConnectionService { try { // poll daemon - if (daemon == null) switchToBestConnection(); + if (daemon == null && !fallbackRequiredBeforeConnectionSwitch()) switchToBestConnection(); try { if (daemon == null) throw new RuntimeException("No connection to Monero daemon"); lastInfo = daemon.getInfo(); @@ -778,16 +791,19 @@ public final class XmrConnectionService { if (isShutDownStarted) return; // invoke fallback handling on startup error - boolean canFallback = isFixedConnection() || isCustomConnections() || lastUsedLocalSyncingNode(); + boolean canFallback = isFixedConnection() || isProvidedConnections() || isCustomConnections() || usedSyncingLocalNodeBeforeStartup; if (lastInfo == null && canFallback) { - if (connectionServiceError.get() == null && (lastFallbackInvocation == null || System.currentTimeMillis() - lastFallbackInvocation > FALLBACK_INVOCATION_PERIOD_MS)) { + if (connectionServiceFallbackType.get() == null && (lastFallbackInvocation == null || System.currentTimeMillis() - lastFallbackInvocation > FALLBACK_INVOCATION_PERIOD_MS)) { lastFallbackInvocation = System.currentTimeMillis(); - if (lastUsedLocalSyncingNode()) { + if (usedSyncingLocalNodeBeforeStartup) { log.warn("Failed to fetch daemon info from local connection on startup: " + e.getMessage()); - connectionServiceError.set(XmrConnectionError.LOCAL); + connectionServiceFallbackType.set(XmrConnectionFallbackType.LOCAL); + } else if (isProvidedConnections()) { + log.warn("Failed to fetch daemon info from provided connections on startup: " + e.getMessage()); + connectionServiceFallbackType.set(XmrConnectionFallbackType.PROVIDED); } else { log.warn("Failed to fetch daemon info from custom connection on startup: " + e.getMessage()); - connectionServiceError.set(XmrConnectionError.CUSTOM); + connectionServiceFallbackType.set(XmrConnectionFallbackType.CUSTOM); } } return; @@ -808,7 +824,7 @@ public final class XmrConnectionService { // connected to daemon isConnected = true; - connectionServiceError.set(null); + connectionServiceFallbackType.set(null); // determine if blockchain is syncing locally boolean blockchainSyncing = lastInfo.getHeight().equals(lastInfo.getHeightWithoutBootstrap()) || (lastInfo.getTargetHeight().equals(0l) && lastInfo.getHeightWithoutBootstrap().equals(0l)); // blockchain is syncing if height equals height without bootstrap, or target height and height without bootstrap both equal 0 @@ -885,10 +901,14 @@ public final class XmrConnectionService { } private boolean isFixedConnection() { - return !"".equals(config.xmrNode) && (!HavenoUtils.isLocalHost(config.xmrNode) || !xmrLocalNode.shouldBeIgnored()) && !fallbackApplied; + return !"".equals(config.xmrNode) && !(HavenoUtils.isLocalHost(config.xmrNode) && xmrLocalNode.shouldBeIgnored()) && !fallbackApplied; } private boolean isCustomConnections() { return preferences.getMoneroNodesOption() == XmrNodes.MoneroNodesOption.CUSTOM; } + + private boolean isProvidedConnections() { + return preferences.getMoneroNodesOption() == XmrNodes.MoneroNodesOption.PROVIDED; + } } diff --git a/core/src/main/java/haveno/core/api/XmrLocalNode.java b/core/src/main/java/haveno/core/api/XmrLocalNode.java index 7295202c64..0928340d25 100644 --- a/core/src/main/java/haveno/core/api/XmrLocalNode.java +++ b/core/src/main/java/haveno/core/api/XmrLocalNode.java @@ -109,17 +109,18 @@ public class XmrLocalNode { public boolean shouldBeIgnored() { if (config.ignoreLocalXmrNode) return true; - // determine if local node is configured + // ignore if fixed connection is not local + if (!"".equals(config.xmrNode)) return !HavenoUtils.isLocalHost(config.xmrNode); + + // check if local node is within configuration boolean hasConfiguredLocalNode = false; for (XmrNode node : xmrNodes.selectPreferredNodes(new XmrNodesSetupPreferences(preferences))) { - if (node.getAddress() != null && equalsUri("http://" + node.getAddress() + ":" + node.getPort())) { + if (node.hasClearNetAddress() && equalsUri(node.getClearNetUri())) { hasConfiguredLocalNode = true; break; } } - if (!hasConfiguredLocalNode) return true; - - return false; + return !hasConfiguredLocalNode; } public void addListener(XmrLocalNodeListener listener) { diff --git a/core/src/main/java/haveno/core/app/HavenoHeadlessApp.java b/core/src/main/java/haveno/core/app/HavenoHeadlessApp.java index 84bdcc746a..0bdac1abc1 100644 --- a/core/src/main/java/haveno/core/app/HavenoHeadlessApp.java +++ b/core/src/main/java/haveno/core/app/HavenoHeadlessApp.java @@ -75,7 +75,7 @@ public class HavenoHeadlessApp implements HeadlessApp { log.info("onDisplayTacHandler: We accept the tacs automatically in headless mode"); acceptedHandler.run(); }); - havenoSetup.setDisplayMoneroConnectionErrorHandler(show -> log.warn("onDisplayMoneroConnectionErrorHandler: show={}", show)); + havenoSetup.setDisplayMoneroConnectionFallbackHandler(show -> log.warn("onDisplayMoneroConnectionFallbackHandler: show={}", show)); havenoSetup.setDisplayTorNetworkSettingsHandler(show -> log.info("onDisplayTorNetworkSettingsHandler: show={}", show)); havenoSetup.setChainFileLockedExceptionHandler(msg -> log.error("onChainFileLockedExceptionHandler: msg={}", msg)); tradeManager.setLockedUpFundsHandler(msg -> log.info("onLockedUpFundsHandler: msg={}", msg)); diff --git a/core/src/main/java/haveno/core/app/HavenoSetup.java b/core/src/main/java/haveno/core/app/HavenoSetup.java index 19503fafd8..192e3870b7 100644 --- a/core/src/main/java/haveno/core/app/HavenoSetup.java +++ b/core/src/main/java/haveno/core/app/HavenoSetup.java @@ -55,7 +55,7 @@ import haveno.core.alert.PrivateNotificationManager; import haveno.core.alert.PrivateNotificationPayload; import haveno.core.api.CoreContext; import haveno.core.api.XmrConnectionService; -import haveno.core.api.XmrConnectionService.XmrConnectionError; +import haveno.core.api.XmrConnectionService.XmrConnectionFallbackType; import haveno.core.api.XmrLocalNode; import haveno.core.locale.Res; import haveno.core.offer.OpenOfferManager; @@ -159,7 +159,7 @@ public class HavenoSetup { rejectedTxErrorMessageHandler; @Setter @Nullable - private Consumer displayMoneroConnectionErrorHandler; + private Consumer displayMoneroConnectionFallbackHandler; @Setter @Nullable private Consumer displayTorNetworkSettingsHandler; @@ -431,9 +431,9 @@ public class HavenoSetup { getXmrWalletSyncProgress().addListener((observable, oldValue, newValue) -> resetStartupTimeout()); // listen for fallback handling - getConnectionServiceError().addListener((observable, oldValue, newValue) -> { - if (displayMoneroConnectionErrorHandler == null) return; - displayMoneroConnectionErrorHandler.accept(newValue); + getConnectionServiceFallbackType().addListener((observable, oldValue, newValue) -> { + if (displayMoneroConnectionFallbackHandler == null) return; + displayMoneroConnectionFallbackHandler.accept(newValue); }); log.info("Init P2P network"); @@ -735,8 +735,8 @@ public class HavenoSetup { return xmrConnectionService.getConnectionServiceErrorMsg(); } - public ObjectProperty getConnectionServiceError() { - return xmrConnectionService.getConnectionServiceError(); + public ObjectProperty getConnectionServiceFallbackType() { + return xmrConnectionService.getConnectionServiceFallbackType(); } public StringProperty getTopErrorMsg() { diff --git a/core/src/main/java/haveno/core/xmr/nodes/XmrNodes.java b/core/src/main/java/haveno/core/xmr/nodes/XmrNodes.java index a8fa1ade26..c38ae9b411 100644 --- a/core/src/main/java/haveno/core/xmr/nodes/XmrNodes.java +++ b/core/src/main/java/haveno/core/xmr/nodes/XmrNodes.java @@ -184,10 +184,6 @@ public class XmrNodes { this.operator = operator; } - public boolean hasOnionAddress() { - return onionAddress != null; - } - public String getHostNameOrAddress() { if (hostName != null) return hostName; @@ -195,10 +191,19 @@ public class XmrNodes { return address; } + public boolean hasOnionAddress() { + return onionAddress != null; + } + public boolean hasClearNetAddress() { return hostName != null || address != null; } + public String getClearNetUri() { + if (!hasClearNetAddress()) throw new IllegalStateException("XmrNode does not have clearnet address"); + return "http://" + getHostNameOrAddress() + ":" + port; + } + @Override public String toString() { return "onionAddress='" + onionAddress + '\'' + diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index 4a978381ed..7c8ab0d6f6 100644 --- a/core/src/main/resources/i18n/displayStrings.properties +++ b/core/src/main/resources/i18n/displayStrings.properties @@ -2090,7 +2090,8 @@ closedTradesSummaryWindow.totalTradeFeeInXmr.value={0} ({1} of total trade amoun walletPasswordWindow.headline=Enter password to unlock xmrConnectionError.headline=Monero connection error -xmrConnectionError.customNode=Error connecting to your custom Monero node(s).\n\nDo you want to use the next best available Monero node? +xmrConnectionError.providedNodes=Error connecting to provided Monero node(s).\n\nDo you want to use the next best available Monero node? +xmrConnectionError.customNodes=Error connecting to your custom Monero node(s).\n\nDo you want to use the next best available Monero node? xmrConnectionError.localNode=We previously synced using a local Monero node, but it appears to be unreachable.\n\nPlease check that it's running and synced. xmrConnectionError.localNode.start=Start local node xmrConnectionError.localNode.start.error=Error starting local node diff --git a/desktop/src/main/java/haveno/desktop/main/MainViewModel.java b/desktop/src/main/java/haveno/desktop/main/MainViewModel.java index 16cef449d6..03c38e99ab 100644 --- a/desktop/src/main/java/haveno/desktop/main/MainViewModel.java +++ b/desktop/src/main/java/haveno/desktop/main/MainViewModel.java @@ -337,7 +337,7 @@ public class MainViewModel implements ViewModel, HavenoSetup.HavenoSetupListener tacWindow.onAction(acceptedHandler::run).show(); }, 1)); - havenoSetup.setDisplayMoneroConnectionErrorHandler(connectionError -> { + havenoSetup.setDisplayMoneroConnectionFallbackHandler(connectionError -> { if (connectionError == null) { if (moneroConnectionErrorPopup != null) moneroConnectionErrorPopup.hide(); } else { @@ -349,7 +349,6 @@ public class MainViewModel implements ViewModel, HavenoSetup.HavenoSetupListener .actionButtonText(Res.get("xmrConnectionError.localNode.start")) .onAction(() -> { log.warn("User has chosen to start local node."); - havenoSetup.getConnectionServiceError().set(null); new Thread(() -> { try { HavenoUtils.xmrConnectionService.startLocalNode(); @@ -359,16 +358,20 @@ public class MainViewModel implements ViewModel, HavenoSetup.HavenoSetupListener .headLine(Res.get("xmrConnectionError.localNode.start.error")) .warning(e.getMessage()) .closeButtonText(Res.get("shared.close")) - .onClose(() -> havenoSetup.getConnectionServiceError().set(null)) + .onClose(() -> havenoSetup.getConnectionServiceFallbackType().set(null)) .show(); + } finally { + havenoSetup.getConnectionServiceFallbackType().set(null); } }).start(); }) .secondaryActionButtonText(Res.get("xmrConnectionError.localNode.fallback")) .onSecondaryAction(() -> { log.warn("User has chosen to fallback to the next best available Monero node."); - havenoSetup.getConnectionServiceError().set(null); - new Thread(() -> HavenoUtils.xmrConnectionService.fallbackToBestConnection()).start(); + new Thread(() -> { + HavenoUtils.xmrConnectionService.fallbackToBestConnection(); + havenoSetup.getConnectionServiceFallbackType().set(null); + }).start(); }) .closeButtonText(Res.get("shared.shutDown")) .onClose(HavenoApp.getShutDownHandler()); @@ -376,16 +379,35 @@ public class MainViewModel implements ViewModel, HavenoSetup.HavenoSetupListener case CUSTOM: moneroConnectionErrorPopup = new Popup() .headLine(Res.get("xmrConnectionError.headline")) - .warning(Res.get("xmrConnectionError.customNode")) + .warning(Res.get("xmrConnectionError.customNodes")) .actionButtonText(Res.get("shared.yes")) .onAction(() -> { - havenoSetup.getConnectionServiceError().set(null); - new Thread(() -> HavenoUtils.xmrConnectionService.fallbackToBestConnection()).start(); + new Thread(() -> { + HavenoUtils.xmrConnectionService.fallbackToBestConnection(); + havenoSetup.getConnectionServiceFallbackType().set(null); + }).start(); }) .closeButtonText(Res.get("shared.no")) .onClose(() -> { log.warn("User has declined to fallback to the next best available Monero node."); - havenoSetup.getConnectionServiceError().set(null); + havenoSetup.getConnectionServiceFallbackType().set(null); + }); + break; + case PROVIDED: + moneroConnectionErrorPopup = new Popup() + .headLine(Res.get("xmrConnectionError.headline")) + .warning(Res.get("xmrConnectionError.providedNodes")) + .actionButtonText(Res.get("shared.yes")) + .onAction(() -> { + new Thread(() -> { + HavenoUtils.xmrConnectionService.fallbackToBestConnection(); + havenoSetup.getConnectionServiceFallbackType().set(null); + }).start(); + }) + .closeButtonText(Res.get("shared.no")) + .onClose(() -> { + log.warn("User has declined to fallback to the next best available Monero node."); + havenoSetup.getConnectionServiceFallbackType().set(null); }); break; } From 39909e7936d1e2537d201b8824d6750caea8abb8 Mon Sep 17 00:00:00 2001 From: woodser Date: Thu, 17 Apr 2025 20:55:01 -0400 Subject: [PATCH 11/74] bump version to 1.1.0 --- build.gradle | 2 +- .../src/main/java/haveno/common/app/Version.java | 2 +- .../main/resources/i18n/displayStrings.properties | 1 + .../linux/exchange.haveno.Haveno.metainfo.xml | 2 +- desktop/package/macosx/Info.plist | 4 ++-- .../settings/network/NetworkSettingsView.fxml | 7 +++++-- .../settings/network/NetworkSettingsView.java | 15 +++++++++++---- .../main/java/haveno/seednode/SeedNodeMain.java | 2 +- 8 files changed, 23 insertions(+), 12 deletions(-) diff --git a/build.gradle b/build.gradle index 20ca924031..aec0e7c89f 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.19-SNAPSHOT' + version = '1.1.0-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 3dc04889b8..c0dd9352ff 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.19"; + public static final String VERSION = "1.1.0"; /** * Holds a list of the tagged resource files for optimizing the getData requests. diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index 7c8ab0d6f6..9d500aefe5 100644 --- a/core/src/main/resources/i18n/displayStrings.properties +++ b/core/src/main/resources/i18n/displayStrings.properties @@ -237,6 +237,7 @@ shared.pending=Pending shared.me=Me shared.maker=Maker shared.taker=Taker +shared.none=None #################################################################### diff --git a/desktop/package/linux/exchange.haveno.Haveno.metainfo.xml b/desktop/package/linux/exchange.haveno.Haveno.metainfo.xml index fc5f50c2b5..9227c587a6 100644 --- a/desktop/package/linux/exchange.haveno.Haveno.metainfo.xml +++ b/desktop/package/linux/exchange.haveno.Haveno.metainfo.xml @@ -60,6 +60,6 @@ - + diff --git a/desktop/package/macosx/Info.plist b/desktop/package/macosx/Info.plist index a24f430c12..fd0a765320 100644 --- a/desktop/package/macosx/Info.plist +++ b/desktop/package/macosx/Info.plist @@ -5,10 +5,10 @@ CFBundleVersion - 1.0.19 + 1.1.0 CFBundleShortVersionString - 1.0.19 + 1.1.0 CFBundleExecutable Haveno diff --git a/desktop/src/main/java/haveno/desktop/main/settings/network/NetworkSettingsView.fxml b/desktop/src/main/java/haveno/desktop/main/settings/network/NetworkSettingsView.fxml index 1f3e8840d7..bbfb4c6e05 100644 --- a/desktop/src/main/java/haveno/desktop/main/settings/network/NetworkSettingsView.fxml +++ b/desktop/src/main/java/haveno/desktop/main/settings/network/NetworkSettingsView.fxml @@ -91,7 +91,7 @@ - + @@ -159,7 +159,10 @@ - + + + diff --git a/desktop/src/main/java/haveno/desktop/main/settings/network/NetworkSettingsView.java b/desktop/src/main/java/haveno/desktop/main/settings/network/NetworkSettingsView.java index 0773217cd1..a767d032bc 100644 --- a/desktop/src/main/java/haveno/desktop/main/settings/network/NetworkSettingsView.java +++ b/desktop/src/main/java/haveno/desktop/main/settings/network/NetworkSettingsView.java @@ -75,7 +75,7 @@ public class NetworkSettingsView extends ActivatableView { @FXML InputTextField xmrNodesInputTextField; @FXML - TextField onionAddress, sentDataTextField, receivedDataTextField, chainHeightTextField; + TextField onionAddress, sentDataTextField, receivedDataTextField, chainHeightTextField, minVersionForTrading; @FXML Label p2PPeersLabel, moneroConnectionsLabel; @FXML @@ -176,6 +176,7 @@ public class NetworkSettingsView extends ActivatableView { sentDataTextField.setPromptText(Res.get("settings.net.sentDataLabel")); receivedDataTextField.setPromptText(Res.get("settings.net.receivedDataLabel")); chainHeightTextField.setPromptText(Res.get("settings.net.chainHeightLabel")); + minVersionForTrading.setPromptText(Res.get("filterWindow.disableTradeBelowVersion")); roundTripTimeColumn.setGraphic(new AutoTooltipLabel(Res.get("settings.net.roundTripTimeColumn"))); sentBytesColumn.setGraphic(new AutoTooltipLabel(Res.get("settings.net.sentBytesColumn"))); receivedBytesColumn.setGraphic(new AutoTooltipLabel(Res.get("settings.net.receivedBytesColumn"))); @@ -275,7 +276,7 @@ public class NetworkSettingsView extends ActivatableView { showShutDownPopup(); } }; - filterPropertyListener = (observable, oldValue, newValue) -> applyPreventPublicXmrNetwork(); + filterPropertyListener = (observable, oldValue, newValue) -> applyFilter(); // disable radio buttons if no nodes available if (xmrNodes.getProvidedXmrNodes().isEmpty()) { @@ -298,7 +299,7 @@ public class NetworkSettingsView extends ActivatableView { moneroPeersToggleGroup.selectedToggleProperty().addListener(moneroPeersToggleGroupListener); if (filterManager.getFilter() != null) - applyPreventPublicXmrNetwork(); + applyFilter(); filterManager.filterProperty().addListener(filterPropertyListener); @@ -492,7 +493,9 @@ public class NetworkSettingsView extends ActivatableView { } - private void applyPreventPublicXmrNetwork() { + private void applyFilter() { + + // prevent public xmr network final boolean preventPublicXmrNetwork = isPreventPublicXmrNetwork(); usePublicNodesRadio.setDisable(isPublicNodesDisabled()); if (preventPublicXmrNetwork && selectedMoneroNodesOption == XmrNodes.MoneroNodesOption.PUBLIC) { @@ -501,6 +504,10 @@ public class NetworkSettingsView extends ActivatableView { selectMoneroPeersToggle(); onMoneroPeersToggleSelected(false); } + + // set min version for trading + String minVersion = filterManager.getDisableTradeBelowVersion(); + minVersionForTrading.textProperty().setValue(minVersion == null ? Res.get("shared.none") : minVersion); } private boolean isPublicNodesDisabled() { diff --git a/seednode/src/main/java/haveno/seednode/SeedNodeMain.java b/seednode/src/main/java/haveno/seednode/SeedNodeMain.java index 35d4bbbe17..1e9c4c5061 100644 --- a/seednode/src/main/java/haveno/seednode/SeedNodeMain.java +++ b/seednode/src/main/java/haveno/seednode/SeedNodeMain.java @@ -41,7 +41,7 @@ import lombok.extern.slf4j.Slf4j; @Slf4j public class SeedNodeMain extends ExecutableForAppWithP2p { private static final long CHECK_CONNECTION_LOSS_SEC = 30; - private static final String VERSION = "1.0.19"; + private static final String VERSION = "1.1.0"; private SeedNode seedNode; private Timer checkConnectionLossTime; From bfef0f9492a3764f819a4b6d096b5da68e7bf220 Mon Sep 17 00:00:00 2001 From: woodser <13068859+woodser@users.noreply.github.com> Date: Fri, 18 Apr 2025 16:44:33 -0400 Subject: [PATCH 12/74] fix hanging while posting or canceling offer --- .../main/java/haveno/core/xmr/Balances.java | 17 +++---- .../java/haveno/desktop/main/MainView.java | 2 +- .../main/offer/MutableOfferViewModel.java | 45 +++++++++++-------- 3 files changed, 32 insertions(+), 32 deletions(-) diff --git a/core/src/main/java/haveno/core/xmr/Balances.java b/core/src/main/java/haveno/core/xmr/Balances.java index fe49b941fd..15f6ff8a74 100644 --- a/core/src/main/java/haveno/core/xmr/Balances.java +++ b/core/src/main/java/haveno/core/xmr/Balances.java @@ -37,7 +37,6 @@ package haveno.core.xmr; import com.google.inject.Inject; import haveno.common.ThreadUtils; -import haveno.common.UserThread; import haveno.core.api.model.XmrBalanceInfo; import haveno.core.offer.OpenOffer; import haveno.core.offer.OpenOfferManager; @@ -163,18 +162,12 @@ public class Balances { // calculate reserved balance reservedBalance = reservedOfferBalance.add(reservedTradeBalance); + // play sound if funds received + boolean fundsReceived = balanceSumBefore != null && getNonTradeBalanceSum().compareTo(balanceSumBefore) > 0; + if (fundsReceived) HavenoUtils.playCashRegisterSound(); + // notify balance update - UserThread.execute(() -> { - - // check if funds received - boolean fundsReceived = balanceSumBefore != null && getNonTradeBalanceSum().compareTo(balanceSumBefore) > 0; - if (fundsReceived) { - HavenoUtils.playCashRegisterSound(); - } - - // increase counter to notify listeners - updateCounter.set(updateCounter.get() + 1); - }); + updateCounter.set(updateCounter.get() + 1); } } } diff --git a/desktop/src/main/java/haveno/desktop/main/MainView.java b/desktop/src/main/java/haveno/desktop/main/MainView.java index f294eea7bf..eaec5d1154 100644 --- a/desktop/src/main/java/haveno/desktop/main/MainView.java +++ b/desktop/src/main/java/haveno/desktop/main/MainView.java @@ -353,7 +353,7 @@ public class MainView extends InitializableView { settingsButtonWithBadge.getStyleClass().add("new"); navigation.addListener((viewPath, data) -> { - UserThread.await(() -> { + UserThread.await(() -> { // TODO: this uses `await` to fix nagivation link from market view to offer book, but await can cause hanging, so execute should be used if (viewPath.size() != 2 || viewPath.indexOf(MainView.class) != 0) return; Class viewClass = viewPath.tip(); diff --git a/desktop/src/main/java/haveno/desktop/main/offer/MutableOfferViewModel.java b/desktop/src/main/java/haveno/desktop/main/offer/MutableOfferViewModel.java index fb971b6296..48adb22f9f 100644 --- a/desktop/src/main/java/haveno/desktop/main/offer/MutableOfferViewModel.java +++ b/desktop/src/main/java/haveno/desktop/main/offer/MutableOfferViewModel.java @@ -19,6 +19,8 @@ package haveno.desktop.main.offer; import com.google.inject.Inject; import com.google.inject.name.Named; + +import haveno.common.ThreadUtils; import haveno.common.UserThread; import haveno.common.app.DevEnv; import haveno.common.handlers.ErrorMessageHandler; @@ -108,7 +110,7 @@ public abstract class MutableOfferViewModel ext private String amountDescription; private String addressAsString; private final String paymentLabel; - private boolean createOfferRequested; + private boolean createOfferInProgress; public boolean createOfferCanceled; public final StringProperty amount = new SimpleStringProperty(); @@ -638,32 +640,37 @@ public abstract class MutableOfferViewModel ext /////////////////////////////////////////////////////////////////////////////////////////// void onPlaceOffer(Offer offer, Runnable resultHandler) { - errorMessage.set(null); - createOfferRequested = true; - createOfferCanceled = false; - - dataModel.onPlaceOffer(offer, transaction -> { - resultHandler.run(); - if (!createOfferCanceled) placeOfferCompleted.set(true); + ThreadUtils.execute(() -> { errorMessage.set(null); - }, errMessage -> { - createOfferRequested = false; - if (offer.getState() == Offer.State.OFFER_FEE_RESERVED) errorMessage.set(errMessage + Res.get("createOffer.errorInfo")); - else errorMessage.set(errMessage); + createOfferInProgress = true; + createOfferCanceled = false; + + dataModel.onPlaceOffer(offer, transaction -> { + createOfferInProgress = false; + resultHandler.run(); + if (!createOfferCanceled) placeOfferCompleted.set(true); + errorMessage.set(null); + }, errMessage -> { + createOfferInProgress = false; + if (offer.getState() == Offer.State.OFFER_FEE_RESERVED) errorMessage.set(errMessage + Res.get("createOffer.errorInfo")); + else errorMessage.set(errorMessage.get()); + + UserThread.execute(() -> { + updateButtonDisableState(); + updateSpinnerInfo(); + resultHandler.run(); + }); + }); UserThread.execute(() -> { updateButtonDisableState(); updateSpinnerInfo(); - resultHandler.run(); }); - }); - - updateButtonDisableState(); - updateSpinnerInfo(); + }, getClass().getSimpleName()); } public void onCancelOffer(ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) { - createOfferRequested = false; + log.info("Canceling posting offer {}", offer.getId()); createOfferCanceled = true; OpenOfferManager openOfferManager = HavenoUtils.openOfferManager; Optional openOffer = openOfferManager.getOpenOffer(offer.getId()); @@ -1355,7 +1362,7 @@ public abstract class MutableOfferViewModel ext inputDataValid = inputDataValid && getExtraInfoValidationResult().isValid; isNextButtonDisabled.set(!inputDataValid); - isPlaceOfferButtonDisabled.set(createOfferRequested || !inputDataValid || !dataModel.getIsXmrWalletFunded().get()); + isPlaceOfferButtonDisabled.set(createOfferInProgress || !inputDataValid || !dataModel.getIsXmrWalletFunded().get()); } private ValidationResult getExtraInfoValidationResult() { From 13e13d945d21d22630f86c7785b8dd48d773f62e Mon Sep 17 00:00:00 2001 From: woodser <13068859+woodser@users.noreply.github.com> Date: Fri, 18 Apr 2025 17:29:33 -0400 Subject: [PATCH 13/74] print pub key hex when missing for signature data --- .../java/haveno/core/account/sign/SignedWitnessService.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/haveno/core/account/sign/SignedWitnessService.java b/core/src/main/java/haveno/core/account/sign/SignedWitnessService.java index b4ba7b58a8..f86a0c2bb2 100644 --- a/core/src/main/java/haveno/core/account/sign/SignedWitnessService.java +++ b/core/src/main/java/haveno/core/account/sign/SignedWitnessService.java @@ -335,12 +335,13 @@ public class SignedWitnessService { String message = Utilities.encodeToHex(signedWitness.getAccountAgeWitnessHash()); String signatureBase64 = new String(signedWitness.getSignature(), Charsets.UTF_8); ECKey key = ECKey.fromPublicOnly(signedWitness.getSignerPubKey()); - if (arbitratorManager.isPublicKeyInList(Utilities.encodeToHex(key.getPubKey()))) { + String pubKeyHex = Utilities.encodeToHex(key.getPubKey()); + if (arbitratorManager.isPublicKeyInList(pubKeyHex)) { key.verifyMessage(message, signatureBase64); verifySignatureWithECKeyResultCache.put(hash, true); return true; } else { - log.warn("Provided EC key is not in list of valid arbitrators."); + log.warn("Provided EC key is not in list of valid arbitrators: " + pubKeyHex); verifySignatureWithECKeyResultCache.put(hash, false); return false; } From c7a3a9740ff7386ba2a6dfdefe1c2a02372d79e5 Mon Sep 17 00:00:00 2001 From: woodser Date: Sat, 19 Apr 2025 16:54:01 -0400 Subject: [PATCH 14/74] fixes when cloned offers are taken at the same time --- .../main/java/haveno/core/trade/Trade.java | 9 ++++- .../core/trade/protocol/TradeProtocol.java | 16 +++++++-- .../ArbitratorProcessDepositRequest.java | 33 +++++++++++-------- .../tasks/ProcessDepositResponse.java | 9 ++--- .../haveno/core/xmr/wallet/XmrWalletBase.java | 1 - .../main/funds/deposit/DepositView.java | 3 +- 6 files changed, 47 insertions(+), 24 deletions(-) diff --git a/core/src/main/java/haveno/core/trade/Trade.java b/core/src/main/java/haveno/core/trade/Trade.java index 9d4112ff3f..2dfa5b0f1f 100644 --- a/core/src/main/java/haveno/core/trade/Trade.java +++ b/core/src/main/java/haveno/core/trade/Trade.java @@ -2648,7 +2648,7 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model { } } setDepositTxs(txs); - if (getMaker().getDepositTx() == null || (getTaker().getDepositTx() == null && !hasBuyerAsTakerWithoutDeposit())) return; // skip if either deposit tx not seen + if (!isPublished(getMaker().getDepositTx()) || (!hasBuyerAsTakerWithoutDeposit() && !isPublished(getTaker().getDepositTx()))) return; // skip if deposit txs not published successfully setStateDepositsSeen(); // set actual security deposits @@ -2750,6 +2750,13 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model { } } + private static boolean isPublished(MoneroTx tx) { + if (tx == null) return false; + if (Boolean.TRUE.equals(tx.isFailed())) return false; + if (!Boolean.TRUE.equals(tx.inTxPool()) && !Boolean.TRUE.equals(tx.isConfirmed())) return false; + return true; + } + private void syncWalletIfBehind() { synchronized (walletLock) { if (isWalletBehind()) { diff --git a/core/src/main/java/haveno/core/trade/protocol/TradeProtocol.java b/core/src/main/java/haveno/core/trade/protocol/TradeProtocol.java index a1ad25ddaf..d87d5f3d5d 100644 --- a/core/src/main/java/haveno/core/trade/protocol/TradeProtocol.java +++ b/core/src/main/java/haveno/core/trade/protocol/TradeProtocol.java @@ -460,9 +460,19 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D .using(new TradeTaskRunner(trade, () -> { stopTimeout(); - this.errorMessageHandler = null; // TODO: set this when trade state is >= DEPOSIT_PUBLISHED - handleTaskRunnerSuccess(sender, response); - if (tradeResultHandler != null) tradeResultHandler.handleResult(trade); // trade is initialized + + // tasks may complete successfully but process an error + if (trade.getInitError() == null) { + this.errorMessageHandler = null; // TODO: set this when trade state is >= DEPOSIT_PUBLISHED + handleTaskRunnerSuccess(sender, response); + if (tradeResultHandler != null) tradeResultHandler.handleResult(trade); // trade is initialized + } else { + handleTaskRunnerSuccess(sender, response); + if (errorMessageHandler != null) errorMessageHandler.handleErrorMessage(trade.getInitError().getMessage()); + } + + this.tradeResultHandler = null; + this.errorMessageHandler = null; }, errorMessage -> { handleTaskRunnerFault(sender, response, errorMessage); diff --git a/core/src/main/java/haveno/core/trade/protocol/tasks/ArbitratorProcessDepositRequest.java b/core/src/main/java/haveno/core/trade/protocol/tasks/ArbitratorProcessDepositRequest.java index 3a7fc7ace9..c11ed57ee4 100644 --- a/core/src/main/java/haveno/core/trade/protocol/tasks/ArbitratorProcessDepositRequest.java +++ b/core/src/main/java/haveno/core/trade/protocol/tasks/ArbitratorProcessDepositRequest.java @@ -95,6 +95,18 @@ public class ArbitratorProcessDepositRequest extends TradeTask { // set peer's signature sender.setContractSignature(signature); + // subscribe to trade state once to send responses with ack or nack + if (!hasBothContractSignatures()) { + trade.stateProperty().addListener((obs, oldState, newState) -> { + if (oldState == newState) return; + if (newState == Trade.State.PUBLISH_DEPOSIT_TX_REQUEST_FAILED) { + sendDepositResponsesOnce(trade.getProcessModel().error == null ? "Arbitrator failed to publish deposit txs within timeout for trade " + trade.getId() : trade.getProcessModel().error.getMessage()); + } else if (newState.ordinal() >= Trade.State.ARBITRATOR_PUBLISHED_DEPOSIT_TXS.ordinal()) { + sendDepositResponsesOnce(null); + } + }); + } + // collect expected values Offer offer = trade.getOffer(); boolean isFromTaker = sender == trade.getTaker(); @@ -138,7 +150,7 @@ public class ArbitratorProcessDepositRequest extends TradeTask { // relay deposit txs when both requests received MoneroDaemon daemon = trade.getXmrWalletService().getDaemon(); - if (processModel.getMaker().getContractSignature() != null && processModel.getTaker().getContractSignature() != null) { + if (hasBothContractSignatures()) { // check timeout and extend just before relaying if (isTimedOut()) throw new RuntimeException("Trade protocol has timed out before relaying deposit txs for {} {}" + trade.getClass().getSimpleName() + " " + trade.getShortId()); @@ -182,22 +194,15 @@ public class ArbitratorProcessDepositRequest extends TradeTask { throw e; } } else { - - // subscribe to trade state once to send responses with ack or nack - trade.stateProperty().addListener((obs, oldState, newState) -> { - if (oldState == newState) return; - if (newState == Trade.State.PUBLISH_DEPOSIT_TX_REQUEST_FAILED) { - sendDepositResponsesOnce(trade.getProcessModel().error == null ? "Arbitrator failed to publish deposit txs within timeout for trade " + trade.getId() : trade.getProcessModel().error.getMessage()); - } else if (newState.ordinal() >= Trade.State.ARBITRATOR_PUBLISHED_DEPOSIT_TXS.ordinal()) { - sendDepositResponsesOnce(null); - } - }); - if (processModel.getMaker().getDepositTxHex() == null) log.info("Arbitrator waiting for deposit request from maker for trade " + trade.getId()); if (processModel.getTaker().getDepositTxHex() == null && !trade.hasBuyerAsTakerWithoutDeposit()) log.info("Arbitrator waiting for deposit request from taker for trade " + trade.getId()); } } + private boolean hasBothContractSignatures() { + return processModel.getMaker().getContractSignature() != null && processModel.getTaker().getContractSignature() != null; + } + private boolean isTimedOut() { return !processModel.getTradeManager().hasOpenTrade(trade); } @@ -210,7 +215,7 @@ public class ArbitratorProcessDepositRequest extends TradeTask { // log error if (errorMessage != null) { - log.warn("Sending deposit responses with error={}", errorMessage, new Throwable("Stack trace")); + log.warn("Sending deposit responses for tradeId={}, error={}", trade.getId(), errorMessage); } // create deposit response @@ -229,7 +234,7 @@ public class ArbitratorProcessDepositRequest extends TradeTask { } private void sendDepositResponse(NodeAddress nodeAddress, PubKeyRing pubKeyRing, DepositResponse response) { - log.info("Sending deposit response to trader={}; offerId={}, error={}", nodeAddress, trade.getId(), trade.getProcessModel().error); + log.info("Sending deposit response to trader={}; offerId={}, error={}", nodeAddress, trade.getId(), response.getErrorMessage()); processModel.getP2PService().sendEncryptedDirectMessage(nodeAddress, pubKeyRing, response, new SendDirectMessageListener() { @Override public void onArrived() { diff --git a/core/src/main/java/haveno/core/trade/protocol/tasks/ProcessDepositResponse.java b/core/src/main/java/haveno/core/trade/protocol/tasks/ProcessDepositResponse.java index dcaf1a7e76..454763e15b 100644 --- a/core/src/main/java/haveno/core/trade/protocol/tasks/ProcessDepositResponse.java +++ b/core/src/main/java/haveno/core/trade/protocol/tasks/ProcessDepositResponse.java @@ -38,13 +38,14 @@ public class ProcessDepositResponse extends TradeTask { try { runInterceptHook(); - // throw if error + // handle error DepositResponse message = (DepositResponse) processModel.getTradeMessage(); if (message.getErrorMessage() != null) { - log.warn("Unregistering trade {} {} because deposit response has error message={}", trade.getClass().getSimpleName(), trade.getShortId(), message.getErrorMessage()); + log.warn("Deposit response for {} {} has error message={}", trade.getClass().getSimpleName(), trade.getShortId(), message.getErrorMessage()); trade.setStateIfValidTransitionTo(Trade.State.PUBLISH_DEPOSIT_TX_REQUEST_FAILED); - processModel.getTradeManager().unregisterTrade(trade); - throw new RuntimeException(message.getErrorMessage()); + trade.setInitError(new RuntimeException(message.getErrorMessage())); + complete(); + return; } // record security deposits diff --git a/core/src/main/java/haveno/core/xmr/wallet/XmrWalletBase.java b/core/src/main/java/haveno/core/xmr/wallet/XmrWalletBase.java index 9646f5e3f4..594f58b6fb 100644 --- a/core/src/main/java/haveno/core/xmr/wallet/XmrWalletBase.java +++ b/core/src/main/java/haveno/core/xmr/wallet/XmrWalletBase.java @@ -105,7 +105,6 @@ public abstract class XmrWalletBase { // start polling wallet for progress syncProgressLatch = new CountDownLatch(1); syncProgressLooper = new TaskLooper(() -> { - if (wallet == null) return; long height; try { height = wallet.getHeight(); // can get read timeout while syncing diff --git a/desktop/src/main/java/haveno/desktop/main/funds/deposit/DepositView.java b/desktop/src/main/java/haveno/desktop/main/funds/deposit/DepositView.java index 884df454e7..e035a04f23 100644 --- a/desktop/src/main/java/haveno/desktop/main/funds/deposit/DepositView.java +++ b/desktop/src/main/java/haveno/desktop/main/funds/deposit/DepositView.java @@ -346,7 +346,8 @@ public class DepositView extends ActivatableView { List addressEntries = xmrWalletService.getAddressEntries(); List items = new ArrayList<>(); for (XmrAddressEntry addressEntry : addressEntries) { - if (addressEntry.isTradePayout()) continue; // do not show trade payout addresses + DepositListItem item = new DepositListItem(addressEntry, xmrWalletService, formatter); + if (addressEntry.isTradePayout() && BigInteger.ZERO.equals(item.getBalanceAsBI())) continue; // do not show empty trade payout addresses items.add(new DepositListItem(addressEntry, xmrWalletService, formatter)); } From cf9a37f29569dfb0b40db8ff9ad1f4d4e7deee0e Mon Sep 17 00:00:00 2001 From: woodser Date: Sat, 19 Apr 2025 22:28:32 -0400 Subject: [PATCH 15/74] improve error handling when clones taken at the same time --- .../main/java/haveno/core/trade/Trade.java | 47 ++++++++++++------- .../java/haveno/core/trade/TradeManager.java | 18 +++++-- .../core/trade/protocol/TradeProtocol.java | 2 +- .../main/funds/deposit/DepositView.java | 3 +- 4 files changed, 46 insertions(+), 24 deletions(-) diff --git a/core/src/main/java/haveno/core/trade/Trade.java b/core/src/main/java/haveno/core/trade/Trade.java index 2dfa5b0f1f..f121470e3a 100644 --- a/core/src/main/java/haveno/core/trade/Trade.java +++ b/core/src/main/java/haveno/core/trade/Trade.java @@ -145,6 +145,7 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model { private static final long DELETE_AFTER_MS = TradeProtocol.TRADE_STEP_TIMEOUT_SECONDS; private static final int NUM_CONFIRMATIONS_FOR_SCHEDULED_IMPORT = 5; protected final Object pollLock = new Object(); + private final Object removeTradeOnErrorLock = new Object(); protected static final Object importMultisigLock = new Object(); private boolean pollInProgress; private boolean restartInProgress; @@ -1608,11 +1609,12 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model { } // shut down trade threads - isInitialized = false; isShutDown = true; List shutDownThreads = new ArrayList<>(); shutDownThreads.add(() -> ThreadUtils.shutDown(getId())); ThreadUtils.awaitTasks(shutDownThreads); + stopProtocolTimeout(); + isInitialized = false; // save and close if (wallet != null) { @@ -1765,24 +1767,30 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model { } private void removeTradeOnError() { - log.warn("removeTradeOnError() trade={}, tradeId={}, state={}", getClass().getSimpleName(), getShortId(), getState()); + synchronized (removeTradeOnErrorLock) { - // force close and re-open wallet in case stuck - forceCloseWallet(); - if (isDepositRequested()) getWallet(); + // skip if already shut down or removed + if (isShutDown || !processModel.getTradeManager().hasTrade(getId())) return; + log.warn("removeTradeOnError() trade={}, tradeId={}, state={}", getClass().getSimpleName(), getShortId(), getState()); - // shut down trade thread - try { - ThreadUtils.shutDown(getId(), 1000l); - } catch (Exception e) { - log.warn("Error shutting down trade thread for {} {}: {}", getClass().getSimpleName(), getId(), e.getMessage()); + // force close and re-open wallet in case stuck + forceCloseWallet(); + if (isDepositRequested()) getWallet(); + + // clear and shut down trade + onShutDownStarted(); + clearAndShutDown(); + + // shut down trade thread + try { + ThreadUtils.shutDown(getId(), 5000l); + } catch (Exception e) { + log.warn("Error shutting down trade thread for {} {}: {}", getClass().getSimpleName(), getId(), e.getMessage()); + } + + // unregister trade + processModel.getTradeManager().unregisterTrade(this); } - - // clear and shut down trade - clearAndShutDown(); - - // unregister trade - processModel.getTradeManager().unregisterTrade(this); } /////////////////////////////////////////////////////////////////////////////////////////// @@ -1824,6 +1832,13 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model { getProtocol().startTimeout(TradeProtocol.TRADE_STEP_TIMEOUT_SECONDS); } + public void stopProtocolTimeout() { + if (!isInitialized) return; + TradeProtocol protocol = getProtocol(); + if (protocol == null) return; + protocol.stopTimeout(); + } + public void setState(State state) { if (isInitialized) { // We don't want to log at startup the setState calls from all persisted trades diff --git a/core/src/main/java/haveno/core/trade/TradeManager.java b/core/src/main/java/haveno/core/trade/TradeManager.java index 14ac565c26..9b092decac 100644 --- a/core/src/main/java/haveno/core/trade/TradeManager.java +++ b/core/src/main/java/haveno/core/trade/TradeManager.java @@ -563,9 +563,14 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi Optional openOfferOptional = openOfferManager.getOpenOffer(request.getOfferId()); if (!openOfferOptional.isPresent()) return; OpenOffer openOffer = openOfferOptional.get(); - if (openOffer.getState() != OpenOffer.State.AVAILABLE) return; Offer offer = openOffer.getOffer(); + // check availability + if (openOffer.getState() != OpenOffer.State.AVAILABLE) { + log.warn("Ignoring InitTradeRequest to maker because offer is not available, offerId={}, sender={}", request.getOfferId(), sender); + return; + } + // validate challenge if (openOffer.getChallenge() != null && !HavenoUtils.getChallengeHash(openOffer.getChallenge()).equals(HavenoUtils.getChallengeHash(request.getChallenge()))) { log.warn("Ignoring InitTradeRequest to maker because challenge is incorrect, tradeId={}, sender={}", request.getOfferId(), sender); @@ -980,9 +985,7 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi closedTradableManager.add(trade); trade.setCompleted(true); removeTrade(trade, true); - - // TODO The address entry should have been removed already. Check and if its the case remove that. - xmrWalletService.swapPayoutAddressEntryToAvailable(trade.getId()); + xmrWalletService.swapPayoutAddressEntryToAvailable(trade.getId()); // TODO The address entry should have been removed already. Check and if its the case remove that. requestPersistence(); } @@ -990,6 +993,7 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi log.warn("Unregistering {} {}", trade.getClass().getSimpleName(), trade.getId()); removeTrade(trade, true); removeFailedTrade(trade); + xmrWalletService.swapPayoutAddressEntryToAvailable(trade.getId()); // TODO The address entry should have been removed already. Check and if its the case remove that. requestPersistence(); } @@ -1274,11 +1278,15 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi return offer.getDirection() == OfferDirection.SELL; } - // TODO (woodser): make Optional versus Trade return types consistent + // TODO: make Optional versus Trade return types consistent public Trade getTrade(String tradeId) { return getOpenTrade(tradeId).orElseGet(() -> getClosedTrade(tradeId).orElseGet(() -> getFailedTrade(tradeId).orElseGet(() -> null))); } + public boolean hasTrade(String tradeId) { + return getTrade(tradeId) != null; + } + public Optional getOpenTrade(String tradeId) { synchronized (tradableList.getList()) { return tradableList.stream().filter(e -> e.getId().equals(tradeId)).findFirst(); diff --git a/core/src/main/java/haveno/core/trade/protocol/TradeProtocol.java b/core/src/main/java/haveno/core/trade/protocol/TradeProtocol.java index d87d5f3d5d..65fcee23c6 100644 --- a/core/src/main/java/haveno/core/trade/protocol/TradeProtocol.java +++ b/core/src/main/java/haveno/core/trade/protocol/TradeProtocol.java @@ -842,7 +842,7 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D } } - protected synchronized void stopTimeout() { + public synchronized void stopTimeout() { synchronized (timeoutTimerLock) { if (timeoutTimer != null) { timeoutTimer.stop(); diff --git a/desktop/src/main/java/haveno/desktop/main/funds/deposit/DepositView.java b/desktop/src/main/java/haveno/desktop/main/funds/deposit/DepositView.java index e035a04f23..884df454e7 100644 --- a/desktop/src/main/java/haveno/desktop/main/funds/deposit/DepositView.java +++ b/desktop/src/main/java/haveno/desktop/main/funds/deposit/DepositView.java @@ -346,8 +346,7 @@ public class DepositView extends ActivatableView { List addressEntries = xmrWalletService.getAddressEntries(); List items = new ArrayList<>(); for (XmrAddressEntry addressEntry : addressEntries) { - DepositListItem item = new DepositListItem(addressEntry, xmrWalletService, formatter); - if (addressEntry.isTradePayout() && BigInteger.ZERO.equals(item.getBalanceAsBI())) continue; // do not show empty trade payout addresses + if (addressEntry.isTradePayout()) continue; // do not show trade payout addresses items.add(new DepositListItem(addressEntry, xmrWalletService, formatter)); } From 38615edf866a410173457282700d8a3213528062 Mon Sep 17 00:00:00 2001 From: woodser Date: Mon, 21 Apr 2025 09:02:17 -0400 Subject: [PATCH 16/74] re-arrange deployment sections and use markup for notes & warnings --- docs/deployment-guide.md | 63 ++++++++++++++++++++-------------------- 1 file changed, 31 insertions(+), 32 deletions(-) diff --git a/docs/deployment-guide.md b/docs/deployment-guide.md index e53de210ed..686c6cd615 100644 --- a/docs/deployment-guide.md +++ b/docs/deployment-guide.md @@ -5,10 +5,11 @@ This guide describes how to deploy a Haveno network: - Manage services on a VPS - Fork and build Haveno - Start a Monero node -- Build and start price nodes - Add seed nodes - Add arbitrators - Configure trade fees and other configuration +- Build and start price nodes +- Set a network filter - Build Haveno installers for distribution - Send alerts to update the application and other maintenance @@ -69,14 +70,6 @@ Optionally customize and deploy monero-stagenet.service and monero-stagenet.conf You can also start the Monero node in your current terminal session by running `make monerod` for mainnet or `make monerod-stagenet` for stagenet. -## Build and start price nodes - -The price node is separated from Haveno and is run as a standalone service. To deploy a pricenode on both TOR and clearnet, see the instructions on the repository: https://github.com/haveno-dex/haveno-pricenode. - -After the price node is built and deployed, add the price node to `DEFAULT_NODES` in [ProvidersRepository.java](https://github.com/haveno-dex/haveno/blob/3cdd88b56915c7f8afd4f1a39e6c1197c2665d63/core/src/main/java/haveno/core/provider/ProvidersRepository.java#L50). - -Customize and deploy haveno-pricenode.env and haveno-pricenode.service to run as a system service. - ## Add seed nodes ### Seed nodes without Proof of Work (PoW) @@ -139,7 +132,7 @@ Each seed node requires a locally running Monero node. You can use the default p Rebuild all seed nodes any time the list of registered seed nodes changes. -> **Notes** +> [!note] > * Avoid all seed nodes going offline at the same time. If all seed nodes go offline at the same time, the network will be reset, including registered arbitrators, the network filter object, and trade history. In that case, arbitrators need to restart or re-register, and the network filter object needs to be re-applied. This should be done immediately or clients will cancel their offers due to the signing arbitrators being unregistered and no replacements being available to re-sign. > * At least 2 seed nodes should be run because the seed nodes restart once per day. @@ -180,35 +173,21 @@ For each arbitrator: The arbitrator is now registered and ready to accept requests for dispute resolution. -**Notes** -- Arbitrators must use a local Monero node with unrestricted RPC in order to submit and flush transactions from the pool. -- Arbitrators should remain online as much as possible in order to balance trades and avoid clients spending time trying to contact offline arbitrators. A VPS or dedicated machine running 24/7 is highly recommended. -- Remember that for the network to run correctly and people to be able to open and accept trades, at least one arbitrator must be registered on the network. -- IMPORTANT: Do not reuse keypairs on multiple arbitrator instances. +> [!note] +> * Arbitrators must use a local Monero node with unrestricted RPC in order to submit and flush transactions from the pool. +> * Arbitrators should remain online as much as possible in order to balance trades and avoid clients spending time trying to contact offline arbitrators. A VPS or dedicated machine running 24/7 is highly recommended. +> * Remember that for the network to run correctly and people to be able to open and accept trades, at least one arbitrator must be registered on the network. +> * IMPORTANT: Do not reuse keypairs on multiple arbitrator instances. ## Remove an arbitrator -> **Note** -> Ensure the arbitrator's trades are completed before retiring the instance. +> [!warning] +> * Ensure the arbitrator's trades are completed before retiring the instance. +> * To preserve signed accounts, the arbitrator public key must remain in the repository, even after revoking. 1. Start the arbitrator's desktop application using the application launcher or e.g. `make arbitrator-desktop-mainnet` from the root of the repository. 2. Go to the `Account` tab and click the button to unregister the arbitrator. -> **Note** -> To preserve signed accounts, the arbitrator public key must remain in the repository, even after revoking. - -## Set a network filter on mainnet - -On mainnet, the p2p network is expected to have a filter object for offers, onions, currencies, payment methods, etc. - -To set the network's filter object: - -1. Enter `ctrl + f` in the arbitrator or other Haveno instance to open the Filter window. -2. Enter a developer private key from the previous steps and click "Add Filter" to register. - -> **Note** -> If all seed nodes are restarted at the same time, arbitrators and the filter object will become unregistered and will need to be re-registered. - ## Change the default folder name for Haveno application data To avoid user data corruption when using multiple Haveno networks, change the default folder name for Haveno's application data on your network: @@ -246,10 +225,30 @@ Set `ARBITRATOR_ASSIGNS_TRADE_FEE_ADDRESS` to `true` for the arbitrator to assig Otherwise set `ARBITRATOR_ASSIGNS_TRADE_FEE_ADDRESS` to `false` and set the XMR address in `getGlobalTradeFeeAddress()` to collect all trade fees to a single address (e.g. a multisig wallet shared among network administrators). +## Build and start price nodes + +The price node is separated from Haveno and is run as a standalone service. To deploy a pricenode on both TOR and clearnet, see the instructions on the repository: https://github.com/haveno-dex/haveno-pricenode. + +After the price node is built and deployed, add the price node to `DEFAULT_NODES` in [ProvidersRepository.java](https://github.com/haveno-dex/haveno/blob/3cdd88b56915c7f8afd4f1a39e6c1197c2665d63/core/src/main/java/haveno/core/provider/ProvidersRepository.java#L50). + +Customize and deploy haveno-pricenode.env and haveno-pricenode.service to run as a system service. + ## Update the download URL Change every instance of `https://haveno.exchange/downloads` to your download URL. For example, `https://havenoexample.com/downloads`. +## Set a network filter on mainnet + +On mainnet, the p2p network is expected to have a filter object for offers, onions, currencies, payment methods, etc. + +To set the network's filter object: + +1. Enter `ctrl + f` in the arbitrator or other Haveno instance to open the Filter window. +2. Enter a developer private key from the previous steps and click "Add Filter" to register. + +> [!note] +> If all seed nodes are restarted at the same time, arbitrators and the filter object will become unregistered and will need to be re-registered. + ## Start users for testing Start user1 on Monero's mainnet using `make user1-desktop-mainnet` or Monero's stagenet using `make user1-desktop-stagenet`. From 77429472f4d353d74ef166651549472af1d90d50 Mon Sep 17 00:00:00 2001 From: woodser Date: Mon, 21 Apr 2025 09:26:49 -0400 Subject: [PATCH 17/74] fix error popup when arbitrator nacks signing offer --- .../java/haveno/desktop/main/offer/MutableOfferViewModel.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/desktop/src/main/java/haveno/desktop/main/offer/MutableOfferViewModel.java b/desktop/src/main/java/haveno/desktop/main/offer/MutableOfferViewModel.java index 48adb22f9f..821f9e8a5c 100644 --- a/desktop/src/main/java/haveno/desktop/main/offer/MutableOfferViewModel.java +++ b/desktop/src/main/java/haveno/desktop/main/offer/MutableOfferViewModel.java @@ -653,7 +653,7 @@ public abstract class MutableOfferViewModel ext }, errMessage -> { createOfferInProgress = false; if (offer.getState() == Offer.State.OFFER_FEE_RESERVED) errorMessage.set(errMessage + Res.get("createOffer.errorInfo")); - else errorMessage.set(errorMessage.get()); + else errorMessage.set(errMessage); UserThread.execute(() -> { updateButtonDisableState(); From a3d3f51f02780f8722340700e9eeee48fb2357d1 Mon Sep 17 00:00:00 2001 From: woodser Date: Mon, 21 Apr 2025 10:29:06 -0400 Subject: [PATCH 18/74] change popup from warning to error on take offer error --- .../java/haveno/desktop/main/offer/takeoffer/TakeOfferView.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 dc11216f53..58f6bf4158 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 @@ -649,7 +649,7 @@ public class TakeOfferView extends ActivatableViewAndModel { if (newValue != null) { - new Popup().warning(Res.get("takeOffer.error.message", model.errorMessage.get())) + new Popup().error(Res.get("takeOffer.error.message", model.errorMessage.get())) .onClose(() -> { errorPopupDisplayed.set(true); model.resetErrorMessage(); From 9a14d5552efca8d326648968f2a6596575a35f66 Mon Sep 17 00:00:00 2001 From: XMRZombie Date: Mon, 21 Apr 2025 15:11:29 +0000 Subject: [PATCH 19/74] Update tails script to expect installer instead of archive Update haveno-install.sh Archive extraction bypass, also renaming the filename to package_filename trough mv for keeping install.sh stable --- scripts/install_tails/haveno-install.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/install_tails/haveno-install.sh b/scripts/install_tails/haveno-install.sh index e9a8c37bf4..16529f35e0 100755 --- a/scripts/install_tails/haveno-install.sh +++ b/scripts/install_tails/haveno-install.sh @@ -124,7 +124,7 @@ OUTPUT=$(gpg --digest-algo SHA256 --verify "${signature_filename}" "${binary_fil if ! echo "$OUTPUT" | grep -q "Good signature from"; then echo_red "Verification failed: $OUTPUT" exit 1; - else 7z x "${binary_filename}" && mv haveno*.deb "${package_filename}" + else mv -f "${binary_filename}" "${package_filename}" fi echo_blue "Haveno binaries have been successfully verified." @@ -136,7 +136,7 @@ mkdir -p "${install_dir}" # Delete old Haveno binaries #rm -f "${install_dir}/"*.deb* -mv "${binary_filename}" "${package_filename}" "${key_filename}" "${signature_filename}" "${install_dir}" +mv "${package_filename}" "${key_filename}" "${signature_filename}" "${install_dir}" echo_blue "Files moved to persistent directory ${install_dir}" From 923b3ad73bf921b57b2dd6e9029c2924f8160fe3 Mon Sep 17 00:00:00 2001 From: woodser <13068859+woodser@users.noreply.github.com> Date: Mon, 21 Apr 2025 17:39:47 -0400 Subject: [PATCH 20/74] do not await updating trade state properties on trade thread --- core/src/main/java/haveno/core/trade/Trade.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/haveno/core/trade/Trade.java b/core/src/main/java/haveno/core/trade/Trade.java index f121470e3a..e218bf970f 100644 --- a/core/src/main/java/haveno/core/trade/Trade.java +++ b/core/src/main/java/haveno/core/trade/Trade.java @@ -1852,7 +1852,7 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model { this.state = state; requestPersistence(); - UserThread.await(() -> { + UserThread.execute(() -> { stateProperty.set(state); phaseProperty.set(state.getPhase()); }); @@ -1884,7 +1884,7 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model { this.payoutState = payoutState; requestPersistence(); - UserThread.await(() -> payoutStateProperty.set(payoutState)); + UserThread.execute(() -> payoutStateProperty.set(payoutState)); } public void setDisputeState(DisputeState disputeState) { From de5250e89a613d19cc3fd1a34c3168fcef3f619e Mon Sep 17 00:00:00 2001 From: woodser <13068859+woodser@users.noreply.github.com> Date: Mon, 21 Apr 2025 18:14:22 -0400 Subject: [PATCH 21/74] persist trade with payment confirmation msgs before processing --- .../main/java/haveno/core/trade/Trade.java | 4 + .../java/haveno/core/trade/TradeManager.java | 4 + .../core/trade/protocol/TradeProtocol.java | 210 +++++++++--------- 3 files changed, 114 insertions(+), 104 deletions(-) diff --git a/core/src/main/java/haveno/core/trade/Trade.java b/core/src/main/java/haveno/core/trade/Trade.java index e218bf970f..769d7e95af 100644 --- a/core/src/main/java/haveno/core/trade/Trade.java +++ b/core/src/main/java/haveno/core/trade/Trade.java @@ -811,6 +811,10 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model { if (processModel.getTradeManager() != null) processModel.getTradeManager().requestPersistence(); } + public void persistNow(@Nullable Runnable completeHandler) { + processModel.getTradeManager().persistNow(completeHandler); + } + public TradeProtocol getProtocol() { return processModel.getTradeManager().getTradeProtocol(this); } diff --git a/core/src/main/java/haveno/core/trade/TradeManager.java b/core/src/main/java/haveno/core/trade/TradeManager.java index 9b092decac..e34cc9b1a7 100644 --- a/core/src/main/java/haveno/core/trade/TradeManager.java +++ b/core/src/main/java/haveno/core/trade/TradeManager.java @@ -546,6 +546,10 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi persistenceManager.requestPersistence(); } + public void persistNow(@Nullable Runnable completeHandler) { + persistenceManager.persistNow(completeHandler); + } + private void handleInitTradeRequest(InitTradeRequest request, NodeAddress sender) { log.info("TradeManager handling InitTradeRequest for tradeId={}, sender={}, uid={}", request.getOfferId(), sender, request.getUid()); diff --git a/core/src/main/java/haveno/core/trade/protocol/TradeProtocol.java b/core/src/main/java/haveno/core/trade/protocol/TradeProtocol.java index 65fcee23c6..09c26d8a1d 100644 --- a/core/src/main/java/haveno/core/trade/protocol/TradeProtocol.java +++ b/core/src/main/java/haveno/core/trade/protocol/TradeProtocol.java @@ -537,62 +537,63 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D // save message for reprocessing trade.getBuyer().setPaymentSentMessage(message); - trade.requestPersistence(); + trade.persistNow(() -> { - // process message on trade thread - if (!trade.isInitialized() || trade.isShutDownStarted()) return; - ThreadUtils.execute(() -> { - // We are more tolerant with expected phase and allow also DEPOSITS_PUBLISHED as it can be the case - // that the wallet is still syncing and so the DEPOSITS_CONFIRMED state to yet triggered when we received - // a mailbox message with PaymentSentMessage. - // TODO A better fix would be to add a listener for the wallet sync state and process - // the mailbox msg once wallet is ready and trade state set. - synchronized (trade.getLock()) { - if (!trade.isInitialized() || trade.isShutDownStarted()) return; - if (trade.getPhase().ordinal() >= Trade.Phase.PAYMENT_SENT.ordinal()) { - log.warn("Received another PaymentSentMessage which was already processed for {} {}, ACKing", trade.getClass().getSimpleName(), trade.getId()); - handleTaskRunnerSuccess(peer, message); - return; + // process message on trade thread + if (!trade.isInitialized() || trade.isShutDownStarted()) return; + ThreadUtils.execute(() -> { + // We are more tolerant with expected phase and allow also DEPOSITS_PUBLISHED as it can be the case + // that the wallet is still syncing and so the DEPOSITS_CONFIRMED state to yet triggered when we received + // a mailbox message with PaymentSentMessage. + // TODO A better fix would be to add a listener for the wallet sync state and process + // the mailbox msg once wallet is ready and trade state set. + synchronized (trade.getLock()) { + if (!trade.isInitialized() || trade.isShutDownStarted()) return; + if (trade.getPhase().ordinal() >= Trade.Phase.PAYMENT_SENT.ordinal()) { + log.warn("Received another PaymentSentMessage which was already processed for {} {}, ACKing", trade.getClass().getSimpleName(), trade.getId()); + handleTaskRunnerSuccess(peer, message); + return; + } + if (trade.getPayoutTx() != null) { + log.warn("We received a PaymentSentMessage but we have already created the payout tx " + + "so we ignore the message. This can happen if the ACK message to the peer did not " + + "arrive and the peer repeats sending us the message. We send another ACK msg."); + sendAckMessage(peer, message, true, null); + removeMailboxMessageAfterProcessing(message); + return; + } + latchTrade(); + expect(anyPhase() + .with(message) + .from(peer)) + .setup(tasks( + ApplyFilter.class, + ProcessPaymentSentMessage.class, + VerifyPeersAccountAgeWitness.class) + .using(new TradeTaskRunner(trade, + () -> { + handleTaskRunnerSuccess(peer, message); + }, + (errorMessage) -> { + log.warn("Error processing payment sent message: " + errorMessage); + processModel.getTradeManager().requestPersistence(); + + // schedule to reprocess message unless deleted + if (trade.getBuyer().getPaymentSentMessage() != null) { + UserThread.runAfter(() -> { + reprocessPaymentSentMessageCount++; + maybeReprocessPaymentSentMessage(reprocessOnError); + }, trade.getReprocessDelayInSeconds(reprocessPaymentSentMessageCount)); + } else { + handleTaskRunnerFault(peer, message, errorMessage); // otherwise send nack + } + unlatchTrade(); + }))) + .executeTasks(true); + awaitTradeLatch(); } - if (trade.getPayoutTx() != null) { - log.warn("We received a PaymentSentMessage but we have already created the payout tx " + - "so we ignore the message. This can happen if the ACK message to the peer did not " + - "arrive and the peer repeats sending us the message. We send another ACK msg."); - sendAckMessage(peer, message, true, null); - removeMailboxMessageAfterProcessing(message); - return; - } - latchTrade(); - expect(anyPhase() - .with(message) - .from(peer)) - .setup(tasks( - ApplyFilter.class, - ProcessPaymentSentMessage.class, - VerifyPeersAccountAgeWitness.class) - .using(new TradeTaskRunner(trade, - () -> { - handleTaskRunnerSuccess(peer, message); - }, - (errorMessage) -> { - log.warn("Error processing payment sent message: " + errorMessage); - processModel.getTradeManager().requestPersistence(); - - // schedule to reprocess message unless deleted - if (trade.getBuyer().getPaymentSentMessage() != null) { - UserThread.runAfter(() -> { - reprocessPaymentSentMessageCount++; - maybeReprocessPaymentSentMessage(reprocessOnError); - }, trade.getReprocessDelayInSeconds(reprocessPaymentSentMessageCount)); - } else { - handleTaskRunnerFault(peer, message, errorMessage); // otherwise send nack - } - unlatchTrade(); - }))) - .executeTasks(true); - awaitTradeLatch(); - } - }, trade.getId()); + }, trade.getId()); + }); } // received by buyer and arbitrator @@ -619,59 +620,60 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D // save message for reprocessing trade.getSeller().setPaymentReceivedMessage(message); - trade.requestPersistence(); + trade.persistNow(() -> { - // process message on trade thread - if (!trade.isInitialized() || trade.isShutDownStarted()) return; - ThreadUtils.execute(() -> { - synchronized (trade.getLock()) { - if (!trade.isInitialized() || trade.isShutDownStarted()) return; - latchTrade(); - Validator.checkTradeId(processModel.getOfferId(), message); - processModel.setTradeMessage(message); + // process message on trade thread + if (!trade.isInitialized() || trade.isShutDownStarted()) return; + ThreadUtils.execute(() -> { + synchronized (trade.getLock()) { + if (!trade.isInitialized() || trade.isShutDownStarted()) return; + latchTrade(); + Validator.checkTradeId(processModel.getOfferId(), message); + processModel.setTradeMessage(message); - // check minimum trade phase - if (trade.isBuyer() && trade.getPhase().ordinal() < Trade.Phase.PAYMENT_SENT.ordinal()) { - log.warn("Received PaymentReceivedMessage before payment sent for {} {}, ignoring", trade.getClass().getSimpleName(), trade.getId()); - return; + // check minimum trade phase + if (trade.isBuyer() && trade.getPhase().ordinal() < Trade.Phase.PAYMENT_SENT.ordinal()) { + log.warn("Received PaymentReceivedMessage before payment sent for {} {}, ignoring", trade.getClass().getSimpleName(), trade.getId()); + return; + } + if (trade.isArbitrator() && trade.getPhase().ordinal() < Trade.Phase.DEPOSITS_CONFIRMED.ordinal()) { + log.warn("Received PaymentReceivedMessage before deposits confirmed for {} {}, ignoring", trade.getClass().getSimpleName(), trade.getId()); + return; + } + if (trade.isSeller() && trade.getPhase().ordinal() < Trade.Phase.DEPOSITS_UNLOCKED.ordinal()) { + log.warn("Received PaymentReceivedMessage before deposits unlocked for {} {}, ignoring", trade.getClass().getSimpleName(), trade.getId()); + return; + } + + expect(anyPhase() + .with(message) + .from(peer)) + .setup(tasks( + ProcessPaymentReceivedMessage.class) + .using(new TradeTaskRunner(trade, + () -> { + handleTaskRunnerSuccess(peer, message); + }, + errorMessage -> { + log.warn("Error processing payment received message: " + errorMessage); + processModel.getTradeManager().requestPersistence(); + + // schedule to reprocess message unless deleted + if (trade.getSeller().getPaymentReceivedMessage() != null) { + UserThread.runAfter(() -> { + reprocessPaymentReceivedMessageCount++; + maybeReprocessPaymentReceivedMessage(reprocessOnError); + }, trade.getReprocessDelayInSeconds(reprocessPaymentReceivedMessageCount)); + } else { + handleTaskRunnerFault(peer, message, errorMessage); // otherwise send nack + } + unlatchTrade(); + }))) + .executeTasks(true); + awaitTradeLatch(); } - if (trade.isArbitrator() && trade.getPhase().ordinal() < Trade.Phase.DEPOSITS_CONFIRMED.ordinal()) { - log.warn("Received PaymentReceivedMessage before deposits confirmed for {} {}, ignoring", trade.getClass().getSimpleName(), trade.getId()); - return; - } - if (trade.isSeller() && trade.getPhase().ordinal() < Trade.Phase.DEPOSITS_UNLOCKED.ordinal()) { - log.warn("Received PaymentReceivedMessage before deposits unlocked for {} {}, ignoring", trade.getClass().getSimpleName(), trade.getId()); - return; - } - - expect(anyPhase() - .with(message) - .from(peer)) - .setup(tasks( - ProcessPaymentReceivedMessage.class) - .using(new TradeTaskRunner(trade, - () -> { - handleTaskRunnerSuccess(peer, message); - }, - errorMessage -> { - log.warn("Error processing payment received message: " + errorMessage); - processModel.getTradeManager().requestPersistence(); - - // schedule to reprocess message unless deleted - if (trade.getSeller().getPaymentReceivedMessage() != null) { - UserThread.runAfter(() -> { - reprocessPaymentReceivedMessageCount++; - maybeReprocessPaymentReceivedMessage(reprocessOnError); - }, trade.getReprocessDelayInSeconds(reprocessPaymentReceivedMessageCount)); - } else { - handleTaskRunnerFault(peer, message, errorMessage); // otherwise send nack - } - unlatchTrade(); - }))) - .executeTasks(true); - awaitTradeLatch(); - } - }, trade.getId()); + }, trade.getId()); + }); } public void onWithdrawCompleted() { From bd9c28fafa42937e7d6fcf153e9d88a7e26955a6 Mon Sep 17 00:00:00 2001 From: woodser <13068859+woodser@users.noreply.github.com> Date: Wed, 23 Apr 2025 12:05:39 -0400 Subject: [PATCH 22/74] remove expected warning when wallet is null --- core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java b/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java index 8b319622b9..0e91e5fa1d 100644 --- a/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java +++ b/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java @@ -1448,7 +1448,7 @@ public class XmrWalletService extends XmrWalletBase { try { syncWithProgress(true); // repeat sync to latest target height } catch (Exception e) { - log.warn("Error syncing wallet with progress on startup: " + e.getMessage()); + if (wallet != null) log.warn("Error syncing wallet with progress on startup: " + e.getMessage()); forceCloseMainWallet(); requestSwitchToNextBestConnection(sourceConnection); maybeInitMainWallet(true, numSyncAttempts - 1); // re-initialize wallet and sync again From 8611593a3f35d9d522d98ccac08290de9baca1fc Mon Sep 17 00:00:00 2001 From: woodser <13068859+woodser@users.noreply.github.com> Date: Wed, 23 Apr 2025 12:23:53 -0400 Subject: [PATCH 23/74] do not force restart main wallet on connection change with same config --- .../haveno/core/xmr/wallet/XmrWalletService.java | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java b/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java index 0e91e5fa1d..838ebfc5bc 100644 --- a/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java +++ b/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java @@ -1367,11 +1367,16 @@ public class XmrWalletService extends XmrWalletBase { }, THREAD_ID); } else { - // force restart main wallet if connection changed while syncing - if (wallet != null) { - log.warn("Force restarting main wallet because connection changed while syncing"); - forceRestartMainWallet(); + // check if ignored + if (wallet == null || isShutDownStarted) return; + if (HavenoUtils.connectionConfigsEqual(connection, wallet.getDaemonConnection())) { + updatePollPeriod(); + return; } + + // force restart main wallet if connection changed while syncing + log.warn("Force restarting main wallet because connection changed while syncing"); + forceRestartMainWallet(); } }); From 58506b02f595d810711e3f82ac47b4561910f105 Mon Sep 17 00:00:00 2001 From: woodser <13068859+woodser@users.noreply.github.com> Date: Wed, 23 Apr 2025 12:38:51 -0400 Subject: [PATCH 24/74] recover from payment received nack with updated multisig info --- .../haveno/core/network/MessageState.java | 3 +- .../java/haveno/core/trade/SellerTrade.java | 5 + .../main/java/haveno/core/trade/Trade.java | 8 +- .../java/haveno/core/trade/TradeManager.java | 7 +- .../core/trade/protocol/ProcessModel.java | 2 +- .../haveno/core/trade/protocol/TradePeer.java | 8 +- .../core/trade/protocol/TradeProtocol.java | 93 +++++++++++++++---- .../SellerSendPaymentReceivedMessage.java | 16 ++-- .../steps/buyer/BuyerStep3View.java | 1 + .../steps/seller/SellerStep3View.java | 3 + .../java/haveno/network/p2p/AckMessage.java | 28 ++++++ proto/src/main/proto/pb.proto | 1 + 12 files changed, 138 insertions(+), 37 deletions(-) diff --git a/core/src/main/java/haveno/core/network/MessageState.java b/core/src/main/java/haveno/core/network/MessageState.java index c4ae7f8f40..759b87d0a2 100644 --- a/core/src/main/java/haveno/core/network/MessageState.java +++ b/core/src/main/java/haveno/core/network/MessageState.java @@ -23,5 +23,6 @@ public enum MessageState { ARRIVED, STORED_IN_MAILBOX, ACKNOWLEDGED, - FAILED + FAILED, + NACKED } diff --git a/core/src/main/java/haveno/core/trade/SellerTrade.java b/core/src/main/java/haveno/core/trade/SellerTrade.java index fae3cce7a1..ddccfe59c3 100644 --- a/core/src/main/java/haveno/core/trade/SellerTrade.java +++ b/core/src/main/java/haveno/core/trade/SellerTrade.java @@ -19,6 +19,7 @@ package haveno.core.trade; import haveno.core.offer.Offer; import haveno.core.trade.protocol.ProcessModel; +import haveno.core.trade.protocol.SellerProtocol; import haveno.core.xmr.wallet.XmrWalletService; import haveno.network.p2p.NodeAddress; import lombok.extern.slf4j.Slf4j; @@ -59,5 +60,9 @@ public abstract class SellerTrade extends Trade { public boolean confirmPermitted() { return true; } + + public boolean isFinished() { + return super.isFinished() && ((SellerProtocol) getProtocol()).needsToResendPaymentReceivedMessages(); + } } diff --git a/core/src/main/java/haveno/core/trade/Trade.java b/core/src/main/java/haveno/core/trade/Trade.java index 769d7e95af..b7387a9bd7 100644 --- a/core/src/main/java/haveno/core/trade/Trade.java +++ b/core/src/main/java/haveno/core/trade/Trade.java @@ -787,12 +787,15 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model { } public boolean isFinished() { - return isPayoutUnlocked() && isCompleted() && !getProtocol().needsToResendPaymentReceivedMessages(); + return isPayoutUnlocked() && isCompleted(); } public void resetToPaymentSentState() { setState(Trade.State.BUYER_SENT_PAYMENT_SENT_MSG); - for (TradePeer peer : getAllPeers()) peer.setPaymentReceivedMessage(null); + for (TradePeer peer : getAllPeers()) { + peer.setPaymentReceivedMessage(null); + peer.setPaymentReceivedMessageState(MessageState.UNDEFINED); + } setPayoutTxHex(null); } @@ -2105,6 +2108,7 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model { private MessageState getPaymentSentMessageState() { if (isPaymentReceived()) return MessageState.ACKNOWLEDGED; if (getSeller().getPaymentSentMessageStateProperty().get() == MessageState.ACKNOWLEDGED) return MessageState.ACKNOWLEDGED; + if (getSeller().getPaymentSentMessageStateProperty().get() == MessageState.NACKED) return MessageState.NACKED; switch (state) { case BUYER_SENT_PAYMENT_SENT_MSG: return MessageState.SENT; diff --git a/core/src/main/java/haveno/core/trade/TradeManager.java b/core/src/main/java/haveno/core/trade/TradeManager.java index e34cc9b1a7..9befbd6e82 100644 --- a/core/src/main/java/haveno/core/trade/TradeManager.java +++ b/core/src/main/java/haveno/core/trade/TradeManager.java @@ -683,7 +683,7 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi if (!sender.equals(request.getTakerNodeAddress())) { if (sender.equals(request.getMakerNodeAddress())) { log.warn("Received InitTradeRequest from maker to arbitrator for trade that is already initializing, tradeId={}, sender={}", request.getOfferId(), sender); - sendAckMessage(sender, trade.getMaker().getPubKeyRing(), request, false, "Trade is already initializing for " + getClass().getSimpleName() + " " + trade.getId()); + sendAckMessage(sender, trade.getMaker().getPubKeyRing(), request, false, "Trade is already initializing for " + getClass().getSimpleName() + " " + trade.getId(), null); } else { log.warn("Ignoring InitTradeRequest from non-taker, tradeId={}, sender={}", request.getOfferId(), sender); } @@ -1212,7 +1212,7 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi // Getters, Utils /////////////////////////////////////////////////////////////////////////////////////////// - public void sendAckMessage(NodeAddress peer, PubKeyRing peersPubKeyRing, TradeMessage message, boolean result, @Nullable String errorMessage) { + public void sendAckMessage(NodeAddress peer, PubKeyRing peersPubKeyRing, TradeMessage message, boolean result, @Nullable String errorMessage, String updatedMultisigHex) { // create ack message String tradeId = message.getOfferId(); @@ -1223,7 +1223,8 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi sourceUid, tradeId, result, - errorMessage); + errorMessage, + updatedMultisigHex); // send ack message log.info("Send AckMessage for {} to peer {}. tradeId={}, sourceUid={}", diff --git a/core/src/main/java/haveno/core/trade/protocol/ProcessModel.java b/core/src/main/java/haveno/core/trade/protocol/ProcessModel.java index ae6a27e7f0..4f5ce9e66a 100644 --- a/core/src/main/java/haveno/core/trade/protocol/ProcessModel.java +++ b/core/src/main/java/haveno/core/trade/protocol/ProcessModel.java @@ -311,7 +311,7 @@ public class ProcessModel implements Model, PersistablePayload { void setDepositTxSentAckMessage(AckMessage ackMessage) { MessageState messageState = ackMessage.isSuccess() ? MessageState.ACKNOWLEDGED : - MessageState.FAILED; + MessageState.NACKED; setDepositTxMessageState(messageState); } diff --git a/core/src/main/java/haveno/core/trade/protocol/TradePeer.java b/core/src/main/java/haveno/core/trade/protocol/TradePeer.java index 11c035a329..7ec33716a1 100644 --- a/core/src/main/java/haveno/core/trade/protocol/TradePeer.java +++ b/core/src/main/java/haveno/core/trade/protocol/TradePeer.java @@ -208,21 +208,21 @@ public final class TradePeer implements PersistablePayload { void setDepositsConfirmedAckMessage(AckMessage ackMessage) { MessageState messageState = ackMessage.isSuccess() ? MessageState.ACKNOWLEDGED : - MessageState.FAILED; + MessageState.NACKED; setDepositsConfirmedMessageState(messageState); } void setPaymentSentAckMessage(AckMessage ackMessage) { MessageState messageState = ackMessage.isSuccess() ? MessageState.ACKNOWLEDGED : - MessageState.FAILED; + MessageState.NACKED; setPaymentSentMessageState(messageState); } void setPaymentReceivedAckMessage(AckMessage ackMessage) { MessageState messageState = ackMessage.isSuccess() ? MessageState.ACKNOWLEDGED : - MessageState.FAILED; + MessageState.NACKED; setPaymentReceivedMessageState(messageState); } @@ -256,7 +256,7 @@ public final class TradePeer implements PersistablePayload { } public boolean isPaymentReceivedMessageReceived() { - return paymentReceivedMessageStateProperty.get() == MessageState.ACKNOWLEDGED || paymentReceivedMessageStateProperty.get() == MessageState.STORED_IN_MAILBOX; + return paymentReceivedMessageStateProperty.get() == MessageState.ACKNOWLEDGED || paymentReceivedMessageStateProperty.get() == MessageState.STORED_IN_MAILBOX || paymentReceivedMessageStateProperty.get() == MessageState.NACKED; } @Override diff --git a/core/src/main/java/haveno/core/trade/protocol/TradeProtocol.java b/core/src/main/java/haveno/core/trade/protocol/TradeProtocol.java index 09c26d8a1d..6272d40749 100644 --- a/core/src/main/java/haveno/core/trade/protocol/TradeProtocol.java +++ b/core/src/main/java/haveno/core/trade/protocol/TradeProtocol.java @@ -42,6 +42,7 @@ import haveno.common.crypto.PubKeyRing; import haveno.common.handlers.ErrorMessageHandler; import haveno.common.proto.network.NetworkEnvelope; import haveno.common.taskrunner.Task; +import haveno.core.network.MessageState; import haveno.core.trade.ArbitratorTrade; import haveno.core.trade.BuyerTrade; import haveno.core.trade.HavenoUtils; @@ -272,7 +273,7 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D handleTaskRunnerSuccess(null, null, "maybeSendDepositsConfirmedMessages"); }, (errorMessage) -> { - handleTaskRunnerFault(null, null, "maybeSendDepositsConfirmedMessages", errorMessage); + handleTaskRunnerFault(null, null, "maybeSendDepositsConfirmedMessages", errorMessage, null); }))) .executeTasks(true); awaitTradeLatch(); @@ -280,10 +281,6 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D }, trade.getId()); } - public boolean needsToResendPaymentReceivedMessages() { - return false; // seller protocol overrides - } - public void maybeReprocessPaymentSentMessage(boolean reprocessOnError) { if (trade.isShutDownStarted()) return; ThreadUtils.execute(() -> { @@ -627,6 +624,11 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D ThreadUtils.execute(() -> { synchronized (trade.getLock()) { if (!trade.isInitialized() || trade.isShutDownStarted()) return; + if (trade.getPhase().ordinal() >= Trade.Phase.PAYMENT_RECEIVED.ordinal()) { + log.warn("Received another PaymentReceivedMessage which was already processed for {} {}, ACKing", trade.getClass().getSimpleName(), trade.getId()); + handleTaskRunnerSuccess(peer, message); + return; + } latchTrade(); Validator.checkTradeId(processModel.getOfferId(), message); processModel.setTradeMessage(message); @@ -665,7 +667,7 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D maybeReprocessPaymentReceivedMessage(reprocessOnError); }, trade.getReprocessDelayInSeconds(reprocessPaymentReceivedMessageCount)); } else { - handleTaskRunnerFault(peer, message, errorMessage); // otherwise send nack + handleTaskRunnerFault(peer, message, null, errorMessage, trade.getSelf().getUpdatedMultisigHex()); // otherwise send nack } unlatchTrade(); }))) @@ -694,7 +696,8 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D handleTaskRunnerFault(null, null, result.name(), - result.getInfo()); + result.getInfo(), + null); } }); } @@ -734,7 +737,7 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D private void onAckMessage(AckMessage ackMessage, NodeAddress sender) { // ignore if trade is completely finished - if (trade.isFinished()) return; + if (trade.isFinished()) return; // get trade peer TradePeer peer = trade.getTradePeer(sender); @@ -755,7 +758,7 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D peer.setNodeAddress(sender); } - // set trade state on deposit request nack + // handle nack of deposit request if (ackMessage.getSourceMsgClassName().equals(DepositRequest.class.getSimpleName())) { if (!ackMessage.isSuccess()) { trade.setStateIfValidTransitionTo(Trade.State.PUBLISH_DEPOSIT_TX_REQUEST_FAILED); @@ -763,13 +766,13 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D } } - // handle ack for DepositsConfirmedMessage, which automatically re-sends if not ACKed in a certain time + // handle ack message for DepositsConfirmedMessage, which automatically re-sends if not ACKed in a certain time if (ackMessage.getSourceMsgClassName().equals(DepositsConfirmedMessage.class.getSimpleName())) { peer.setDepositsConfirmedAckMessage(ackMessage); processModel.getTradeManager().requestPersistence(); } - // handle ack for PaymentSentMessage, which automatically re-sends if not ACKed in a certain time + // handle ack message for PaymentSentMessage, which automatically re-sends if not ACKed in a certain time if (ackMessage.getSourceMsgClassName().equals(PaymentSentMessage.class.getSimpleName())) { if (trade.getTradePeer(sender) == trade.getSeller()) { trade.getSeller().setPaymentSentAckMessage(ackMessage); @@ -785,15 +788,55 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D } } - // handle ack for PaymentReceivedMessage, which automatically re-sends if not ACKed in a certain time + // handle ack message for PaymentReceivedMessage, which automatically re-sends if not ACKed in a certain time if (ackMessage.getSourceMsgClassName().equals(PaymentReceivedMessage.class.getSimpleName())) { + + // ack message from buyer if (trade.getTradePeer(sender) == trade.getBuyer()) { trade.getBuyer().setPaymentReceivedAckMessage(ackMessage); - if (ackMessage.isSuccess()) trade.setStateIfValidTransitionTo(Trade.State.BUYER_RECEIVED_PAYMENT_RECEIVED_MSG); - else trade.setState(Trade.State.SELLER_SEND_FAILED_PAYMENT_RECEIVED_MSG); + + // handle successful ack + if (ackMessage.isSuccess()) { + trade.setStateIfValidTransitionTo(Trade.State.BUYER_RECEIVED_PAYMENT_RECEIVED_MSG); + } + + // handle nack + else { + log.warn("We received a NACK for our PaymentReceivedMessage to the buyer for {} {}", trade.getClass().getSimpleName(), trade.getId()); + + // update multisig hex + if (ackMessage.getUpdatedMultisigHex() != null) { + trade.getBuyer().setUpdatedMultisigHex(ackMessage.getUpdatedMultisigHex()); + } + + // reset state if not processed + if (trade.isPaymentReceived() && !trade.isPayoutPublished() && !isPaymentReceivedMessageAckedByEither()) { + log.warn("Resetting state to payment sent for {} {}", trade.getClass().getSimpleName(), trade.getId()); + trade.resetToPaymentSentState(); + } + } processModel.getTradeManager().requestPersistence(); - } else if (trade.getTradePeer(sender) == trade.getArbitrator()) { + } + + // ack message from arbitrator + else if (trade.getTradePeer(sender) == trade.getArbitrator()) { trade.getArbitrator().setPaymentReceivedAckMessage(ackMessage); + + // handle nack + if (!ackMessage.isSuccess()) { + log.warn("We received a NACK for our PaymentReceivedMessage to the arbitrator for {} {}", trade.getClass().getSimpleName(), trade.getId()); + + // update multisig hex + if (ackMessage.getUpdatedMultisigHex() != null) { + trade.getArbitrator().setUpdatedMultisigHex(ackMessage.getUpdatedMultisigHex()); + } + + // reset state if not processed + if (trade.isPaymentReceived() && !trade.isPayoutPublished() && !isPaymentReceivedMessageAckedByEither()) { + log.warn("Resetting state to payment sent for {} {}", trade.getClass().getSimpleName(), trade.getId()); + trade.resetToPaymentSentState(); + } + } processModel.getTradeManager().requestPersistence(); } else { log.warn("Received AckMessage from unexpected peer for {}, sender={}, trade={} {}, messageUid={}, success={}, errorMsg={}", ackMessage.getSourceMsgClassName(), sender, trade.getClass().getSimpleName(), trade.getId(), ackMessage.getSourceUid(), ackMessage.isSuccess(), ackMessage.getErrorMessage()); @@ -813,7 +856,17 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D trade.onAckMessage(ackMessage, sender); } + private boolean isPaymentReceivedMessageAckedByEither() { + if (trade.getBuyer().getPaymentReceivedMessageStateProperty().get() == MessageState.ACKNOWLEDGED) return true; + if (trade.getArbitrator().getPaymentReceivedMessageStateProperty().get() == MessageState.ACKNOWLEDGED) return true; + return false; + } + protected void sendAckMessage(NodeAddress peer, TradeMessage message, boolean result, @Nullable String errorMessage) { + sendAckMessage(peer, message, result, errorMessage, null); + } + + protected void sendAckMessage(NodeAddress peer, TradeMessage message, boolean result, @Nullable String errorMessage, String updatedMultisigHex) { // get peer's pub key ring PubKeyRing peersPubKeyRing = getPeersPubKeyRing(peer); @@ -823,7 +876,7 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D } // send ack message - processModel.getTradeManager().sendAckMessage(peer, peersPubKeyRing, message, result, errorMessage); + processModel.getTradeManager().sendAckMessage(peer, peersPubKeyRing, message, result, errorMessage, updatedMultisigHex); } @@ -870,11 +923,11 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D } protected void handleTaskRunnerFault(NodeAddress sender, TradeMessage message, String errorMessage) { - handleTaskRunnerFault(sender, message, message.getClass().getSimpleName(), errorMessage); + handleTaskRunnerFault(sender, message, message.getClass().getSimpleName(), errorMessage, null); } protected void handleTaskRunnerFault(FluentProtocol.Event event, String errorMessage) { - handleTaskRunnerFault(null, null, event.name(), errorMessage); + handleTaskRunnerFault(null, null, event.name(), errorMessage, null); } @@ -936,11 +989,11 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D unlatchTrade(); } - void handleTaskRunnerFault(NodeAddress ackReceiver, @Nullable TradeMessage message, String source, String errorMessage) { + void handleTaskRunnerFault(NodeAddress ackReceiver, @Nullable TradeMessage message, String source, String errorMessage, String updatedMultisigHex) { log.error("Task runner failed with error {}. Triggered from {}. Monerod={}" , errorMessage, source, trade.getXmrWalletService().getXmrConnectionService().getConnection()); if (message != null) { - sendAckMessage(ackReceiver, message, false, errorMessage); + sendAckMessage(ackReceiver, message, false, errorMessage, updatedMultisigHex); } handleError(errorMessage); diff --git a/core/src/main/java/haveno/core/trade/protocol/tasks/SellerSendPaymentReceivedMessage.java b/core/src/main/java/haveno/core/trade/protocol/tasks/SellerSendPaymentReceivedMessage.java index 202d4c8c79..9fe31dba60 100644 --- a/core/src/main/java/haveno/core/trade/protocol/tasks/SellerSendPaymentReceivedMessage.java +++ b/core/src/main/java/haveno/core/trade/protocol/tasks/SellerSendPaymentReceivedMessage.java @@ -90,8 +90,8 @@ public abstract class SellerSendPaymentReceivedMessage extends SendMailboxMessag try { runInterceptHook(); - // skip if already received - if (isReceived()) { + // skip if stopped + if (stopSending()) { if (!isCompleted()) complete(); return; } @@ -191,8 +191,8 @@ public abstract class SellerSendPaymentReceivedMessage extends SendMailboxMessag private void tryToSendAgainLater() { - // skip if already received - if (isReceived()) return; + // skip if stopped + if (stopSending()) return; if (resendCounter >= MAX_RESEND_ATTEMPTS) { cleanup(); @@ -226,12 +226,16 @@ public abstract class SellerSendPaymentReceivedMessage extends SendMailboxMessag } private void onMessageStateChange(MessageState newValue) { - if (isReceived()) { + if (isMessageReceived()) { cleanup(); } } - protected boolean isReceived() { + protected boolean isMessageReceived() { return getReceiver().isPaymentReceivedMessageReceived(); } + + protected boolean stopSending() { + return isMessageReceived() || !trade.isPaymentReceived(); // stop if received or trade state reset // TODO: also stop after some number of blocks? + } } diff --git a/desktop/src/main/java/haveno/desktop/main/portfolio/pendingtrades/steps/buyer/BuyerStep3View.java b/desktop/src/main/java/haveno/desktop/main/portfolio/pendingtrades/steps/buyer/BuyerStep3View.java index b28eda4a10..dba3fe4a3d 100644 --- a/desktop/src/main/java/haveno/desktop/main/portfolio/pendingtrades/steps/buyer/BuyerStep3View.java +++ b/desktop/src/main/java/haveno/desktop/main/portfolio/pendingtrades/steps/buyer/BuyerStep3View.java @@ -112,6 +112,7 @@ public class BuyerStep3View extends TradeStepView { iconLabel.getStyleClass().add("trade-msg-state-stored"); break; case FAILED: + case NACKED: textFieldWithIcon.setIcon(AwesomeIcon.EXCLAMATION_SIGN); iconLabel.getStyleClass().add("trade-msg-state-acknowledged"); break; diff --git a/desktop/src/main/java/haveno/desktop/main/portfolio/pendingtrades/steps/seller/SellerStep3View.java b/desktop/src/main/java/haveno/desktop/main/portfolio/pendingtrades/steps/seller/SellerStep3View.java index c6fe0cab23..20e12a3f56 100644 --- a/desktop/src/main/java/haveno/desktop/main/portfolio/pendingtrades/steps/seller/SellerStep3View.java +++ b/desktop/src/main/java/haveno/desktop/main/portfolio/pendingtrades/steps/seller/SellerStep3View.java @@ -151,6 +151,9 @@ public class SellerStep3View extends TradeStepView { break; } } + + // update confirm button state + confirmButton.setDisable(!confirmPaymentReceivedPermitted()); }); } diff --git a/p2p/src/main/java/haveno/network/p2p/AckMessage.java b/p2p/src/main/java/haveno/network/p2p/AckMessage.java index 7a7a0ff990..6d6470d568 100644 --- a/p2p/src/main/java/haveno/network/p2p/AckMessage.java +++ b/p2p/src/main/java/haveno/network/p2p/AckMessage.java @@ -53,6 +53,8 @@ public final class AckMessage extends NetworkEnvelope implements MailboxMessage, private final boolean success; @Nullable private final String errorMessage; + @Nullable + private final String updatedMultisigHex; /** * @@ -79,6 +81,27 @@ public final class AckMessage extends NetworkEnvelope implements MailboxMessage, sourceId, success, errorMessage, + null, + Version.getP2PMessageVersion()); + } + + public AckMessage(NodeAddress senderNodeAddress, + AckMessageSourceType sourceType, + String sourceMsgClassName, + String sourceUid, + String sourceId, + boolean success, + String errorMessage, + String updatedMultisigHex) { + this(UUID.randomUUID().toString(), + senderNodeAddress, + sourceType, + sourceMsgClassName, + sourceUid, + sourceId, + success, + errorMessage, + updatedMultisigHex, Version.getP2PMessageVersion()); } @@ -95,6 +118,7 @@ public final class AckMessage extends NetworkEnvelope implements MailboxMessage, String sourceId, boolean success, @Nullable String errorMessage, + String updatedMultisigInfo, String messageVersion) { super(messageVersion); this.uid = uid; @@ -105,6 +129,7 @@ public final class AckMessage extends NetworkEnvelope implements MailboxMessage, this.sourceId = sourceId; this.success = success; this.errorMessage = errorMessage; + this.updatedMultisigHex = updatedMultisigInfo; } public protobuf.AckMessage toProtoMessage() { @@ -126,6 +151,7 @@ public final class AckMessage extends NetworkEnvelope implements MailboxMessage, .setSuccess(success); Optional.ofNullable(sourceUid).ifPresent(builder::setSourceUid); Optional.ofNullable(errorMessage).ifPresent(builder::setErrorMessage); + Optional.ofNullable(updatedMultisigHex).ifPresent(builder::setUpdatedMultisigHex); return builder; } @@ -139,6 +165,7 @@ public final class AckMessage extends NetworkEnvelope implements MailboxMessage, proto.getSourceId(), proto.getSuccess(), proto.getErrorMessage().isEmpty() ? null : proto.getErrorMessage(), + proto.getUpdatedMultisigHex().isEmpty() ? null : proto.getUpdatedMultisigHex(), messageVersion); } @@ -163,6 +190,7 @@ public final class AckMessage extends NetworkEnvelope implements MailboxMessage, ",\n sourceId='" + sourceId + '\'' + ",\n success=" + success + ",\n errorMessage='" + errorMessage + '\'' + + ",\n updatedMultisigInfo='" + updatedMultisigHex + '\'' + "\n} " + super.toString(); } } diff --git a/proto/src/main/proto/pb.proto b/proto/src/main/proto/pb.proto index 5cdde1f0ce..022031d08d 100644 --- a/proto/src/main/proto/pb.proto +++ b/proto/src/main/proto/pb.proto @@ -216,6 +216,7 @@ message AckMessage { string source_id = 6; // id of source (tradeId, disputeId) bool success = 7; // true if source message was processed successfully string error_message = 8; // optional error message if source message processing failed + string updated_multisig_hex = 9; // data to update the multisig state } message PrefixedSealedAndSignedMessage { From adcd5da4313ad5f3f3f44cd4202bd159c47f614c Mon Sep 17 00:00:00 2001 From: woodser <13068859+woodser@users.noreply.github.com> Date: Fri, 25 Apr 2025 08:32:41 -0400 Subject: [PATCH 25/74] fix shut down of trade and wallet services in seed node --- .../app/misc/ExecutableForAppWithP2p.java | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/core/src/main/java/haveno/core/app/misc/ExecutableForAppWithP2p.java b/core/src/main/java/haveno/core/app/misc/ExecutableForAppWithP2p.java index 9c8016d506..3184d9ba11 100644 --- a/core/src/main/java/haveno/core/app/misc/ExecutableForAppWithP2p.java +++ b/core/src/main/java/haveno/core/app/misc/ExecutableForAppWithP2p.java @@ -151,23 +151,23 @@ public abstract class ExecutableForAppWithP2p extends HavenoExecutable { UserThread.runAfter(() -> System.exit(HavenoExecutable.EXIT_SUCCESS), 1); }); }); - - // shut down trade and wallet services - log.info("Shutting down trade and wallet services"); - injector.getInstance(OfferBookService.class).shutDown(); - injector.getInstance(TradeManager.class).shutDown(); - injector.getInstance(BtcWalletService.class).shutDown(); - injector.getInstance(XmrWalletService.class).shutDown(); - injector.getInstance(XmrConnectionService.class).shutDown(); - injector.getInstance(WalletsSetup.class).shutDown(); }); + + // shut down trade and wallet services + log.info("Shutting down trade and wallet services"); + injector.getInstance(OfferBookService.class).shutDown(); + injector.getInstance(TradeManager.class).shutDown(); + injector.getInstance(BtcWalletService.class).shutDown(); + injector.getInstance(XmrWalletService.class).shutDown(); + injector.getInstance(XmrConnectionService.class).shutDown(); + injector.getInstance(WalletsSetup.class).shutDown(); }); // we wait max 5 sec. UserThread.runAfter(() -> { PersistenceManager.flushAllDataToDiskAtShutdown(() -> { resultHandler.handleResult(); - log.info("Graceful shutdown caused a timeout. Exiting now."); + log.warn("Graceful shutdown caused a timeout. Exiting now."); UserThread.runAfter(() -> System.exit(HavenoExecutable.EXIT_SUCCESS), 1); }); }, 5); From 350aa1083991d6e8f015302009e3c823bd228531 Mon Sep 17 00:00:00 2001 From: woodser <13068859+woodser@users.noreply.github.com> Date: Fri, 25 Apr 2025 17:49:48 -0400 Subject: [PATCH 26/74] tolerate miner fees within 5x of each other --- .../dispute/arbitration/ArbitrationManager.java | 6 ++---- .../main/java/haveno/core/trade/HavenoUtils.java | 13 +++++++++++++ core/src/main/java/haveno/core/trade/Trade.java | 6 ++---- .../haveno/core/xmr/wallet/XmrWalletService.java | 6 ++---- 4 files changed, 19 insertions(+), 12 deletions(-) diff --git a/core/src/main/java/haveno/core/support/dispute/arbitration/ArbitrationManager.java b/core/src/main/java/haveno/core/support/dispute/arbitration/ArbitrationManager.java index 5ac7cd389a..848dbdb6f5 100644 --- a/core/src/main/java/haveno/core/support/dispute/arbitration/ArbitrationManager.java +++ b/core/src/main/java/haveno/core/support/dispute/arbitration/ArbitrationManager.java @@ -487,10 +487,8 @@ public final class ArbitrationManager extends DisputeManager XmrWalletService.MINER_FEE_TOLERANCE) throw new RuntimeException("Miner fee is not within " + (XmrWalletService.MINER_FEE_TOLERANCE * 100) + "% of estimated fee, expected " + feeEstimate + " but was " + arbitratorSignedPayoutTx.getFee()); - log.info("Payout tx fee {} is within tolerance, diff %={}", arbitratorSignedPayoutTx.getFee(), feeDiff); + HavenoUtils.verifyMinerFee(feeEstimateTx.getFee(), arbitratorSignedPayoutTx.getFee()); + log.info("Dispute payout tx fee {} is within tolerance"); } } else { disputeTxSet.setMultisigTxHex(trade.getPayoutTxHex()); diff --git a/core/src/main/java/haveno/core/trade/HavenoUtils.java b/core/src/main/java/haveno/core/trade/HavenoUtils.java index d238d78843..74caf5d7f2 100644 --- a/core/src/main/java/haveno/core/trade/HavenoUtils.java +++ b/core/src/main/java/haveno/core/trade/HavenoUtils.java @@ -96,6 +96,7 @@ public class HavenoUtils { public static final double MAKER_FEE_PCT = 0.0015; // 0.15% public static final double TAKER_FEE_PCT = 0.0075; // 0.75% public static final double MAKER_FEE_FOR_TAKER_WITHOUT_DEPOSIT_PCT = MAKER_FEE_PCT + TAKER_FEE_PCT; // customize maker's fee when no deposit or fee from taker + public static final double MINER_FEE_TOLERANCE_FACTOR = 5.0; // miner fees must be within 5x of each other // other configuration public static final long LOG_POLL_ERROR_PERIOD_MS = 1000 * 60 * 4; // log poll errors up to once every 4 minutes @@ -650,4 +651,16 @@ public class HavenoUtils { } }).start(); } + + public static void verifyMinerFee(BigInteger expected, BigInteger actual) { + BigInteger max = expected.max(actual); + BigInteger min = expected.min(actual); + if (min.compareTo(BigInteger.ZERO) <= 0) { + throw new IllegalArgumentException("Miner fees must be greater than zero"); + } + double factor = divide(max, min); + if (factor > MINER_FEE_TOLERANCE_FACTOR) { + throw new IllegalArgumentException("Miner fees are not within " + MINER_FEE_TOLERANCE_FACTOR + "x of each other. Expected=" + expected + ", actual=" + actual + ", factor=" + factor); + } + } } diff --git a/core/src/main/java/haveno/core/trade/Trade.java b/core/src/main/java/haveno/core/trade/Trade.java index b7387a9bd7..aa2330a78f 100644 --- a/core/src/main/java/haveno/core/trade/Trade.java +++ b/core/src/main/java/haveno/core/trade/Trade.java @@ -1457,10 +1457,8 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model { log.info("Creating fee estimate tx for {} {}", getClass().getSimpleName(), getId()); saveWallet(); // save wallet before creating fee estimate tx MoneroTxWallet feeEstimateTx = createPayoutTx(); - BigInteger feeEstimate = feeEstimateTx.getFee(); - double feeDiff = payoutTx.getFee().subtract(feeEstimate).abs().doubleValue() / feeEstimate.doubleValue(); // TODO: use BigDecimal? - if (feeDiff > XmrWalletService.MINER_FEE_TOLERANCE) throw new IllegalArgumentException("Miner fee is not within " + (XmrWalletService.MINER_FEE_TOLERANCE * 100) + "% of estimated fee, expected " + feeEstimate + " but was " + payoutTx.getFee()); - log.info("Payout tx fee {} is within tolerance, diff %={}", payoutTx.getFee(), feeDiff); + HavenoUtils.verifyMinerFee(feeEstimateTx.getFee(), payoutTx.getFee()); + log.info("Payout tx fee {} is within tolerance"); } // set signed payout tx hex diff --git a/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java b/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java index 838ebfc5bc..fc83118cbd 100644 --- a/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java +++ b/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java @@ -110,7 +110,6 @@ public class XmrWalletService extends XmrWalletBase { public static final String MONERO_BINS_DIR = Config.appDataDir().getAbsolutePath(); public static final String MONERO_WALLET_RPC_NAME = Utilities.isWindows() ? "monero-wallet-rpc.exe" : "monero-wallet-rpc"; public static final String MONERO_WALLET_RPC_PATH = MONERO_BINS_DIR + File.separator + MONERO_WALLET_RPC_NAME; - public static final double MINER_FEE_TOLERANCE = 0.25; // miner fee must be within percent of estimated fee public static final MoneroTxPriority PROTOCOL_FEE_PRIORITY = MoneroTxPriority.DEFAULT; public static final int MONERO_LOG_LEVEL = -1; // monero library log level, -1 to disable private static final MoneroNetworkType MONERO_NETWORK_TYPE = getMoneroNetworkType(); @@ -767,9 +766,8 @@ public class XmrWalletService extends XmrWalletBase { // verify miner fee BigInteger minerFeeEstimate = getFeeEstimate(tx.getWeight()); - double minerFeeDiff = tx.getFee().subtract(minerFeeEstimate).abs().doubleValue() / minerFeeEstimate.doubleValue(); - if (minerFeeDiff > MINER_FEE_TOLERANCE) throw new RuntimeException("Miner fee is not within " + (MINER_FEE_TOLERANCE * 100) + "% of estimated fee, expected " + minerFeeEstimate + " but was " + tx.getFee() + ", diff%=" + minerFeeDiff); - log.info("Trade miner fee {} is within tolerance, diff%={}", tx.getFee(), minerFeeDiff); + HavenoUtils.verifyMinerFee(minerFeeEstimate, tx.getFee()); + log.info("Trade miner fee {} is within tolerance"); // verify proof to fee address BigInteger actualTradeFee = BigInteger.ZERO; From 0b8e43b7a822dec42c94cb9d8aeccadac6f4c731 Mon Sep 17 00:00:00 2001 From: woodser <13068859+woodser@users.noreply.github.com> Date: Fri, 25 Apr 2025 17:54:44 -0400 Subject: [PATCH 27/74] preserve old behavior when nack does not include updated multisig --- .../core/trade/protocol/TradeProtocol.java | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/core/src/main/java/haveno/core/trade/protocol/TradeProtocol.java b/core/src/main/java/haveno/core/trade/protocol/TradeProtocol.java index 6272d40749..844d13e86a 100644 --- a/core/src/main/java/haveno/core/trade/protocol/TradeProtocol.java +++ b/core/src/main/java/haveno/core/trade/protocol/TradeProtocol.java @@ -804,15 +804,15 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D else { log.warn("We received a NACK for our PaymentReceivedMessage to the buyer for {} {}", trade.getClass().getSimpleName(), trade.getId()); - // update multisig hex + // nack includes updated multisig hex since v1.1.1 if (ackMessage.getUpdatedMultisigHex() != null) { trade.getBuyer().setUpdatedMultisigHex(ackMessage.getUpdatedMultisigHex()); - } - // reset state if not processed - if (trade.isPaymentReceived() && !trade.isPayoutPublished() && !isPaymentReceivedMessageAckedByEither()) { - log.warn("Resetting state to payment sent for {} {}", trade.getClass().getSimpleName(), trade.getId()); - trade.resetToPaymentSentState(); + // reset state if not processed + if (trade.isPaymentReceived() && !trade.isPayoutPublished() && !isPaymentReceivedMessageAckedByEither()) { + log.warn("Resetting state to payment sent for {} {}", trade.getClass().getSimpleName(), trade.getId()); + trade.resetToPaymentSentState(); + } } } processModel.getTradeManager().requestPersistence(); @@ -826,15 +826,15 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D if (!ackMessage.isSuccess()) { log.warn("We received a NACK for our PaymentReceivedMessage to the arbitrator for {} {}", trade.getClass().getSimpleName(), trade.getId()); - // update multisig hex + // nack includes updated multisig hex since v1.1.1 if (ackMessage.getUpdatedMultisigHex() != null) { trade.getArbitrator().setUpdatedMultisigHex(ackMessage.getUpdatedMultisigHex()); - } - // reset state if not processed - if (trade.isPaymentReceived() && !trade.isPayoutPublished() && !isPaymentReceivedMessageAckedByEither()) { - log.warn("Resetting state to payment sent for {} {}", trade.getClass().getSimpleName(), trade.getId()); - trade.resetToPaymentSentState(); + // reset state if not processed + if (trade.isPaymentReceived() && !trade.isPayoutPublished() && !isPaymentReceivedMessageAckedByEither()) { + log.warn("Resetting state to payment sent for {} {}", trade.getClass().getSimpleName(), trade.getId()); + trade.resetToPaymentSentState(); + } } } processModel.getTradeManager().requestPersistence(); From ae5ee15a8526e2cdaff8bd5330f9b4569874dbec Mon Sep 17 00:00:00 2001 From: woodser <13068859+woodser@users.noreply.github.com> Date: Sat, 26 Apr 2025 15:57:23 -0400 Subject: [PATCH 28/74] instruct to try again on nack --- .../src/main/java/haveno/core/trade/protocol/TradeProtocol.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/haveno/core/trade/protocol/TradeProtocol.java b/core/src/main/java/haveno/core/trade/protocol/TradeProtocol.java index 844d13e86a..5ff09324fd 100644 --- a/core/src/main/java/haveno/core/trade/protocol/TradeProtocol.java +++ b/core/src/main/java/haveno/core/trade/protocol/TradeProtocol.java @@ -849,7 +849,7 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D log.info("Received AckMessage for {}, sender={}, trade={} {}, messageUid={}", ackMessage.getSourceMsgClassName(), sender, trade.getClass().getSimpleName(), trade.getId(), ackMessage.getSourceUid()); } else { log.warn("Received AckMessage with error state for {}, sender={}, trade={} {}, messageUid={}, errorMessage={}", ackMessage.getSourceMsgClassName(), sender, trade.getClass().getSimpleName(), trade.getId(), ackMessage.getSourceUid(), ackMessage.getErrorMessage()); - handleError(ackMessage.getErrorMessage()); + handleError("Your peer had a problem processing your message. Please ensure you and your peer are running the latest version and try again.\n\nError details:\n" + ackMessage.getErrorMessage()); } // notify trade listeners From 81eaeb6df0924bbd330956ec5dc11e312a541ef6 Mon Sep 17 00:00:00 2001 From: woodser <13068859+woodser@users.noreply.github.com> Date: Sat, 26 Apr 2025 16:02:21 -0400 Subject: [PATCH 29/74] bump version to v1.1.1 --- build.gradle | 2 +- common/src/main/java/haveno/common/app/Version.java | 2 +- desktop/package/linux/exchange.haveno.Haveno.metainfo.xml | 2 +- desktop/package/macosx/Info.plist | 4 ++-- seednode/src/main/java/haveno/seednode/SeedNodeMain.java | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/build.gradle b/build.gradle index aec0e7c89f..0c8a4412f2 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.1.0-SNAPSHOT' + version = '1.1.1-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 c0dd9352ff..d7be891aea 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.1.0"; + public static final String VERSION = "1.1.1"; /** * Holds a list of the tagged resource files for optimizing the getData requests. diff --git a/desktop/package/linux/exchange.haveno.Haveno.metainfo.xml b/desktop/package/linux/exchange.haveno.Haveno.metainfo.xml index 9227c587a6..57aeb02f09 100644 --- a/desktop/package/linux/exchange.haveno.Haveno.metainfo.xml +++ b/desktop/package/linux/exchange.haveno.Haveno.metainfo.xml @@ -60,6 +60,6 @@ - + diff --git a/desktop/package/macosx/Info.plist b/desktop/package/macosx/Info.plist index fd0a765320..c6bf09f2de 100644 --- a/desktop/package/macosx/Info.plist +++ b/desktop/package/macosx/Info.plist @@ -5,10 +5,10 @@ CFBundleVersion - 1.1.0 + 1.1.1 CFBundleShortVersionString - 1.1.0 + 1.1.1 CFBundleExecutable Haveno diff --git a/seednode/src/main/java/haveno/seednode/SeedNodeMain.java b/seednode/src/main/java/haveno/seednode/SeedNodeMain.java index 1e9c4c5061..b846dff4c9 100644 --- a/seednode/src/main/java/haveno/seednode/SeedNodeMain.java +++ b/seednode/src/main/java/haveno/seednode/SeedNodeMain.java @@ -41,7 +41,7 @@ import lombok.extern.slf4j.Slf4j; @Slf4j public class SeedNodeMain extends ExecutableForAppWithP2p { private static final long CHECK_CONNECTION_LOSS_SEC = 30; - private static final String VERSION = "1.1.0"; + private static final String VERSION = "1.1.1"; private SeedNode seedNode; private Timer checkConnectionLossTime; From c214919aa590372c8e3f4ebb2f43df08414afb96 Mon Sep 17 00:00:00 2001 From: woodser <13068859+woodser@users.noreply.github.com> Date: Mon, 28 Apr 2025 06:40:17 -0400 Subject: [PATCH 30/74] fix concurrent modification in portfolio by locking sequence number map --- .../network/p2p/storage/P2PDataStorage.java | 16 ++++-- .../persistence/SequenceNumberMap.java | 56 +++++++++++++------ 2 files changed, 49 insertions(+), 23 deletions(-) diff --git a/p2p/src/main/java/haveno/network/p2p/storage/P2PDataStorage.java b/p2p/src/main/java/haveno/network/p2p/storage/P2PDataStorage.java index 40be21ef32..0517097190 100644 --- a/p2p/src/main/java/haveno/network/p2p/storage/P2PDataStorage.java +++ b/p2p/src/main/java/haveno/network/p2p/storage/P2PDataStorage.java @@ -187,7 +187,9 @@ public class P2PDataStorage implements MessageListener, ConnectionListener, Pers @Override public void readPersisted(Runnable completeHandler) { persistenceManager.readPersisted(persisted -> { - sequenceNumberMap.setMap(getPurgedSequenceNumberMap(persisted.getMap())); + synchronized (persisted.getMap()) { + sequenceNumberMap.setMap(getPurgedSequenceNumberMap(persisted.getMap())); + } completeHandler.run(); }, completeHandler); @@ -198,7 +200,9 @@ public class P2PDataStorage implements MessageListener, ConnectionListener, Pers public void readPersistedSync() { SequenceNumberMap persisted = persistenceManager.getPersisted(); if (persisted != null) { - sequenceNumberMap.setMap(getPurgedSequenceNumberMap(persisted.getMap())); + synchronized (persisted.getMap()) { + sequenceNumberMap.setMap(getPurgedSequenceNumberMap(persisted.getMap())); + } } } @@ -641,9 +645,11 @@ public class P2PDataStorage implements MessageListener, ConnectionListener, Pers } removeFromMapAndDataStore(toRemoveList); - if (sequenceNumberMap.size() > this.maxSequenceNumberMapSizeBeforePurge) { - sequenceNumberMap.setMap(getPurgedSequenceNumberMap(sequenceNumberMap.getMap())); - requestPersistence(); + synchronized (sequenceNumberMap.getMap()) { + if (sequenceNumberMap.size() > this.maxSequenceNumberMapSizeBeforePurge) { + sequenceNumberMap.setMap(getPurgedSequenceNumberMap(sequenceNumberMap.getMap())); + requestPersistence(); + } } } } diff --git a/p2p/src/main/java/haveno/network/p2p/storage/persistence/SequenceNumberMap.java b/p2p/src/main/java/haveno/network/p2p/storage/persistence/SequenceNumberMap.java index f774c4c3c4..43cf2c1e1e 100644 --- a/p2p/src/main/java/haveno/network/p2p/storage/persistence/SequenceNumberMap.java +++ b/p2p/src/main/java/haveno/network/p2p/storage/persistence/SequenceNumberMap.java @@ -19,8 +19,6 @@ package haveno.network.p2p.storage.persistence; import haveno.common.proto.persistable.PersistableEnvelope; import haveno.network.p2p.storage.P2PDataStorage; -import lombok.Getter; -import lombok.Setter; import java.util.HashMap; import java.util.Map; @@ -33,8 +31,6 @@ import java.util.stream.Collectors; * Hence this Persistable class. */ public class SequenceNumberMap implements PersistableEnvelope { - @Getter - @Setter private Map map = new ConcurrentHashMap<>(); public SequenceNumberMap() { @@ -46,20 +42,24 @@ public class SequenceNumberMap implements PersistableEnvelope { /////////////////////////////////////////////////////////////////////////////////////////// private SequenceNumberMap(Map map) { - this.map.putAll(map); + synchronized (this.map) { + this.map.putAll(map); + } } @Override public protobuf.PersistableEnvelope toProtoMessage() { - return protobuf.PersistableEnvelope.newBuilder() - .setSequenceNumberMap(protobuf.SequenceNumberMap.newBuilder() - .addAllSequenceNumberEntries(map.entrySet().stream() - .map(entry -> protobuf.SequenceNumberEntry.newBuilder() - .setBytes(entry.getKey().toProtoMessage()) - .setMapValue(entry.getValue().toProtoMessage()) - .build()) - .collect(Collectors.toList()))) - .build(); + synchronized (map) { + return protobuf.PersistableEnvelope.newBuilder() + .setSequenceNumberMap(protobuf.SequenceNumberMap.newBuilder() + .addAllSequenceNumberEntries(map.entrySet().stream() + .map(entry -> protobuf.SequenceNumberEntry.newBuilder() + .setBytes(entry.getKey().toProtoMessage()) + .setMapValue(entry.getValue().toProtoMessage()) + .build()) + .collect(Collectors.toList()))) + .build(); + } } public static SequenceNumberMap fromProto(protobuf.SequenceNumberMap proto) { @@ -74,20 +74,40 @@ public class SequenceNumberMap implements PersistableEnvelope { // API /////////////////////////////////////////////////////////////////////////////////////////// + public Map getMap() { + synchronized (map) { + return map; + } + } + + public void setMap(Map map) { + synchronized (this.map) { + this.map = map; + } + } + // Delegates public int size() { - return map.size(); + synchronized (map) { + return map.size(); + } } public boolean containsKey(P2PDataStorage.ByteArray key) { - return map.containsKey(key); + synchronized (map) { + return map.containsKey(key); + } } public P2PDataStorage.MapValue get(P2PDataStorage.ByteArray key) { - return map.get(key); + synchronized (map) { + return map.get(key); + } } public void put(P2PDataStorage.ByteArray key, P2PDataStorage.MapValue value) { - map.put(key, value); + synchronized (map) { + map.put(key, value); + } } } From c2fbd4b16ff2c694f504401d33d3e2105dbd5d5e Mon Sep 17 00:00:00 2001 From: woodser <13068859+woodser@users.noreply.github.com> Date: Wed, 30 Apr 2025 12:05:26 -0400 Subject: [PATCH 31/74] update donation address --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4e90b06ce3..5b2e8015eb 100644 --- a/README.md +++ b/README.md @@ -79,7 +79,7 @@ To bring Haveno to life, we need resources. If you have the possibility, please

Donate Monero
- 42sjokkT9FmiWPqVzrWPFE5NCJXwt96bkBozHf4vgLR9hXyJDqKHEHKVscAARuD7in5wV1meEcSTJTanCTDzidTe2cFXS1F + 47fo8N5m2VVW4uojadGQVJ34LFR9yXwDrZDRugjvVSjcTWV2WFSoc1XfNpHmxwmVtfNY9wMBch6259G6BXXFmhU49YG1zfB

If you are using a wallet that supports OpenAlias (like the 'official' CLI and GUI wallets), you can simply put `fund@haveno.exchange` as the "receiver" address. From e3946f3abaeab79a7db9bfc0024f647dd5cdf71c Mon Sep 17 00:00:00 2001 From: woodser <13068859+woodser@users.noreply.github.com> Date: Thu, 1 May 2025 08:29:45 -0400 Subject: [PATCH 32/74] move settings tab to last --- .../src/main/java/haveno/desktop/main/MainView.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/desktop/src/main/java/haveno/desktop/main/MainView.java b/desktop/src/main/java/haveno/desktop/main/MainView.java index eaec5d1154..7856a2021f 100644 --- a/desktop/src/main/java/haveno/desktop/main/MainView.java +++ b/desktop/src/main/java/haveno/desktop/main/MainView.java @@ -172,8 +172,8 @@ public class MainView extends InitializableView { ToggleButton fundsButton = new NavButton(FundsView.class, Res.get("mainView.menu.funds").toUpperCase()); ToggleButton supportButton = new NavButton(SupportView.class, Res.get("mainView.menu.support")); - ToggleButton settingsButton = new NavButton(SettingsView.class, Res.get("mainView.menu.settings")); ToggleButton accountButton = new NavButton(AccountView.class, Res.get("mainView.menu.account")); + ToggleButton settingsButton = new NavButton(SettingsView.class, Res.get("mainView.menu.settings")); JFXBadge portfolioButtonWithBadge = new JFXBadge(portfolioButton); JFXBadge supportButtonWithBadge = new JFXBadge(supportButton); @@ -199,10 +199,10 @@ public class MainView extends InitializableView { fundsButton.fire(); } else if (Utilities.isAltOrCtrlPressed(KeyCode.DIGIT6, keyEvent)) { supportButton.fire(); - } else if (Utilities.isAltOrCtrlPressed(KeyCode.DIGIT7, keyEvent)) { - settingsButton.fire(); } else if (Utilities.isAltOrCtrlPressed(KeyCode.DIGIT8, keyEvent)) { accountButton.fire(); + } else if (Utilities.isAltOrCtrlPressed(KeyCode.DIGIT7, keyEvent)) { + settingsButton.fire(); } }); } @@ -305,8 +305,8 @@ public class MainView extends InitializableView { primaryNav.getStyleClass().add("nav-primary"); HBox.setHgrow(primaryNav, Priority.SOMETIMES); - HBox secondaryNav = new HBox(supportButtonWithBadge, getNavigationSpacer(), settingsButtonWithBadge, - getNavigationSpacer(), accountButton, getNavigationSpacer()); + HBox secondaryNav = new HBox(supportButtonWithBadge, getNavigationSpacer(), accountButton, + getNavigationSpacer(), settingsButtonWithBadge, getNavigationSpacer()); secondaryNav.getStyleClass().add("nav-secondary"); HBox.setHgrow(secondaryNav, Priority.SOMETIMES); From c4758d1e4b5488eb04f6a046204a697eb94beb9e Mon Sep 17 00:00:00 2001 From: woodser <13068859+woodser@users.noreply.github.com> Date: Tue, 6 May 2025 07:49:23 -0400 Subject: [PATCH 33/74] use default 'password' to authenticate with wallet rpc server --- core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java b/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java index fc83118cbd..f2d16943d1 100644 --- a/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java +++ b/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java @@ -1785,7 +1785,7 @@ public class XmrWalletService extends XmrWalletBase { List cmd = new ArrayList<>(Arrays.asList( // modifiable list MONERO_WALLET_RPC_PATH, "--rpc-login", - MONERO_WALLET_RPC_USERNAME + ":" + getWalletPassword(), + MONERO_WALLET_RPC_USERNAME + ":" + MONERO_WALLET_RPC_DEFAULT_PASSWORD, "--wallet-dir", walletDir.toString())); // omit --mainnet flag since it does not exist From fe3283f3b0a7299bc3c05aafacbb494d899a3333 Mon Sep 17 00:00:00 2001 From: woodser <13068859+woodser@users.noreply.github.com> Date: Thu, 8 May 2025 07:08:51 -0400 Subject: [PATCH 34/74] update donation qr and readme --- README.md | 8 +++----- media/donate_monero.png | Bin 28501 -> 34213 bytes 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 5b2e8015eb..c8078a97aa 100644 --- a/README.md +++ b/README.md @@ -73,13 +73,11 @@ If you are not able to contribute code and want to contribute development resour To incentivize development and reward contributors, we adopt a simple bounty system. Contributors may be awarded bounties after completing a task (resolving an issue). Take a look at the [issues labeled '💰bounty'](https://github.com/haveno-dex/haveno/issues?q=is%3Aopen+is%3Aissue+label%3A%F0%9F%92%B0bounty) in the main `haveno` repository. [Details and conditions for receiving a bounty](docs/bounties.md). -## Support and sponsorships +## Support -To bring Haveno to life, we need resources. If you have the possibility, please consider [becoming a sponsor](https://haveno.exchange/sponsors/) or donating to the project: +To bring Haveno to life, we need resources. If you have the possibility, please consider donating to the project: -

+

Donate Monero
47fo8N5m2VVW4uojadGQVJ34LFR9yXwDrZDRugjvVSjcTWV2WFSoc1XfNpHmxwmVtfNY9wMBch6259G6BXXFmhU49YG1zfB

- -If you are using a wallet that supports OpenAlias (like the 'official' CLI and GUI wallets), you can simply put `fund@haveno.exchange` as the "receiver" address. diff --git a/media/donate_monero.png b/media/donate_monero.png index 35b3e21d8fccf03ebb74c756a16e53b710aa50b0..83fe64fed62a54a9742320337a8bf57a4c51d1eb 100644 GIT binary patch literal 34213 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
    84&piW`kULl;qLP> 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Ãv@jbRQJ0 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 Date: Mon, 12 May 2025 21:27:39 +0000 Subject: [PATCH 35/74] Update Qubes/Whonix install for individual files (#1739) --- scripts/install_whonix_qubes/INSTALL.md | 29 +++++++------------ scripts/install_whonix_qubes/README.md | 6 ++-- .../1-TemplateVM/1.0-haveno-templatevm.sh | 16 +++++----- .../scripts/3-AppVM/3.0-haveno-appvm.sh | 4 +-- 4 files changed, 23 insertions(+), 32 deletions(-) diff --git a/scripts/install_whonix_qubes/INSTALL.md b/scripts/install_whonix_qubes/INSTALL.md index c56b35cacd..adee6d2e60 100644 --- a/scripts/install_whonix_qubes/INSTALL.md +++ b/scripts/install_whonix_qubes/INSTALL.md @@ -147,13 +147,13 @@ $ printf 'haveno-Haveno.desktop' | qvm-appmenus --set-whitelist – haveno ##### In `haveno-template` TemplateVM: ```shell -% sudo bash QubesIncoming/dispXXXX/1.0-haveno-templatevm.sh "" "" +% sudo bash QubesIncoming/dispXXXX/1.0-haveno-templatevm.sh "" "" ```

    Example:

    ```shell -% sudo bash QubesIncoming/dispXXXX/1.0-haveno-templatevm.sh "https://github.com/havenoexample/haveno-example/releases/download/v1.0.18/haveno-linux-deb.zip" "ABAF11C65A2970B130ABE3C479BE3E4300411886" +% sudo bash QubesIncoming/dispXXXX/1.0-haveno-templatevm.sh "https://github.com/havenoexample/haveno-example/releases/download/1.1.1/haveno-v1.1.1-linux-x86_64-installer.deb" "ABAF11C65A2970B130ABE3C479BE3E4300411886" ``` #### *TemplateVM Using Precompiled Package From `git` Repository (CLI)* @@ -195,10 +195,9 @@ $ printf 'haveno-Haveno.desktop' | qvm-appmenus --set-whitelist – haveno ```shell # export https_proxy=http://127.0.0.1:8082 -# curl -sSLo /tmp/hashes.txt https://github.com/havenoexample/haveno-example/releases/download/v1.0.18/1.0.18-hashes.txt -# curl -sSLo /tmp/hashes.txt.sig https://github.com/havenoexample/haveno-example/releases/download/v1.0.18/1.0.18-hashes.txt.sig -# curl -sSLo /tmp/haveno.zip https://github.com/havenoexample/haveno-example/releases/download/v1.0.18/haveno_amd64_deb-latest.zip -# curl -sSLo /tmp/haveno.zip.sig https://github.com/havenoexample/haveno-example/releases/download/v1.0.18/haveno_amd64_deb-latest.zip.sig +# curl -sSLo /tmp/haveno.deb https://github.com/havenoexample/haveno-example/releases/download/1.1.1/haveno-v1.1.1-linux-x86_64-installer.deb +# curl -sSLo /tmp/haveno.deb.sig https://github.com/havenoexample/haveno-example/releases/download/1.1.1/haveno-v1.1.1-linux-x86_64-installer.deb.sig +# curl -sSLo /tmp/haveno-jar.SHA-256 https://github.com/havenoexample/haveno-example/releases/download/1.1.1/haveno-v1.1.1-linux-x86_64-SNAPSHOT-all.jar.SHA-256 ```

    Note:

    @@ -207,28 +206,22 @@ $ printf 'haveno-Haveno.desktop' | qvm-appmenus --set-whitelist – haveno

    For Whonix On Anything Other Than Qubes OS:

    ```shell -# curl -sSLo /tmp/hashes.txt https://github.com/havenoexample/haveno-example/releases/download/v1.0.18/1.0.18-hashes.txt -# curl -sSLo /tmp/hashes.txt.sig https://github.com/havenoexample/haveno-example/releases/download/v1.0.18/1.0.18-hashes.txt.sig -# curl -sSLo /tmp/haveno.zip https://github.com/havenoexample/haveno-example/releases/download/v1.0.18/haveno_amd64_deb-latest.zip -# curl -sSLo /tmp/haveno.zip.sig https://github.com/havenoexample/haveno-example/releases/download/v1.0.18/haveno_amd64_deb-latest.zip.sig +# curl -sSLo /tmp/haveno.deb https://github.com/havenoexample/haveno-example/releases/download/1.1.1/haveno-v1.1.1-linux-x86_64-installer.deb +# curl -sSLo /tmp/haveno.deb.sig https://github.com/havenoexample/haveno-example/releases/download/1.1.1/haveno-v1.1.1-linux-x86_64-installer.deb.sig +# curl -sSLo /tmp/haveno-jar.SHA-256 https://github.com/havenoexample/haveno-example/releases/download/1.1.1/haveno-v1.1.1-linux-x86_64-SNAPSHOT-all.jar.SHA-256 ```

    Note:

    Above are dummy URLS which MUST be replaced with actual working URLs

    -###### Verify Release Files +###### Verify & Install Package File ```shell -# if gpg --digest-algo SHA256 --verify /tmp/hashes.txt.sig >/dev/null 2>&1; then printf $'SHASUM file has a VALID signature!\n'; else printf $'SHASUMS failed signature check\n' && sleep 5 && exit 1; fi -``` - -###### Verify Hash, Unpack & Install Package -```shell -# if [[ $(cat /tmp/hashes.txt) =~ $(sha512sum /tmp/haveno*.zip | awk '{ print $1 }') ]] ; then printf $'SHA Hash IS valid!\n' && mkdir -p /usr/share/desktop-directories && cd /tmp && unzip /tmp/haveno*.zip && apt install -y /tmp/haveno*.deb; else printf $'WARNING: Bad Hash!\n' && exit; fi +# if gpg --digest-algo SHA256 --verify /tmp/haveno.deb.sig >/dev/null 2>&1; then printf $'PACKAGE file has a VALID signature!\n' && mkdir -p /usr/share/desktop-directories && apt install -y /tmp/haveno*.deb; else printf $'PACKAGE failed signature check\n' && sleep 5 && exit 1; fi ``` ###### Verify Jar ```shell -# if [[ $(cat /tmp/desktop*.SHA-256) =~ $(sha256sum /opt/haveno/lib/app/desktop*.jar | awk '{ print $1 }') ]] ; then printf $'SHA Hash IS valid!\n' && printf 'Happy trading!\n'; else printf $'WARNING: Bad Hash!\n' && exit; fi +# if [[ $(cat /tmp/haveno-jar.SHA-256) =~ $(sha256sum /opt/haveno/lib/app/desktop*.jar | awk '{ print $1 }') ]] ; then printf $'SHA Hash IS valid!\n' && printf 'Happy trading!\n'; else printf $'WARNING: Bad Hash!\n' && exit; fi ``` #### *TemplateVM Building From Source via `git` Repository (Scripted)* diff --git a/scripts/install_whonix_qubes/README.md b/scripts/install_whonix_qubes/README.md index 72670e41ca..21b6beb468 100644 --- a/scripts/install_whonix_qubes/README.md +++ b/scripts/install_whonix_qubes/README.md @@ -25,17 +25,17 @@ $ bash 0.0-dom0.sh && bash 0.1-dom0.sh && bash 0.2-dom0.sh ## **Build TemplateVM** -### *Via Binary Archive* +### *Via Package* #### **In `haveno-template` `TemplateVM`:** ```shell -% sudo bash QubesIncoming/dispXXXX/1.0-haveno-templatevm.sh "" "" +% sudo bash QubesIncoming/dispXXXX/1.0-haveno-templatevm.sh "" "" ```

    Example:

    ```shell -% sudo bash 1.0-haveno-templatevm.sh "https://github.com/havenoexample/haveno-example/releases/download/v1.0.18/haveno-linux-deb.zip" "ABAF11C65A2970B130ABE3C479BE3E4300411886" +% sudo bash 1.0-haveno-templatevm.sh "https://github.com/havenoexample/haveno-example/releases/download/1.1.1/haveno-v1.1.1-linux-x86_64-installer.deb" "ABAF11C65A2970B130ABE3C479BE3E4300411886" ``` ### *Via Source* diff --git a/scripts/install_whonix_qubes/scripts/1-TemplateVM/1.0-haveno-templatevm.sh b/scripts/install_whonix_qubes/scripts/1-TemplateVM/1.0-haveno-templatevm.sh index f1ab43ae1b..2dff819eaa 100644 --- a/scripts/install_whonix_qubes/scripts/1-TemplateVM/1.0-haveno-templatevm.sh +++ b/scripts/install_whonix_qubes/scripts/1-TemplateVM/1.0-haveno-templatevm.sh @@ -3,8 +3,8 @@ function remote { - if [[ -z $PRECOMPILED_URL || -z $FINGERPRINT ]]; then - printf "\nNo arguments provided!\n\nThis script requires two arguments to be provided:\nBinary URL & PGP Fingerprint\n\nPlease review documentation and try again.\n\nExiting now ...\n" + if [[ -z $PACKAGE_URL || -z $FINGERPRINT ]]; then + printf "\nNo arguments provided!\n\nThis script requires two arguments to be provided:\nPackage URL & PGP Fingerprint\n\nPlease review documentation and try again.\n\nExiting now ...\n" exit 1 fi ## Update & Upgrade @@ -32,12 +32,11 @@ function remote { ## Define URL & PGP Fingerprint etc. vars: - user_url=$PRECOMPILED_URL + user_url=$PACKAGE_URL base_url=$(printf ${user_url} | awk -F'/' -v OFS='/' '{$NF=""}1') expected_fingerprint=$FINGERPRINT - binary_filename=$(awk -F'/' '{ print $NF }' <<< "$user_url") - package_filename="haveno.deb" - signature_filename="${binary_filename}.sig" + package_filename=$(awk -F'/' '{ print $NF }' <<< "$user_url") + signature_filename="${package_filename}.sig" key_filename="$(printf "$expected_fingerprint" | tr -d ' ' | sed -E 's/.*(................)/\1/' )".asc wget_flags="--tries=10 --timeout=10 --waitretry=5 --retry-connrefused --show-progress" @@ -46,7 +45,6 @@ function remote { printf "\nUser URL=$user_url\n" printf "\nBase URL=$base_url\n" printf "\nFingerprint=$expected_fingerprint\n" - printf "\nBinary Name=$binary_filename\n" printf "\nPackage Name=$package_filename\n" printf "\nSig Filename=$signature_filename\n" printf "\nKey Filename=$key_filename\n" @@ -94,7 +92,7 @@ function remote { ## Verify the downloaded binary with the signature: echo_blue "Verifying the signature of the downloaded file ..." if gpg --digest-algo SHA256 --verify "${signature_filename}" >/dev/null 2>&1; then - 7z x "${binary_filename}" && mv haveno*.deb "${package_filename}"; + mkdir -p /usr/share/desktop-directories; else echo_red "Verification failed!" && sleep 5 exit 1; fi @@ -172,7 +170,7 @@ if ! [[ $# -eq 2 || $# -eq 3 ]] ; then fi if [[ $# -eq 2 ]] ; then - PRECOMPILED_URL=$1 + PACKAGE_URL=$1 FINGERPRINT=$2 remote fi diff --git a/scripts/install_whonix_qubes/scripts/3-AppVM/3.0-haveno-appvm.sh b/scripts/install_whonix_qubes/scripts/3-AppVM/3.0-haveno-appvm.sh index 11582a8314..52b4950ef6 100644 --- a/scripts/install_whonix_qubes/scripts/3-AppVM/3.0-haveno-appvm.sh +++ b/scripts/install_whonix_qubes/scripts/3-AppVM/3.0-haveno-appvm.sh @@ -1,5 +1,5 @@ #!/bin/zsh -## ./haveno-on-qubes/scripts/3.0-haveno-appvm_taker.sh +## ./haveno-on-qubes/scripts/3.0-haveno-appvm_taker.sh ## Function to print messages in blue: echo_blue() { @@ -42,7 +42,7 @@ whonix_firewall ### Create Desktop Launcher: echo_blue "Creating desktop launcher ..." mkdir -p /home/$(ls /home)/\.local/share/applications -sed 's|/opt/haveno/bin/Haveno|/opt/haveno/bin/Haveno --torControlPort=9051 --torControlUseSafeCookieAuth --torControlCookieFile=/var/run/tor/control.authcookie --socks5ProxyXmrAddress=127.0.0.1:9050 --useTorForXmr=on|g' /opt/haveno/lib/haveno-Haveno.desktop > /home/$(ls /home)/.local/share/applications/haveno-Haveno.desktop +sed 's|/opt/haveno/bin/Haveno|/opt/haveno/bin/Haveno --torControlPort=9051 --socks5ProxyXmrAddress=127.0.0.1:9050 --useTorForXmr=on|g' /opt/haveno/lib/haveno-Haveno.desktop > /home/$(ls /home)/.local/share/applications/haveno-Haveno.desktop chown -R $(ls /home):$(ls /home) /home/$(ls /home)/.local/share/applications/haveno-Haveno.desktop From 4c30e4625bfb7fc7c5a8939d3a57b05e81fa2ba9 Mon Sep 17 00:00:00 2001 From: woodser Date: Sun, 25 May 2025 08:57:32 -0400 Subject: [PATCH 36/74] improve error message when offer's arbitrator is not registered --- .../haveno/core/offer/OfferFilterService.java | 38 ++++++++++--------- .../haveno/core/offer/OpenOfferManager.java | 2 +- .../resources/i18n/displayStrings.properties | 2 +- .../i18n/displayStrings_cs.properties | 2 +- .../i18n/displayStrings_tr.properties | 2 +- 5 files changed, 25 insertions(+), 21 deletions(-) diff --git a/core/src/main/java/haveno/core/offer/OfferFilterService.java b/core/src/main/java/haveno/core/offer/OfferFilterService.java index e64a1ee6eb..298fb51bcd 100644 --- a/core/src/main/java/haveno/core/offer/OfferFilterService.java +++ b/core/src/main/java/haveno/core/offer/OfferFilterService.java @@ -127,6 +127,9 @@ public class OfferFilterService { if (isMyInsufficientTradeLimit(offer)) { return Result.IS_MY_INSUFFICIENT_TRADE_LIMIT; } + if (!hasValidArbitrator(offer)) { + return Result.ARBITRATOR_NOT_VALIDATED; + } if (!hasValidSignature(offer)) { return Result.SIGNATURE_NOT_VALIDATED; } @@ -215,27 +218,28 @@ public class OfferFilterService { return result; } - private boolean hasValidSignature(Offer offer) { + private boolean hasValidArbitrator(Offer offer) { + Arbitrator arbitrator = getArbitrator(offer); + return arbitrator != null; + } - // get accepted arbitrator by address + private Arbitrator getArbitrator(Offer offer) { + + // get arbitrator by address Arbitrator arbitrator = user.getAcceptedArbitratorByAddress(offer.getOfferPayload().getArbitratorSigner()); + if (arbitrator != null) return arbitrator; - // accepted arbitrator is null if we are the signing arbitrator - if (arbitrator == null && offer.getOfferPayload().getArbitratorSigner() != null) { - Arbitrator thisArbitrator = user.getRegisteredArbitrator(); - if (thisArbitrator != null && thisArbitrator.getNodeAddress().equals(offer.getOfferPayload().getArbitratorSigner())) { - if (thisArbitrator.getNodeAddress().equals(p2PService.getNetworkNode().getNodeAddress())) arbitrator = thisArbitrator; // TODO: unnecessary to compare arbitrator and p2pservice address? - } else { - - // // otherwise log warning that arbitrator is unregistered - // List arbitratorAddresses = user.getAcceptedArbitrators().stream().map(Arbitrator::getNodeAddress).collect(Collectors.toList()); - // if (!arbitratorAddresses.isEmpty()) { - // log.warn("No arbitrator is registered with offer's signer. offerId={}, arbitrator signer={}, accepted arbitrators={}", offer.getId(), offer.getOfferPayload().getArbitratorSigner(), arbitratorAddresses); - // } - } - } + // check if we are the signing arbitrator + Arbitrator thisArbitrator = user.getRegisteredArbitrator(); + if (thisArbitrator != null && thisArbitrator.getNodeAddress().equals(offer.getOfferPayload().getArbitratorSigner())) return thisArbitrator; - if (arbitrator == null) return false; // invalid arbitrator + // cannot get arbitrator + return null; + } + + private boolean hasValidSignature(Offer offer) { + Arbitrator arbitrator = getArbitrator(offer); + if (arbitrator == null) return false; return HavenoUtils.isArbitratorSignatureValid(offer.getOfferPayload(), arbitrator); } diff --git a/core/src/main/java/haveno/core/offer/OpenOfferManager.java b/core/src/main/java/haveno/core/offer/OpenOfferManager.java index a928494c1d..710cb92d54 100644 --- a/core/src/main/java/haveno/core/offer/OpenOfferManager.java +++ b/core/src/main/java/haveno/core/offer/OpenOfferManager.java @@ -1189,7 +1189,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe } else if (openOffer.getOffer().getOfferPayload().getArbitratorSignature() == null) { throw new IllegalArgumentException("Offer " + openOffer.getId() + " has no arbitrator signature"); } else if (arbitrator == null) { - throw new IllegalArgumentException("Offer " + openOffer.getId() + " signed by unavailable arbitrator"); + throw new IllegalArgumentException("Offer " + openOffer.getId() + " signed by unregistered arbitrator"); } else if (!HavenoUtils.isArbitratorSignatureValid(openOffer.getOffer().getOfferPayload(), arbitrator)) { throw new IllegalArgumentException("Offer " + openOffer.getId() + " has invalid arbitrator signature"); } else if (openOffer.getOffer().getOfferPayload().getReserveTxKeyImages() == null || openOffer.getOffer().getOfferPayload().getReserveTxKeyImages().isEmpty() || openOffer.getReserveTxHash() == null || openOffer.getReserveTxHash().isEmpty()) { diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index 9d500aefe5..8133a7b0f0 100644 --- a/core/src/main/resources/i18n/displayStrings.properties +++ b/core/src/main/resources/i18n/displayStrings.properties @@ -458,7 +458,7 @@ offerbook.warning.requireUpdateToNewVersion=Your version of Haveno is not compat offerbook.warning.offerWasAlreadyUsedInTrade=You cannot take this offer because you already took it earlier. \ It could be that your previous take-offer attempt resulted in a failed trade. -offerbook.warning.arbitratorNotValidated=This offer cannot be taken because the arbitrator is invalid. +offerbook.warning.arbitratorNotValidated=This offer cannot be taken because the arbitrator is not registered. offerbook.warning.signatureNotValidated=This offer cannot be taken because the arbitrator's signature is invalid. offerbook.warning.reserveFundsSpent=This offer cannot be taken because the reserved funds were already spent. diff --git a/core/src/main/resources/i18n/displayStrings_cs.properties b/core/src/main/resources/i18n/displayStrings_cs.properties index 16b6a05bba..8ca69463ca 100644 --- a/core/src/main/resources/i18n/displayStrings_cs.properties +++ b/core/src/main/resources/i18n/displayStrings_cs.properties @@ -442,7 +442,7 @@ offerbook.warning.requireUpdateToNewVersion=Vaše verze Haveno již není kompat offerbook.warning.offerWasAlreadyUsedInTrade=Tuto nabídku nemůžete přijmout, protože jste ji již dříve využili. \ Je možné, že váš předchozí pokus o přijetí nabídky vyústil v neúspěšný obchod. -offerbook.warning.arbitratorNotValidated=Tuto nabídku nelze přijmout, protože rozhodce je neplatný +offerbook.warning.arbitratorNotValidated=Tuto nabídku nelze přijmout, protože arbitr není registrován. offerbook.warning.signatureNotValidated=Tuto nabídku nelze přijmout, protože rozhodce má neplatný podpis offerbook.info.sellAtMarketPrice=Budete prodávat za tržní cenu (aktualizováno každou minutu). diff --git a/core/src/main/resources/i18n/displayStrings_tr.properties b/core/src/main/resources/i18n/displayStrings_tr.properties index 28034c72a3..d796e21e20 100644 --- a/core/src/main/resources/i18n/displayStrings_tr.properties +++ b/core/src/main/resources/i18n/displayStrings_tr.properties @@ -439,7 +439,7 @@ offerbook.warning.requireUpdateToNewVersion=Sizin Haveno sürümünüz artık ti offerbook.warning.offerWasAlreadyUsedInTrade=Bu teklifi alamazsınız çünkü daha önce aldınız. \ Önceki teklif alma girişiminiz başarısız bir ticaretle sonuçlanmış olabilir. -offerbook.warning.arbitratorNotValidated=Bu teklif, hakem geçersiz olduğu için alınamaz +offerbook.warning.arbitratorNotValidated=Bu teklif kabul edilemez çünkü hakem kayıtlı değil. offerbook.warning.signatureNotValidated=Bu teklif, hakemin imzası geçersiz olduğu için alınamaz offerbook.info.sellAtMarketPrice=Piyasa fiyatından satış yapacaksınız (her dakika güncellenir). From 115fa96daf8baf2217b7b216b0b18b78391207a1 Mon Sep 17 00:00:00 2001 From: PromptPunksFauxCough <200402670+PromptPunksFauxCough@users.noreply.github.com> Date: Wed, 28 May 2025 10:25:45 +0000 Subject: [PATCH 37/74] Update exec.sh to leverage Tails Stream-Isolation (#1746) https://tails.net/contribute/design/stream_isolation --- scripts/install_tails/assets/exec.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/install_tails/assets/exec.sh b/scripts/install_tails/assets/exec.sh index ad0610a60b..55d821f386 100644 --- a/scripts/install_tails/assets/exec.sh +++ b/scripts/install_tails/assets/exec.sh @@ -59,4 +59,4 @@ fi echo_blue "Starting Haveno..." -/opt/haveno/bin/Haveno --torControlPort 951 --torControlCookieFile=/var/run/tor/control.authcookie --torControlUseSafeCookieAuth --userDataDir=${data_dir} --useTorForXmr=on --socks5ProxyXmrAddress=127.0.0.1:9050 +/opt/haveno/bin/Haveno --torControlPort 951 --torControlCookieFile=/var/run/tor/control.authcookie --torControlUseSafeCookieAuth --userDataDir=${data_dir} --useTorForXmr=on --socks5ProxyXmrAddress=127.0.0.1:9062 From ddab1702104493655c25758282757e10cb3d76f6 Mon Sep 17 00:00:00 2001 From: woodser Date: Wed, 28 May 2025 08:56:34 -0400 Subject: [PATCH 38/74] remove currency codes from crypto names --- .../main/java/haveno/asset/tokens/TetherUSDERC20.java | 2 +- .../main/java/haveno/asset/tokens/TetherUSDTRC20.java | 2 +- .../main/java/haveno/asset/tokens/USDCoinERC20.java | 2 +- .../main/java/haveno/core/locale/CryptoCurrency.java | 2 +- .../src/main/java/haveno/core/locale/CurrencyUtil.java | 10 +++++----- .../java/haveno/core/locale/TraditionalCurrency.java | 2 +- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/assets/src/main/java/haveno/asset/tokens/TetherUSDERC20.java b/assets/src/main/java/haveno/asset/tokens/TetherUSDERC20.java index 1afb7ff1f2..ffbcac2cd3 100644 --- a/assets/src/main/java/haveno/asset/tokens/TetherUSDERC20.java +++ b/assets/src/main/java/haveno/asset/tokens/TetherUSDERC20.java @@ -6,6 +6,6 @@ public class TetherUSDERC20 extends Erc20Token { public TetherUSDERC20() { // If you add a new USDT variant or want to change this ticker symbol you should also look here: // core/src/main/java/haveno/core/provider/price/PriceProvider.java:getAll() - super("Tether USD (ERC20)", "USDT-ERC20"); + super("Tether USD", "USDT-ERC20"); } } diff --git a/assets/src/main/java/haveno/asset/tokens/TetherUSDTRC20.java b/assets/src/main/java/haveno/asset/tokens/TetherUSDTRC20.java index c5669d126a..c12bb37442 100644 --- a/assets/src/main/java/haveno/asset/tokens/TetherUSDTRC20.java +++ b/assets/src/main/java/haveno/asset/tokens/TetherUSDTRC20.java @@ -6,6 +6,6 @@ public class TetherUSDTRC20 extends Trc20Token { public TetherUSDTRC20() { // If you add a new USDT variant or want to change this ticker symbol you should also look here: // core/src/main/java/haveno/core/provider/price/PriceProvider.java:getAll() - super("Tether USD (TRC20)", "USDT-TRC20"); + super("Tether USD", "USDT-TRC20"); } } diff --git a/assets/src/main/java/haveno/asset/tokens/USDCoinERC20.java b/assets/src/main/java/haveno/asset/tokens/USDCoinERC20.java index a65c021df9..cb371bd221 100644 --- a/assets/src/main/java/haveno/asset/tokens/USDCoinERC20.java +++ b/assets/src/main/java/haveno/asset/tokens/USDCoinERC20.java @@ -22,6 +22,6 @@ import haveno.asset.Erc20Token; public class USDCoinERC20 extends Erc20Token { public USDCoinERC20() { - super("USD Coin (ERC20)", "USDC-ERC20"); + super("USD Coin", "USDC-ERC20"); } } diff --git a/core/src/main/java/haveno/core/locale/CryptoCurrency.java b/core/src/main/java/haveno/core/locale/CryptoCurrency.java index 6c46c9d2b3..feabaf1943 100644 --- a/core/src/main/java/haveno/core/locale/CryptoCurrency.java +++ b/core/src/main/java/haveno/core/locale/CryptoCurrency.java @@ -54,7 +54,7 @@ public final class CryptoCurrency extends TradeCurrency { public static CryptoCurrency fromProto(protobuf.TradeCurrency proto) { return new CryptoCurrency(proto.getCode(), - proto.getName(), + CurrencyUtil.getNameByCode(proto.getCode()), proto.getCryptoCurrency().getIsAsset()); } diff --git a/core/src/main/java/haveno/core/locale/CurrencyUtil.java b/core/src/main/java/haveno/core/locale/CurrencyUtil.java index 6ed42b6234..911bb65d96 100644 --- a/core/src/main/java/haveno/core/locale/CurrencyUtil.java +++ b/core/src/main/java/haveno/core/locale/CurrencyUtil.java @@ -66,7 +66,7 @@ import static java.lang.String.format; @Slf4j public class CurrencyUtil { public static void setup() { - setBaseCurrencyCode("XMR"); + setBaseCurrencyCode(baseCurrencyCode); } private static final AssetRegistry assetRegistry = new AssetRegistry(); @@ -200,10 +200,10 @@ public class CurrencyUtil { result.add(new CryptoCurrency("BCH", "Bitcoin Cash")); result.add(new CryptoCurrency("ETH", "Ether")); result.add(new CryptoCurrency("LTC", "Litecoin")); - result.add(new CryptoCurrency("DAI-ERC20", "Dai Stablecoin (ERC20)")); - result.add(new CryptoCurrency("USDT-ERC20", "Tether USD (ERC20)")); - result.add(new CryptoCurrency("USDT-TRC20", "Tether USD (TRC20)")); - result.add(new CryptoCurrency("USDC-ERC20", "USD Coin (ERC20)")); + result.add(new CryptoCurrency("DAI-ERC20", "Dai Stablecoin")); + result.add(new CryptoCurrency("USDT-ERC20", "Tether USD")); + result.add(new CryptoCurrency("USDT-TRC20", "Tether USD")); + result.add(new CryptoCurrency("USDC-ERC20", "USD Coin")); result.sort(TradeCurrency::compareTo); return result; } diff --git a/core/src/main/java/haveno/core/locale/TraditionalCurrency.java b/core/src/main/java/haveno/core/locale/TraditionalCurrency.java index 1ab491467e..cc42342abb 100644 --- a/core/src/main/java/haveno/core/locale/TraditionalCurrency.java +++ b/core/src/main/java/haveno/core/locale/TraditionalCurrency.java @@ -86,7 +86,7 @@ public final class TraditionalCurrency extends TradeCurrency { } public static TraditionalCurrency fromProto(protobuf.TradeCurrency proto) { - return new TraditionalCurrency(proto.getCode(), proto.getName()); + return new TraditionalCurrency(proto.getCode(), CurrencyUtil.getNameByCode(proto.getCode())); } From a37654e116cf11c6c67e295610c97163b7109fa9 Mon Sep 17 00:00:00 2001 From: woodser <13068859+woodser@users.noreply.github.com> Date: Wed, 28 May 2025 09:18:20 -0400 Subject: [PATCH 39/74] arbitrator transactions show maker and taker fees --- core/src/main/resources/i18n/displayStrings.properties | 2 ++ .../main/funds/transactions/TransactionsListItem.java | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index 8133a7b0f0..d3ce81f9e1 100644 --- a/core/src/main/resources/i18n/displayStrings.properties +++ b/core/src/main/resources/i18n/displayStrings.properties @@ -1138,6 +1138,8 @@ funds.tx.disputeLost=Lost dispute case: {0} funds.tx.collateralForRefund=Refund collateral: {0} funds.tx.timeLockedPayoutTx=Time locked payout tx: {0} funds.tx.refund=Refund from arbitration: {0} +funds.tx.makerTradeFee=Maker fee: {0} +funds.tx.takerTradeFee=Taker fee: {0} funds.tx.unknown=Unknown reason: {0} funds.tx.noFundsFromDispute=No refund from dispute funds.tx.receivedFunds=Received funds diff --git a/desktop/src/main/java/haveno/desktop/main/funds/transactions/TransactionsListItem.java b/desktop/src/main/java/haveno/desktop/main/funds/transactions/TransactionsListItem.java index b325b04efa..11a013e42f 100644 --- a/desktop/src/main/java/haveno/desktop/main/funds/transactions/TransactionsListItem.java +++ b/desktop/src/main/java/haveno/desktop/main/funds/transactions/TransactionsListItem.java @@ -22,6 +22,7 @@ import com.google.common.base.Suppliers; import haveno.core.locale.Res; import haveno.core.offer.Offer; import haveno.core.offer.OpenOffer; +import haveno.core.trade.ArbitratorTrade; import haveno.core.trade.HavenoUtils; import haveno.core.trade.Tradable; import haveno.core.trade.Trade; @@ -159,6 +160,13 @@ public class TransactionsListItem { } } else { details = Res.get("funds.tx.unknown", tradeId); + if (trade instanceof ArbitratorTrade) { + if (txId.equals(trade.getMaker().getDepositTxHash())) { + details = Res.get("funds.tx.makerTradeFee", tradeId); + } else if (txId.equals(trade.getTaker().getDepositTxHash())) { + details = Res.get("funds.tx.takerTradeFee", tradeId); + } + } } } } From 5d5eb649c6dc5c6b2c6c19ce6d6ff800e319e978 Mon Sep 17 00:00:00 2001 From: woodser <13068859+woodser@users.noreply.github.com> Date: Wed, 28 May 2025 10:36:46 -0400 Subject: [PATCH 40/74] log possible DoS attack --- .../main/java/haveno/network/p2p/network/Connection.java | 6 +++--- .../network/p2p/peers/keepalive/KeepAliveHandler.java | 2 +- .../network/p2p/peers/peerexchange/PeerExchangeHandler.java | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/p2p/src/main/java/haveno/network/p2p/network/Connection.java b/p2p/src/main/java/haveno/network/p2p/network/Connection.java index 79df171470..02218f7249 100644 --- a/p2p/src/main/java/haveno/network/p2p/network/Connection.java +++ b/p2p/src/main/java/haveno/network/p2p/network/Connection.java @@ -656,7 +656,7 @@ public class Connection implements HasCapabilities, Runnable, MessageListener { private static synchronized void resetReportedInvalidRequestsThrottle(boolean logReport) { if (logReport) { - if (numThrottledInvalidRequestReports > 0) log.warn("We received {} other reports of invalid requests since the last log entry", numThrottledInvalidRequestReports); + if (numThrottledInvalidRequestReports > 0) log.warn("Possible DoS attack detected. We received {} other reports of invalid requests since the last log entry", numThrottledInvalidRequestReports); numThrottledInvalidRequestReports = 0; lastLoggedInvalidRequestReportTs = System.currentTimeMillis(); } @@ -942,7 +942,7 @@ public class Connection implements HasCapabilities, Runnable, MessageListener { boolean doLog = System.currentTimeMillis() - lastLoggedWarningTs > LOG_THROTTLE_INTERVAL_MS; if (doLog) { log.warn(msg); - if (numThrottledWarnings > 0) log.warn("{} warnings were throttled since the last log entry", numThrottledWarnings); + if (numThrottledWarnings > 0) log.warn("Possible DoS attack detected. {} warnings were throttled since the last log entry", numThrottledWarnings); numThrottledWarnings = 0; lastLoggedWarningTs = System.currentTimeMillis(); } else { @@ -954,7 +954,7 @@ public class Connection implements HasCapabilities, Runnable, MessageListener { boolean doLog = System.currentTimeMillis() - lastLoggedInfoTs > LOG_THROTTLE_INTERVAL_MS; if (doLog) { log.info(msg); - if (numThrottledInfos > 0) log.info("{} info logs were throttled since the last log entry", numThrottledInfos); + if (numThrottledInfos > 0) log.info("Possible DoS attack detected. {} info logs were throttled since the last log entry", numThrottledInfos); numThrottledInfos = 0; lastLoggedInfoTs = System.currentTimeMillis(); } else { diff --git a/p2p/src/main/java/haveno/network/p2p/peers/keepalive/KeepAliveHandler.java b/p2p/src/main/java/haveno/network/p2p/peers/keepalive/KeepAliveHandler.java index 07ea9397a5..bcc4100ff6 100644 --- a/p2p/src/main/java/haveno/network/p2p/peers/keepalive/KeepAliveHandler.java +++ b/p2p/src/main/java/haveno/network/p2p/peers/keepalive/KeepAliveHandler.java @@ -173,7 +173,7 @@ class KeepAliveHandler implements MessageListener { boolean logWarning = System.currentTimeMillis() - lastLoggedWarningTs > LOG_THROTTLE_INTERVAL_MS; if (logWarning) { log.warn(msg); - if (numThrottledWarnings > 0) log.warn("{} warnings were throttled since the last log entry", numThrottledWarnings); + if (numThrottledWarnings > 0) log.warn("Possible DoS attack detected. {} warnings were throttled since the last log entry", numThrottledWarnings); numThrottledWarnings = 0; lastLoggedWarningTs = System.currentTimeMillis(); } else { diff --git a/p2p/src/main/java/haveno/network/p2p/peers/peerexchange/PeerExchangeHandler.java b/p2p/src/main/java/haveno/network/p2p/peers/peerexchange/PeerExchangeHandler.java index 0b10307ccb..e0eb7a6f33 100644 --- a/p2p/src/main/java/haveno/network/p2p/peers/peerexchange/PeerExchangeHandler.java +++ b/p2p/src/main/java/haveno/network/p2p/peers/peerexchange/PeerExchangeHandler.java @@ -222,7 +222,7 @@ class PeerExchangeHandler implements MessageListener { boolean logWarning = System.currentTimeMillis() - lastLoggedWarningTs > LOG_THROTTLE_INTERVAL_MS; if (logWarning) { log.warn(msg); - if (numThrottledWarnings > 0) log.warn("{} warnings were throttled since the last log entry", numThrottledWarnings); + if (numThrottledWarnings > 0) log.warn("Possible DoS attack detected. {} warnings were throttled since the last log entry", numThrottledWarnings); numThrottledWarnings = 0; lastLoggedWarningTs = System.currentTimeMillis(); } else { From 8f310469da5fe1f725061870adabd9de118b2b9c Mon Sep 17 00:00:00 2001 From: woodser <13068859+woodser@users.noreply.github.com> Date: Wed, 28 May 2025 10:28:58 -0400 Subject: [PATCH 41/74] fix npe with japan bank account --- .../main/java/haveno/core/payment/JapanBankData.java | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/core/src/main/java/haveno/core/payment/JapanBankData.java b/core/src/main/java/haveno/core/payment/JapanBankData.java index 9181e0f48a..c8919acec0 100644 --- a/core/src/main/java/haveno/core/payment/JapanBankData.java +++ b/core/src/main/java/haveno/core/payment/JapanBankData.java @@ -18,8 +18,7 @@ package haveno.core.payment; import com.google.common.collect.ImmutableMap; -import com.google.inject.Inject; -import haveno.core.user.Preferences; +import haveno.core.trade.HavenoUtils; import java.util.ArrayList; import java.util.List; @@ -47,13 +46,6 @@ import java.util.Map; public class JapanBankData { - private static String userLanguage; - - @Inject - JapanBankData(Preferences preferences) { - userLanguage = preferences.getUserLanguage(); - } - /* Returns the main list of ~500 banks in Japan with bank codes, but since 90%+ of people will be using one of ~30 major banks, @@ -793,7 +785,7 @@ public class JapanBankData { // don't localize these strings into all languages, // all we want is either Japanese or English here. public static String getString(String id) { - boolean ja = userLanguage.equals("ja"); + boolean ja = HavenoUtils.preferences.getUserLanguage().equals("ja"); switch (id) { case "bank": From 85ee6787cda0da511469e38279fd403bfce03442 Mon Sep 17 00:00:00 2001 From: woodser <13068859+woodser@users.noreply.github.com> Date: Thu, 29 May 2025 16:29:32 -0400 Subject: [PATCH 42/74] fix npe on report dispute button with null payment account --- .../desktop/main/support/dispute/DisputeView.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/desktop/src/main/java/haveno/desktop/main/support/dispute/DisputeView.java b/desktop/src/main/java/haveno/desktop/main/support/dispute/DisputeView.java index 5ac5f03328..5af027c16c 100644 --- a/desktop/src/main/java/haveno/desktop/main/support/dispute/DisputeView.java +++ b/desktop/src/main/java/haveno/desktop/main/support/dispute/DisputeView.java @@ -739,11 +739,13 @@ public abstract class DisputeView extends ActivatableView implements .append(winner) .append(")\n"); - String buyerPaymentAccountPayload = Utilities.toTruncatedString( - firstDispute.getBuyerPaymentAccountPayload().getPaymentDetails(). + String buyerPaymentAccountPayload = firstDispute.getBuyerPaymentAccountPayload() == null ? null : + Utilities.toTruncatedString( + firstDispute.getBuyerPaymentAccountPayload().getPaymentDetails(). replace("\n", " ").replace(";", "."), 100); - String sellerPaymentAccountPayload = Utilities.toTruncatedString( - firstDispute.getSellerPaymentAccountPayload().getPaymentDetails() + String sellerPaymentAccountPayload = firstDispute.getSellerPaymentAccountPayload() == null ? null : + Utilities.toTruncatedString( + firstDispute.getSellerPaymentAccountPayload().getPaymentDetails() .replace("\n", " ").replace(";", "."), 100); String buyerNodeAddress = contract.getBuyerNodeAddress().getFullAddress(); String sellerNodeAddress = contract.getSellerNodeAddress().getFullAddress(); From dd65cdca13effc0fe899d637b115f806de08c83f Mon Sep 17 00:00:00 2001 From: woodser <13068859+woodser@users.noreply.github.com> Date: Thu, 29 May 2025 17:07:20 -0400 Subject: [PATCH 43/74] fix custom amounts for dispute result --- .../windows/DisputeSummaryWindow.java | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/desktop/src/main/java/haveno/desktop/main/overlays/windows/DisputeSummaryWindow.java b/desktop/src/main/java/haveno/desktop/main/overlays/windows/DisputeSummaryWindow.java index ebf1c25309..5004d240d8 100644 --- a/desktop/src/main/java/haveno/desktop/main/overlays/windows/DisputeSummaryWindow.java +++ b/desktop/src/main/java/haveno/desktop/main/overlays/windows/DisputeSummaryWindow.java @@ -25,6 +25,7 @@ import haveno.common.handlers.ResultHandler; import haveno.common.util.Tuple2; import haveno.common.util.Tuple3; import haveno.core.api.CoreDisputesService; +import haveno.core.api.CoreDisputesService.PayoutSuggestion; import haveno.core.locale.Res; import haveno.core.support.SupportType; import haveno.core.support.dispute.Dispute; @@ -138,6 +139,7 @@ public class DisputeSummaryWindow extends Overlay { public void show(Dispute dispute) { this.dispute = dispute; this.trade = tradeManager.getTrade(dispute.getTradeId()); + this.payoutSuggestion = null; rowIndex = -1; width = 1150; @@ -243,7 +245,6 @@ public class DisputeSummaryWindow extends Overlay { reasonWasPeerWasLateRadioButton.setDisable(true); reasonWasTradeAlreadySettledRadioButton.setDisable(true); - applyPayoutAmounts(tradeAmountToggleGroup.selectedToggleProperty().get()); applyTradeAmountRadioButtonStates(); } @@ -724,6 +725,10 @@ public class DisputeSummaryWindow extends Overlay { private void applyTradeAmountRadioButtonStates() { + if (payoutSuggestion == null) { + payoutSuggestion = getPayoutSuggestionFromDisputeResult(); + } + BigInteger buyerPayoutAmount = disputeResult.getBuyerPayoutAmountBeforeCost(); BigInteger sellerPayoutAmount = disputeResult.getSellerPayoutAmountBeforeCost(); @@ -748,4 +753,20 @@ public class DisputeSummaryWindow extends Overlay { break; } } + + // TODO: Persist the payout suggestion to DisputeResult like Bisq upstream? + // That would be a better design, but it's not currently needed. + private PayoutSuggestion getPayoutSuggestionFromDisputeResult() { + if (disputeResult.getBuyerPayoutAmountBeforeCost().equals(BigInteger.ZERO)) { + return PayoutSuggestion.SELLER_GETS_ALL; + } else if (disputeResult.getSellerPayoutAmountBeforeCost().equals(BigInteger.ZERO)) { + return PayoutSuggestion.BUYER_GETS_ALL; + } else if (disputeResult.getBuyerPayoutAmountBeforeCost().equals(trade.getAmount().add(trade.getBuyer().getSecurityDeposit()))) { + return PayoutSuggestion.BUYER_GETS_TRADE_AMOUNT; + } else if (disputeResult.getSellerPayoutAmountBeforeCost().equals(trade.getAmount().add(trade.getSeller().getSecurityDeposit()))) { + return PayoutSuggestion.SELLER_GETS_TRADE_AMOUNT; + } else { + return PayoutSuggestion.CUSTOM; + } + } } From 5b04eb17a2f6ad259bebaeee5fb59697ea526734 Mon Sep 17 00:00:00 2001 From: woodser <13068859+woodser@users.noreply.github.com> Date: Fri, 30 May 2025 09:54:20 -0400 Subject: [PATCH 44/74] improve error messages when deposit txs are missing --- .../trade/protocol/tasks/ProcessPaymentReceivedMessage.java | 2 +- .../trade/protocol/tasks/ProcessPaymentSentMessage.java | 2 +- core/src/main/resources/i18n/displayStrings.properties | 6 +----- core/src/main/resources/i18n/displayStrings_cs.properties | 6 +----- core/src/main/resources/i18n/displayStrings_de.properties | 2 +- core/src/main/resources/i18n/displayStrings_es.properties | 2 +- core/src/main/resources/i18n/displayStrings_fa.properties | 2 +- core/src/main/resources/i18n/displayStrings_fr.properties | 2 +- core/src/main/resources/i18n/displayStrings_it.properties | 2 +- core/src/main/resources/i18n/displayStrings_ja.properties | 2 +- .../src/main/resources/i18n/displayStrings_pt-br.properties | 2 +- core/src/main/resources/i18n/displayStrings_pt.properties | 2 +- core/src/main/resources/i18n/displayStrings_ru.properties | 2 +- core/src/main/resources/i18n/displayStrings_th.properties | 2 +- core/src/main/resources/i18n/displayStrings_tr.properties | 6 +----- core/src/main/resources/i18n/displayStrings_vi.properties | 2 +- .../main/resources/i18n/displayStrings_zh-hans.properties | 2 +- .../main/resources/i18n/displayStrings_zh-hant.properties | 2 +- 18 files changed, 18 insertions(+), 30 deletions(-) diff --git a/core/src/main/java/haveno/core/trade/protocol/tasks/ProcessPaymentReceivedMessage.java b/core/src/main/java/haveno/core/trade/protocol/tasks/ProcessPaymentReceivedMessage.java index 5d55bbaea7..331494f767 100644 --- a/core/src/main/java/haveno/core/trade/protocol/tasks/ProcessPaymentReceivedMessage.java +++ b/core/src/main/java/haveno/core/trade/protocol/tasks/ProcessPaymentReceivedMessage.java @@ -85,7 +85,7 @@ public class ProcessPaymentReceivedMessage extends TradeTask { if (!trade.isDepositsUnlocked()) { trade.syncAndPollWallet(); if (!trade.isDepositsUnlocked()) { - throw new RuntimeException("Cannot process PaymentReceivedMessage until wallet sees that deposits are unlocked for " + trade.getClass().getSimpleName() + " " + trade.getId()); + throw new RuntimeException("Cannot process PaymentReceivedMessage until the trade wallet sees that the deposits are unlocked for " + trade.getClass().getSimpleName() + " " + trade.getId()); } } diff --git a/core/src/main/java/haveno/core/trade/protocol/tasks/ProcessPaymentSentMessage.java b/core/src/main/java/haveno/core/trade/protocol/tasks/ProcessPaymentSentMessage.java index 1f99d64806..7bdd6659ea 100644 --- a/core/src/main/java/haveno/core/trade/protocol/tasks/ProcessPaymentSentMessage.java +++ b/core/src/main/java/haveno/core/trade/protocol/tasks/ProcessPaymentSentMessage.java @@ -52,7 +52,7 @@ public class ProcessPaymentSentMessage extends TradeTask { if (!trade.isDepositsConfirmed()) { trade.syncAndPollWallet(); if (!trade.isDepositsConfirmed()) { - throw new RuntimeException("Cannot process PaymentSentMessage until wallet sees that deposits are confirmed for " + trade.getClass().getSimpleName() + " " + trade.getId()); + throw new RuntimeException("Cannot process PaymentSentMessage until the trade wallet sees that the deposits are confirmed for " + trade.getClass().getSimpleName() + " " + trade.getId()); } } diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index d3ce81f9e1..c214a016dd 100644 --- a/core/src/main/resources/i18n/displayStrings.properties +++ b/core/src/main/resources/i18n/displayStrings.properties @@ -994,11 +994,7 @@ portfolio.pending.failedTrade.maker.missingTakerFeeTx=The peer's taker fee trans Without this tx, the trade cannot be completed. No funds have been locked. \ Your offer is still available to other traders, so you have not lost the maker fee. \ You can move this trade to failed trades. -portfolio.pending.failedTrade.missingDepositTx=The deposit transaction (the 2-of-2 multisig transaction) is missing.\n\n\ - Without this tx, the trade cannot be completed. No funds have been locked but your trade fee has been paid. \ - You can make a request to be reimbursed the trade fee here: \ - [HYPERLINK:https://github.com/haveno-dex/haveno/issues]\n\n\ - Feel free to move this trade to failed trades. +portfolio.pending.failedTrade.missingDepositTx=A deposit transaction is missing.\n\nThis transaction is required to complete the trade. Please ensure your wallet is fully synchronized with the Monero blockchain.\n\nYou can move this trade to the "Failed Trades" section to deactivate it. portfolio.pending.failedTrade.buyer.existingDepositTxButMissingDelayedPayoutTx=The delayed payout transaction is missing, \ but funds have been locked in the deposit transaction.\n\n\ Please do NOT send the traditional or cryptocurrency payment to the XMR seller, because without the delayed payout tx, arbitration \ diff --git a/core/src/main/resources/i18n/displayStrings_cs.properties b/core/src/main/resources/i18n/displayStrings_cs.properties index 8ca69463ca..ce414afabd 100644 --- a/core/src/main/resources/i18n/displayStrings_cs.properties +++ b/core/src/main/resources/i18n/displayStrings_cs.properties @@ -959,11 +959,7 @@ portfolio.pending.failedTrade.maker.missingTakerFeeTx=Chybí poplatek příjemce Bez tohoto tx nelze obchod dokončit. Nebyly uzamčeny žádné prostředky. Vaše nabídka je \ stále k dispozici dalším obchodníkům, takže jste neztratili poplatek za vytvoření. \ Tento obchod můžete přesunout do neúspěšných obchodů. -portfolio.pending.failedTrade.missingDepositTx=Vkladová transakce (transakce 2-of-2 multisig) chybí.\n\n\ - Bez této tx nelze obchod dokončit. Nebyly uzamčeny žádné prostředky, ale byl zaplacen váš obchodní poplatek. \ - Zde můžete požádat o vrácení obchodního poplatku: \ - [HYPERLINK:https://github.com/haveno-dex/haveno/issues]\n\n\ - Klidně můžete přesunout tento obchod do neúspěšných obchodů. +portfolio.pending.failedTrade.missingDepositTx=Chybí vkladová transakce.\n\nTato transakce je nutná k dokončení obchodu. Ujistěte se, že je vaše peněženka plně synchronizována s blockchainem Monero.\n\nTento obchod můžete přesunout do sekce „Neúspěšné obchody“ pro jeho deaktivaci. portfolio.pending.failedTrade.buyer.existingDepositTxButMissingDelayedPayoutTx=Zpožděná výplatní transakce chybí, ale prostředky byly uzamčeny v vkladové transakci.\n\n\ Nezasílejte prosím fiat nebo crypto platbu prodejci XMR, protože bez odložené platby tx nelze zahájit arbitráž. \ diff --git a/core/src/main/resources/i18n/displayStrings_de.properties b/core/src/main/resources/i18n/displayStrings_de.properties index 4663384a66..43b664b1cf 100644 --- a/core/src/main/resources/i18n/displayStrings_de.properties +++ b/core/src/main/resources/i18n/displayStrings_de.properties @@ -820,7 +820,7 @@ portfolio.pending.mediationResult.popup.alreadyAccepted=Sie haben bereits akzept portfolio.pending.failedTrade.taker.missingTakerFeeTx=Die Transaktion der Abnehmer-Gebühr fehlt.\n\nOhne diese tx kann der Handel nicht abgeschlossen werden. Keine Gelder wurden gesperrt und keine Handelsgebühr wurde bezahlt. Sie können diesen Handel zu den fehlgeschlagenen Händeln verschieben. portfolio.pending.failedTrade.maker.missingTakerFeeTx=Die Transaktion der Abnehmer-Gebühr fehlt.\n\nOhne diese tx kann der Handel nicht abgeschlossen werden. Keine Gelder wurden gesperrt. Ihr Angebot ist für andere Händler weiterhin verfügbar. Sie haben die Ersteller-Gebühr also nicht verloren. Sie können diesen Handel zu den fehlgeschlagenen Händeln verschieben. -portfolio.pending.failedTrade.missingDepositTx=Die Einzahlungstransaktion (die 2-of-2 Multisig-Transaktion) fehlt.\n\nOhne diese tx kann der Handel nicht abgeschlossen werden. Keine Gelder wurden gesperrt aber die Handels-Gebühr wurde bezahlt. Sie können eine Anfrage für eine Rückerstattung der Handels-Gebühr hier einreichen: [HYPERLINK:https://github.com/haveno-dex/haveno/issues]\n\nSie können diesen Handel gerne zu den fehlgeschlagenen Händeln verschieben. +portfolio.pending.failedTrade.missingDepositTx=Eine Einzahlungstransaktion fehlt.\n\nDiese Transaktion ist erforderlich, um den Handel abzuschließen. Bitte stellen Sie sicher, dass Ihre Wallet vollständig mit der Monero-Blockchain synchronisiert ist.\n\nSie können diesen Handel in den Bereich „Fehlgeschlagene Trades“ verschieben, um ihn zu deaktivieren. portfolio.pending.failedTrade.buyer.existingDepositTxButMissingDelayedPayoutTx=Die verzögerte Auszahlungstransaktion fehlt, aber die Gelder wurden in der Einzahlungstransaktion gesperrt.\n\nBitte schicken Sie KEINE Geld-(Traditional-) oder Crypto-Zahlungen an den XMR Verkäufer, weil ohne die verzögerte Auszahlungstransaktion später kein Schlichtungsverfahren eröffnet werden kann. Stattdessen öffnen Sie ein Vermittlungs-Ticket mit Cmd/Strg+o. Der Vermittler sollte vorschlagen, dass beide Handelspartner ihre vollständige Sicherheitskaution zurückerstattet bekommen (und der Verkäufer auch seinen Handels-Betrag). Durch diese Vorgehensweise entsteht kein Sicherheitsrisiko und es geht ausschließlich die Handelsgebühr verloren.\n\nSie können eine Rückerstattung der verlorenen gegangenen Handelsgebühren hier erbitten: [HYPERLINK:https://github.com/haveno-dex/haveno/issues] portfolio.pending.failedTrade.seller.existingDepositTxButMissingDelayedPayoutTx=Die verzögerte Auszahlungstransaktion fehlt, aber die Gelder wurden in der Einzahlungstransaktion gesperrt.\n\nWenn dem Käufer die verzögerte Auszahlungstransaktion auch fehlt, wird er dazu aufgefordert die Bezahlung NICHT zu schicken und stattdessen ein Vermittlungs-Ticket zu eröffnen. Sie sollten auch ein Vermittlungs-Ticket mit Cmd/Strg+o öffnen.\n\nWenn der Käufer die Zahlung noch nicht geschickt hat, sollte der Vermittler vorschlagen, dass beide Handelspartner ihre Sicherheitskaution vollständig zurückerhalten (und der Verkäufer auch den Handels-Betrag). Anderenfalls sollte der Handels-Betrag an den Käufer gehen.\n\nSie können eine Rückerstattung der verlorenen gegangenen Handelsgebühren hier erbitten: [HYPERLINK:https://github.com/haveno-dex/haveno/issues] portfolio.pending.failedTrade.errorMsgSet=Während der Ausführung des Handel-Protokolls ist ein Fehler aufgetreten.\n\nFehler: {0}\n\nEs kann sein, dass dieser Fehler nicht gravierend ist und der Handel ganz normal abgeschlossen werden kann. Wenn Sie sich unsicher sind, öffnen Sie ein Vermittlungs-Ticket um den Rat eines Haveno Vermittlers zu erhalten.\n\nWenn der Fehler gravierend war, kann der Handel nicht abgeschlossen werden und Sie haben vielleicht die Handelsgebühr verloren. Sie können eine Rückerstattung der verlorenen gegangenen Handelsgebühren hier erbitten: [HYPERLINK:https://github.com/haveno-dex/haveno/issues] diff --git a/core/src/main/resources/i18n/displayStrings_es.properties b/core/src/main/resources/i18n/displayStrings_es.properties index a8f105d6c0..a4001cb075 100644 --- a/core/src/main/resources/i18n/displayStrings_es.properties +++ b/core/src/main/resources/i18n/displayStrings_es.properties @@ -820,7 +820,7 @@ portfolio.pending.mediationResult.popup.alreadyAccepted=Ya ha aceptado portfolio.pending.failedTrade.taker.missingTakerFeeTx=Falta la transacción de tasa de tomador\n\nSin esta tx, el intercambio no se puede completar. No se han bloqueado fondos y no se ha pagado ninguna tasa de intercambio. Puede mover esta operación a intercambios fallidos. portfolio.pending.failedTrade.maker.missingTakerFeeTx=Falta la transacción de tasa de tomador de su par.\n\nSin esta tx, el intercambio no se puede completar. No se han bloqueado fondos. Su oferta aún está disponible para otros comerciantes, por lo que no ha perdido la tasa de tomador. Puede mover este intercambio a intercambios fallidos. -portfolio.pending.failedTrade.missingDepositTx=Falta la transacción de depósito (la transacción multifirma 2 de 2).\n\nSin esta tx, el intercambio no se puede completar. No se han bloqueado fondos, pero se ha pagado su tarifa comercial. Puede hacer una solicitud para que se le reembolse la tarifa comercial aquí: [HYPERLINK:https://github.com/haveno-dex/haveno/issues].\n\nSiéntase libre de mover esta operación a operaciones fallidas. +portfolio.pending.failedTrade.missingDepositTx=Falta una transacción de depósito.\n\nEsta transacción es necesaria para completar la operación. Por favor, asegúrate de que tu monedero esté completamente sincronizado con la cadena de bloques de Monero.\n\nPuedes mover esta operación a la sección de "Operaciones Fallidas" para desactivarla. portfolio.pending.failedTrade.buyer.existingDepositTxButMissingDelayedPayoutTx=Falta la transacción de pago demorado, pero los fondos se han bloqueado en la transacción de depósito.\n\nNO envíe el pago traditional o crypto al vendedor de XMR, porque sin el tx de pago demorado, no se puede abrir el arbitraje. En su lugar, abra un ticket de mediación con Cmd / Ctrl + o. El mediador debe sugerir que ambos pares recuperen el monto total de sus depósitos de seguridad (y el vendedor también recibirá el monto total de la operación). De esta manera, no hay riesgo en la seguridad y solo se pierden las tarifas comerciales.\n\nPuede solicitar un reembolso por las tarifas comerciales perdidas aquí: [HYPERLINK:https://github.com/haveno-dex/haveno/issues]. portfolio.pending.failedTrade.seller.existingDepositTxButMissingDelayedPayoutTx=Falta la transacción del pago demorado, pero los fondos se han bloqueado en la transacción de depósito.\n\nSi al comprador también le falta la transacción de pago demorado, se le indicará que NO envíe el pago y abra un ticket de mediación. También debe abrir un ticket de mediación con Cmd / Ctrl + o.\n\nSi el comprador aún no ha enviado el pago, el mediador debe sugerir que ambos pares recuperen el monto total de sus depósitos de seguridad (y el vendedor también recibirá el monto total de la operación). De lo contrario, el monto comercial debe ir al comprador.\n\nPuede solicitar un reembolso por las tarifas comerciales perdidas aquí: [HYPERLINK:https://github.com/haveno-dex/haveno/issues]. portfolio.pending.failedTrade.errorMsgSet=Hubo un error durante la ejecución del protocolo de intercambio.\n\nError: {0}\n\nPuede ser que este error no sea crítico y que el intercambio se pueda completar normalmente. Si no está seguro, abra un ticket de mediación para obtener consejos de los mediadores de Haveno.\n\nSi el error fue crítico y la operación no se puede completar, es posible que haya perdido su tarifa de operación. Solicite un reembolso por las tarifas comerciales perdidas aquí: [HYPERLINK:ttps://github.com/bisq-network/support/issues]. diff --git a/core/src/main/resources/i18n/displayStrings_fa.properties b/core/src/main/resources/i18n/displayStrings_fa.properties index dd0cbee6d3..891dc297da 100644 --- a/core/src/main/resources/i18n/displayStrings_fa.properties +++ b/core/src/main/resources/i18n/displayStrings_fa.properties @@ -819,7 +819,7 @@ portfolio.pending.mediationResult.popup.alreadyAccepted=You've already accepted portfolio.pending.failedTrade.taker.missingTakerFeeTx=The taker fee transaction is missing.\n\nWithout this tx, the trade cannot be completed. No funds have been locked and no trade fee has been paid. You can move this trade to failed trades. portfolio.pending.failedTrade.maker.missingTakerFeeTx=The peer's taker fee transaction is missing.\n\nWithout this tx, the trade cannot be completed. No funds have been locked. Your offer is still available to other traders, so you have not lost the maker fee. You can move this trade to failed trades. -portfolio.pending.failedTrade.missingDepositTx=The deposit transaction (the 2-of-2 multisig transaction) is missing.\n\nWithout this tx, the trade cannot be completed. No funds have been locked but your trade fee has been paid. You can make a request to be reimbursed the trade fee here: [HYPERLINK:https://github.com/haveno-dex/haveno/issues]\n\nFeel free to move this trade to failed trades. +portfolio.pending.failedTrade.missingDepositTx=یک تراکنش واریز مفقود است.\n\nاین تراکنش برای تکمیل معامله لازم است. لطفاً اطمینان حاصل کنید که کیف پول شما به‌طور کامل با بلاک‌چین مونرو همگام‌سازی شده است.\n\nمی‌توانید این معامله را به بخش «معاملات ناموفق» منتقل کنید تا غیرفعال شود. portfolio.pending.failedTrade.buyer.existingDepositTxButMissingDelayedPayoutTx=The delayed payout transaction is missing, but funds have been locked in the deposit transaction.\n\nPlease do NOT send the traditional or crypto payment to the XMR seller, because without the delayed payout tx, arbitration cannot be opened. Instead, open a mediation ticket with Cmd/Ctrl+o. The mediator should suggest that both peers each get back the the full amount of their security deposits (with seller receiving full trade amount back as well). This way, there is no security risk, and only trade fees are lost. \n\nYou can request a reimbursement for lost trade fees here: [HYPERLINK:https://github.com/haveno-dex/haveno/issues] portfolio.pending.failedTrade.seller.existingDepositTxButMissingDelayedPayoutTx=The delayed payout transaction is missing but funds have been locked in the deposit transaction.\n\nIf the buyer is also missing the delayed payout transaction, they will be instructed to NOT send the payment and open a mediation ticket instead. You should also open a mediation ticket with Cmd/Ctrl+o. \n\nIf the buyer has not sent payment yet, the mediator should suggest that both peers each get back the full amount of their security deposits (with seller receiving full trade amount back as well). Otherwise the trade amount should go to the buyer. \n\nYou can request a reimbursement for lost trade fees here: [HYPERLINK:https://github.com/haveno-dex/haveno/issues] portfolio.pending.failedTrade.errorMsgSet=There was an error during trade protocol execution.\n\nError: {0}\n\nIt might be that this error is not critical, and the trade can be completed normally. If you are unsure, open a mediation ticket to get advice from Haveno mediators. \n\nIf the error was critical and the trade cannot be completed, you might have lost your trade fee. Request a reimbursement for lost trade fees here: [HYPERLINK:https://github.com/haveno-dex/haveno/issues] diff --git a/core/src/main/resources/i18n/displayStrings_fr.properties b/core/src/main/resources/i18n/displayStrings_fr.properties index f4395ca9c4..0a0e1274ad 100644 --- a/core/src/main/resources/i18n/displayStrings_fr.properties +++ b/core/src/main/resources/i18n/displayStrings_fr.properties @@ -821,7 +821,7 @@ portfolio.pending.mediationResult.popup.alreadyAccepted=Vous avez déjà accept portfolio.pending.failedTrade.taker.missingTakerFeeTx=Le frais de transaction du preneur est manquant.\n\nSans ce tx, le trade ne peut être complété. Aucun fonds ont été verrouillés et aucun frais de trade a été payé. Vous pouvez déplacer ce trade vers les trade échoués. portfolio.pending.failedTrade.maker.missingTakerFeeTx=Le frais de transaction du pair preneur est manquant.\n\nSans ce tx, le trade ne peut être complété. Aucun fonds ont été verrouillés. Votre offre est toujours valable pour les autres traders, vous n'avez donc pas perdu le frais de maker. Vous pouvez déplacer ce trade vers les trades échoués. -portfolio.pending.failedTrade.missingDepositTx=Cette transaction de marge (transaction multi-signature de 2 à 2) est manquante.\n\nSans ce tx, la transaction ne peut pas être complétée. Aucun fonds n'est bloqué, mais vos frais de transaction sont toujours payés. Vous pouvez lancer une demande de compensation des frais de transaction ici: [HYPERLINK:https://github.com/haveno-dex/haveno/issues] \nN'hésitez pas à déplacer la transaction vers la transaction échouée. +portfolio.pending.failedTrade.missingDepositTx=Une transaction de dépôt est manquante.\n\nCette transaction est nécessaire pour compléter la transaction. Veuillez vous assurer que votre portefeuille est entièrement synchronisé avec la blockchain Monero.\n\nVous pouvez déplacer cette transaction dans la section « Transactions échouées » pour la désactiver. portfolio.pending.failedTrade.buyer.existingDepositTxButMissingDelayedPayoutTx=La transaction de paiement différée est manquante, mais les fonds ont été verrouillés dans la transaction de dépôt.\n\nVeuillez NE PAS envoyer de Fiat ou d'crypto au vendeur de XMR, car avec le tx de paiement différé, le jugemenbt ne peut être ouvert. À la place, ouvrez un ticket de médiation avec Cmd/Ctrl+O. Le médiateur devrait suggérer que les deux pair reçoivent tous les deux le montant total de leurs dépôts de sécurité (le vendeur aussi doit reçevoir le montant total du trade). De cette manière, il n'y a pas de risque de non sécurité, et seuls les frais du trade sont perdus.\n\nVous pouvez demander le remboursement des frais de trade perdus ici;\n[LIEN:https://github.com/haveno-dex/haveno/issues] portfolio.pending.failedTrade.seller.existingDepositTxButMissingDelayedPayoutTx=La transaction de paiement différée est manquante, mais les fonds ont été verrouillés dans la transaction de dépôt.\n\nSi l'acheteur n'a pas non plus la transaction de paiement différée, il sera informé du fait de ne PAS envoyer le paiement et d'ouvrir un ticket de médiation à la place. Vous devriez aussi ouvrir un ticket de médiation avec Cmd/Ctrl+o.\n\nSi l'acheteur n'a pas encore envoyé le paiement, le médiateur devrait suggérer que les deux pairs reçoivent le montant total de leurs dépôts de sécurité (le vendeur doit aussi reçevoir le montant total du trade). Sinon, le montant du trade revient à l'acheteur.\n\nVous pouvez effectuer une demande de remboursement pour les frais de trade perdus ici: [LIEN:https://github.com/haveno-dex/haveno/issues] portfolio.pending.failedTrade.errorMsgSet=Il y'a eu une erreur durant l'exécution du protocole de trade.\n\nErreur: {0}\n\nIl est possible que cette erreur ne soit pas critique, et que le trade puisse être complété normalement. Si vous n'en êtes pas sûr, ouvrez un ticket de médiation pour avoir des conseils de la part des médiateurs de Haveno.\n\nSi cette erreur est critique et que le trade ne peut être complété, il est possible que vous ayez perdu le frais du trade. Effectuez une demande de remboursement ici: [LIEN:https://github.com/haveno-dex/haveno/issues] diff --git a/core/src/main/resources/i18n/displayStrings_it.properties b/core/src/main/resources/i18n/displayStrings_it.properties index 4a5d177c0a..7847646806 100644 --- a/core/src/main/resources/i18n/displayStrings_it.properties +++ b/core/src/main/resources/i18n/displayStrings_it.properties @@ -819,7 +819,7 @@ portfolio.pending.mediationResult.popup.alreadyAccepted=Hai già accettato portfolio.pending.failedTrade.taker.missingTakerFeeTx=The taker fee transaction is missing.\n\nWithout this tx, the trade cannot be completed. No funds have been locked and no trade fee has been paid. You can move this trade to failed trades. portfolio.pending.failedTrade.maker.missingTakerFeeTx=The peer's taker fee transaction is missing.\n\nWithout this tx, the trade cannot be completed. No funds have been locked. Your offer is still available to other traders, so you have not lost the maker fee. You can move this trade to failed trades. -portfolio.pending.failedTrade.missingDepositTx=The deposit transaction (the 2-of-2 multisig transaction) is missing.\n\nWithout this tx, the trade cannot be completed. No funds have been locked but your trade fee has been paid. You can make a request to be reimbursed the trade fee here: [HYPERLINK:https://github.com/haveno-dex/haveno/issues]\n\nFeel free to move this trade to failed trades. +portfolio.pending.failedTrade.missingDepositTx=Manca una transazione di deposito.\n\nQuesta transazione è necessaria per completare lo scambio. Assicurati che il tuo portafoglio sia completamente sincronizzato con la blockchain di Monero.\n\nPuoi spostare questo scambio nella sezione "Scambi Falliti" per disattivarlo. portfolio.pending.failedTrade.buyer.existingDepositTxButMissingDelayedPayoutTx=The delayed payout transaction is missing, but funds have been locked in the deposit transaction.\n\nPlease do NOT send the traditional or crypto payment to the XMR seller, because without the delayed payout tx, arbitration cannot be opened. Instead, open a mediation ticket with Cmd/Ctrl+o. The mediator should suggest that both peers each get back the the full amount of their security deposits (with seller receiving full trade amount back as well). This way, there is no security risk, and only trade fees are lost. \n\nYou can request a reimbursement for lost trade fees here: [HYPERLINK:https://github.com/haveno-dex/haveno/issues] portfolio.pending.failedTrade.seller.existingDepositTxButMissingDelayedPayoutTx=The delayed payout transaction is missing but funds have been locked in the deposit transaction.\n\nIf the buyer is also missing the delayed payout transaction, they will be instructed to NOT send the payment and open a mediation ticket instead. You should also open a mediation ticket with Cmd/Ctrl+o. \n\nIf the buyer has not sent payment yet, the mediator should suggest that both peers each get back the full amount of their security deposits (with seller receiving full trade amount back as well). Otherwise the trade amount should go to the buyer. \n\nYou can request a reimbursement for lost trade fees here: [HYPERLINK:https://github.com/haveno-dex/haveno/issues] portfolio.pending.failedTrade.errorMsgSet=There was an error during trade protocol execution.\n\nError: {0}\n\nIt might be that this error is not critical, and the trade can be completed normally. If you are unsure, open a mediation ticket to get advice from Haveno mediators. \n\nIf the error was critical and the trade cannot be completed, you might have lost your trade fee. Request a reimbursement for lost trade fees here: [HYPERLINK:https://github.com/haveno-dex/haveno/issues] diff --git a/core/src/main/resources/i18n/displayStrings_ja.properties b/core/src/main/resources/i18n/displayStrings_ja.properties index 3514d37343..9036bd8e2d 100644 --- a/core/src/main/resources/i18n/displayStrings_ja.properties +++ b/core/src/main/resources/i18n/displayStrings_ja.properties @@ -820,7 +820,7 @@ portfolio.pending.mediationResult.popup.alreadyAccepted=すでに受け入れて portfolio.pending.failedTrade.taker.missingTakerFeeTx=欠測テイカー手数料のトランザクション。\n\nこのtxがなければ、トレードを完了できません。資金はロックされず、トレード手数料は支払いませんでした。「失敗トレード」へ送ることができます。 portfolio.pending.failedTrade.maker.missingTakerFeeTx=ピアのテイカー手数料のトランザクションは欠測します。\n\nこのtxがなければ、トレードを完了できません。資金はロックされませんでした。あなたのオファーがまだ他の取引者には有効ですので、メイカー手数料は失っていません。このトレードを「失敗トレード」へ送ることができます。 -portfolio.pending.failedTrade.missingDepositTx=入金トランザクション(2-of-2マルチシグトランザクション)は欠測します。\n\nこのtxがなければ、トレードを完了できません。資金はロックされませんでしたが、トレード手数料は支払いました。トレード手数料の返済要求はここから提出できます: [HYPERLINK:https://github.com/haveno-dex/haveno/issues]\n\nこのトレードを「失敗トレード」へ送れます。 +portfolio.pending.failedTrade.missingDepositTx=入金トランザクションが見つかりません。\n\nこのトランザクションは取引を完了するために必要です。Moneroブロックチェーンとウォレットが完全に同期されていることを確認してください。\n\nこの取引を「失敗した取引」セクションに移動して、無効化することができます。 portfolio.pending.failedTrade.buyer.existingDepositTxButMissingDelayedPayoutTx=遅延支払いトランザクションは欠測しますが、資金は入金トランザクションにロックされました。\n\nこの法定通貨・アルトコイン支払いをXMR売り手に送信しないで下さい。遅延支払いtxがなければ、係争仲裁は開始されることができません。代りに、「Cmd/Ctrl+o」で調停チケットをオープンして下さい。調停者はおそらく両方のピアへセキュリティデポジットの全額を払い戻しを提案します(売り手はトレード金額も払い戻しを受ける)。このような方法でセキュリティーのリスクがなし、トレード手数料のみが失われます。\n\n失われたトレード手数料の払い戻し要求はここから提出できます: [HYPERLINK:https://github.com/haveno-dex/haveno/issues] portfolio.pending.failedTrade.seller.existingDepositTxButMissingDelayedPayoutTx=遅延支払いトランザクションは欠測しますが、資金は入金トランザクションにロックされました。\n\n買い手の遅延支払いトランザクションが同じく欠測される場合、相手は支払いを送信せず調停チケットをオープンするように指示されます。同様に「Cmd/Ctrl+o」で調停チケットをオープンするのは賢明でしょう。\n\n買い手はまだ支払いを送信しなかった場合、調停者はおそらく両方のピアへセキュリティデポジットの全額を払い戻しを提案します(売り手はトレード金額も払い戻しを受ける)。さもなければ、トレード金額は買い手に支払われるでしょう。\n\n失われたトレード手数料の払い戻し要求はここから提出できます: [HYPERLINK:https://github.com/haveno-dex/haveno/issues] portfolio.pending.failedTrade.errorMsgSet=トレードプロトコルの実行にはエラーが生じました。\n\nエラー: {0}\n\nクリティカル・エラーではない可能性はあり、トレードは普通に完了できるかもしれない。迷う場合は調停チケットをオープンして、Haveno調停者からアドバイスを受けることができます。\n\nクリティカル・エラーでトレードが完了できなかった場合はトレード手数料は失われた可能性があります。失われたトレード手数料の払い戻し要求はここから提出できます: [HYPERLINK:https://github.com/haveno-dex/haveno/issues] diff --git a/core/src/main/resources/i18n/displayStrings_pt-br.properties b/core/src/main/resources/i18n/displayStrings_pt-br.properties index 03d90cc9fd..f6520e8436 100644 --- a/core/src/main/resources/i18n/displayStrings_pt-br.properties +++ b/core/src/main/resources/i18n/displayStrings_pt-br.properties @@ -822,7 +822,7 @@ portfolio.pending.mediationResult.popup.alreadyAccepted=Você já aceitou portfolio.pending.failedTrade.taker.missingTakerFeeTx=The taker fee transaction is missing.\n\nWithout this tx, the trade cannot be completed. No funds have been locked and no trade fee has been paid. You can move this trade to failed trades. portfolio.pending.failedTrade.maker.missingTakerFeeTx=The peer's taker fee transaction is missing.\n\nWithout this tx, the trade cannot be completed. No funds have been locked. Your offer is still available to other traders, so you have not lost the maker fee. You can move this trade to failed trades. -portfolio.pending.failedTrade.missingDepositTx=The deposit transaction (the 2-of-2 multisig transaction) is missing.\n\nWithout this tx, the trade cannot be completed. No funds have been locked but your trade fee has been paid. You can make a request to be reimbursed the trade fee here: [HYPERLINK:https://github.com/haveno-dex/haveno/issues]\n\nFeel free to move this trade to failed trades. +portfolio.pending.failedTrade.missingDepositTx=Uma transação de depósito está faltando.\n\nEssa transação é necessária para concluir a negociação. Por favor, certifique-se de que sua carteira esteja totalmente sincronizada com a blockchain do Monero.\n\nVocê pode mover esta negociação para a seção "Negociações Falhas" para desativá-la. portfolio.pending.failedTrade.buyer.existingDepositTxButMissingDelayedPayoutTx=The delayed payout transaction is missing, but funds have been locked in the deposit transaction.\n\nPlease do NOT send the traditional or crypto payment to the XMR seller, because without the delayed payout tx, arbitration cannot be opened. Instead, open a mediation ticket with Cmd/Ctrl+o. The mediator should suggest that both peers each get back the the full amount of their security deposits (with seller receiving full trade amount back as well). This way, there is no security risk, and only trade fees are lost. \n\nYou can request a reimbursement for lost trade fees here: [HYPERLINK:https://github.com/haveno-dex/haveno/issues] portfolio.pending.failedTrade.seller.existingDepositTxButMissingDelayedPayoutTx=The delayed payout transaction is missing but funds have been locked in the deposit transaction.\n\nIf the buyer is also missing the delayed payout transaction, they will be instructed to NOT send the payment and open a mediation ticket instead. You should also open a mediation ticket with Cmd/Ctrl+o. \n\nIf the buyer has not sent payment yet, the mediator should suggest that both peers each get back the full amount of their security deposits (with seller receiving full trade amount back as well). Otherwise the trade amount should go to the buyer. \n\nYou can request a reimbursement for lost trade fees here: [HYPERLINK:https://github.com/haveno-dex/haveno/issues] portfolio.pending.failedTrade.errorMsgSet=There was an error during trade protocol execution.\n\nError: {0}\n\nIt might be that this error is not critical, and the trade can be completed normally. If you are unsure, open a mediation ticket to get advice from Haveno mediators. \n\nIf the error was critical and the trade cannot be completed, you might have lost your trade fee. Request a reimbursement for lost trade fees here: [HYPERLINK:https://github.com/haveno-dex/haveno/issues] diff --git a/core/src/main/resources/i18n/displayStrings_pt.properties b/core/src/main/resources/i18n/displayStrings_pt.properties index 0ec7c93184..c69236f8b1 100644 --- a/core/src/main/resources/i18n/displayStrings_pt.properties +++ b/core/src/main/resources/i18n/displayStrings_pt.properties @@ -819,7 +819,7 @@ portfolio.pending.mediationResult.popup.alreadyAccepted=Você já aceitou portfolio.pending.failedTrade.taker.missingTakerFeeTx=The taker fee transaction is missing.\n\nWithout this tx, the trade cannot be completed. No funds have been locked and no trade fee has been paid. You can move this trade to failed trades. portfolio.pending.failedTrade.maker.missingTakerFeeTx=The peer's taker fee transaction is missing.\n\nWithout this tx, the trade cannot be completed. No funds have been locked. Your offer is still available to other traders, so you have not lost the maker fee. You can move this trade to failed trades. -portfolio.pending.failedTrade.missingDepositTx=The deposit transaction (the 2-of-2 multisig transaction) is missing.\n\nWithout this tx, the trade cannot be completed. No funds have been locked but your trade fee has been paid. You can make a request to be reimbursed the trade fee here: [HYPERLINK:https://github.com/haveno-dex/haveno/issues]\n\nFeel free to move this trade to failed trades. +portfolio.pending.failedTrade.missingDepositTx=Uma transação de depósito está faltando.\n\nEssa transação é necessária para concluir a negociação. Certifique-se de que sua carteira esteja totalmente sincronizada com a blockchain do Monero.\n\nVocê pode mover esta negociação para a seção "Negociações com Falha" para desativá-la. portfolio.pending.failedTrade.buyer.existingDepositTxButMissingDelayedPayoutTx=The delayed payout transaction is missing, but funds have been locked in the deposit transaction.\n\nPlease do NOT send the traditional or crypto payment to the XMR seller, because without the delayed payout tx, arbitration cannot be opened. Instead, open a mediation ticket with Cmd/Ctrl+o. The mediator should suggest that both peers each get back the the full amount of their security deposits (with seller receiving full trade amount back as well). This way, there is no security risk, and only trade fees are lost. \n\nYou can request a reimbursement for lost trade fees here: [HYPERLINK:https://github.com/haveno-dex/haveno/issues] portfolio.pending.failedTrade.seller.existingDepositTxButMissingDelayedPayoutTx=The delayed payout transaction is missing but funds have been locked in the deposit transaction.\n\nIf the buyer is also missing the delayed payout transaction, they will be instructed to NOT send the payment and open a mediation ticket instead. You should also open a mediation ticket with Cmd/Ctrl+o. \n\nIf the buyer has not sent payment yet, the mediator should suggest that both peers each get back the full amount of their security deposits (with seller receiving full trade amount back as well). Otherwise the trade amount should go to the buyer. \n\nYou can request a reimbursement for lost trade fees here: [HYPERLINK:https://github.com/haveno-dex/haveno/issues] portfolio.pending.failedTrade.errorMsgSet=There was an error during trade protocol execution.\n\nError: {0}\n\nIt might be that this error is not critical, and the trade can be completed normally. If you are unsure, open a mediation ticket to get advice from Haveno mediators. \n\nIf the error was critical and the trade cannot be completed, you might have lost your trade fee. Request a reimbursement for lost trade fees here: [HYPERLINK:https://github.com/haveno-dex/haveno/issues] diff --git a/core/src/main/resources/i18n/displayStrings_ru.properties b/core/src/main/resources/i18n/displayStrings_ru.properties index 46828f97b1..f5ded7cdb2 100644 --- a/core/src/main/resources/i18n/displayStrings_ru.properties +++ b/core/src/main/resources/i18n/displayStrings_ru.properties @@ -819,7 +819,7 @@ portfolio.pending.mediationResult.popup.alreadyAccepted=You've already accepted portfolio.pending.failedTrade.taker.missingTakerFeeTx=The taker fee transaction is missing.\n\nWithout this tx, the trade cannot be completed. No funds have been locked and no trade fee has been paid. You can move this trade to failed trades. portfolio.pending.failedTrade.maker.missingTakerFeeTx=The peer's taker fee transaction is missing.\n\nWithout this tx, the trade cannot be completed. No funds have been locked. Your offer is still available to other traders, so you have not lost the maker fee. You can move this trade to failed trades. -portfolio.pending.failedTrade.missingDepositTx=The deposit transaction (the 2-of-2 multisig transaction) is missing.\n\nWithout this tx, the trade cannot be completed. No funds have been locked but your trade fee has been paid. You can make a request to be reimbursed the trade fee here: [HYPERLINK:https://github.com/haveno-dex/haveno/issues]\n\nFeel free to move this trade to failed trades. +portfolio.pending.failedTrade.missingDepositTx=Отсутствует транзакция депозита.\n\nЭта транзакция необходима для завершения сделки. Пожалуйста, убедитесь, что ваш кошелёк полностью синхронизирован с блокчейном Monero.\n\nВы можете переместить эту сделку в раздел "Неудачные сделки", чтобы деактивировать её. portfolio.pending.failedTrade.buyer.existingDepositTxButMissingDelayedPayoutTx=The delayed payout transaction is missing, but funds have been locked in the deposit transaction.\n\nPlease do NOT send the traditional or crypto payment to the XMR seller, because without the delayed payout tx, arbitration cannot be opened. Instead, open a mediation ticket with Cmd/Ctrl+o. The mediator should suggest that both peers each get back the the full amount of their security deposits (with seller receiving full trade amount back as well). This way, there is no security risk, and only trade fees are lost. \n\nYou can request a reimbursement for lost trade fees here: [HYPERLINK:https://github.com/haveno-dex/haveno/issues] portfolio.pending.failedTrade.seller.existingDepositTxButMissingDelayedPayoutTx=The delayed payout transaction is missing but funds have been locked in the deposit transaction.\n\nIf the buyer is also missing the delayed payout transaction, they will be instructed to NOT send the payment and open a mediation ticket instead. You should also open a mediation ticket with Cmd/Ctrl+o. \n\nIf the buyer has not sent payment yet, the mediator should suggest that both peers each get back the full amount of their security deposits (with seller receiving full trade amount back as well). Otherwise the trade amount should go to the buyer. \n\nYou can request a reimbursement for lost trade fees here: [HYPERLINK:https://github.com/haveno-dex/haveno/issues] portfolio.pending.failedTrade.errorMsgSet=There was an error during trade protocol execution.\n\nError: {0}\n\nIt might be that this error is not critical, and the trade can be completed normally. If you are unsure, open a mediation ticket to get advice from Haveno mediators. \n\nIf the error was critical and the trade cannot be completed, you might have lost your trade fee. Request a reimbursement for lost trade fees here: [HYPERLINK:https://github.com/haveno-dex/haveno/issues] diff --git a/core/src/main/resources/i18n/displayStrings_th.properties b/core/src/main/resources/i18n/displayStrings_th.properties index 56fe9e67c9..d2d89f3b83 100644 --- a/core/src/main/resources/i18n/displayStrings_th.properties +++ b/core/src/main/resources/i18n/displayStrings_th.properties @@ -819,7 +819,7 @@ portfolio.pending.mediationResult.popup.alreadyAccepted=You've already accepted portfolio.pending.failedTrade.taker.missingTakerFeeTx=The taker fee transaction is missing.\n\nWithout this tx, the trade cannot be completed. No funds have been locked and no trade fee has been paid. You can move this trade to failed trades. portfolio.pending.failedTrade.maker.missingTakerFeeTx=The peer's taker fee transaction is missing.\n\nWithout this tx, the trade cannot be completed. No funds have been locked. Your offer is still available to other traders, so you have not lost the maker fee. You can move this trade to failed trades. -portfolio.pending.failedTrade.missingDepositTx=The deposit transaction (the 2-of-2 multisig transaction) is missing.\n\nWithout this tx, the trade cannot be completed. No funds have been locked but your trade fee has been paid. You can make a request to be reimbursed the trade fee here: [HYPERLINK:https://github.com/haveno-dex/haveno/issues]\n\nFeel free to move this trade to failed trades. +portfolio.pending.failedTrade.missingDepositTx=ธุรกรรมเงินมัดจำหายไป\n\nธุรกรรมนี้จำเป็นสำหรับการดำเนินการซื้อขายให้เสร็จสมบูรณ์ กรุณาตรวจสอบให้แน่ใจว่า Wallet ของคุณได้ซิงค์กับบล็อกเชน Monero อย่างสมบูรณ์แล้ว\n\nคุณสามารถย้ายการซื้อขายนี้ไปยังส่วน "การซื้อขายที่ล้มเหลว" เพื่อปิดการใช้งานได้ portfolio.pending.failedTrade.buyer.existingDepositTxButMissingDelayedPayoutTx=The delayed payout transaction is missing, but funds have been locked in the deposit transaction.\n\nPlease do NOT send the traditional or crypto payment to the XMR seller, because without the delayed payout tx, arbitration cannot be opened. Instead, open a mediation ticket with Cmd/Ctrl+o. The mediator should suggest that both peers each get back the the full amount of their security deposits (with seller receiving full trade amount back as well). This way, there is no security risk, and only trade fees are lost. \n\nYou can request a reimbursement for lost trade fees here: [HYPERLINK:https://github.com/haveno-dex/haveno/issues] portfolio.pending.failedTrade.seller.existingDepositTxButMissingDelayedPayoutTx=The delayed payout transaction is missing but funds have been locked in the deposit transaction.\n\nIf the buyer is also missing the delayed payout transaction, they will be instructed to NOT send the payment and open a mediation ticket instead. You should also open a mediation ticket with Cmd/Ctrl+o. \n\nIf the buyer has not sent payment yet, the mediator should suggest that both peers each get back the full amount of their security deposits (with seller receiving full trade amount back as well). Otherwise the trade amount should go to the buyer. \n\nYou can request a reimbursement for lost trade fees here: [HYPERLINK:https://github.com/haveno-dex/haveno/issues] portfolio.pending.failedTrade.errorMsgSet=There was an error during trade protocol execution.\n\nError: {0}\n\nIt might be that this error is not critical, and the trade can be completed normally. If you are unsure, open a mediation ticket to get advice from Haveno mediators. \n\nIf the error was critical and the trade cannot be completed, you might have lost your trade fee. Request a reimbursement for lost trade fees here: [HYPERLINK:https://github.com/haveno-dex/haveno/issues] diff --git a/core/src/main/resources/i18n/displayStrings_tr.properties b/core/src/main/resources/i18n/displayStrings_tr.properties index d796e21e20..426c99500f 100644 --- a/core/src/main/resources/i18n/displayStrings_tr.properties +++ b/core/src/main/resources/i18n/displayStrings_tr.properties @@ -956,11 +956,7 @@ portfolio.pending.failedTrade.maker.missingTakerFeeTx=Karşı tarafın alıcı Bu işlem olmadan, ticaret tamamlanamaz. Hiçbir fon kilitlenmedi. \ Teklifiniz diğer tüccarlar için hala mevcut, bu yüzden üretici ücretini kaybetmediniz. \ Bu ticareti başarısız ticaretler arasına taşıyabilirsiniz. -portfolio.pending.failedTrade.missingDepositTx=Para yatırma işlemi (2-of-2 multisig işlemi) eksik.\n\n\ - Bu işlem olmadan, ticaret tamamlanamaz. Hiçbir fon kilitlenmedi ancak ticaret ücretiniz ödendi. \ - Ticaret ücretinin geri ödenmesi için burada talepte bulunabilirsiniz: \ - [HYPERLINK:https://github.com/haveno-dex/haveno/issues]\n\n\ - Bu ticareti başarısız ticaretler arasına taşımakta özgürsünüz. +portfolio.pending.failedTrade.missingDepositTx=Bir teminat işlemi eksik.\n\nBu işlem, ticareti tamamlamak için gereklidir. Lütfen cüzdanınızın Monero blok zinciri ile tamamen senkronize olduğundan emin olun.\n\nBu ticareti devre dışı bırakmak için "Başarısız İşlemler" bölümüne taşıyabilirsiniz. portfolio.pending.failedTrade.buyer.existingDepositTxButMissingDelayedPayoutTx=Gecikmiş ödeme işlemi eksik, \ ancak fonlar depozito işleminde kilitlendi.\n\n\ Lütfen geleneksel veya kripto para ödemesini XMR satıcısına göndermeyin, çünkü gecikmiş ödeme işlemi olmadan arabuluculuk \ diff --git a/core/src/main/resources/i18n/displayStrings_vi.properties b/core/src/main/resources/i18n/displayStrings_vi.properties index e6dd802dee..4df8f44b16 100644 --- a/core/src/main/resources/i18n/displayStrings_vi.properties +++ b/core/src/main/resources/i18n/displayStrings_vi.properties @@ -819,7 +819,7 @@ portfolio.pending.mediationResult.popup.alreadyAccepted=You've already accepted portfolio.pending.failedTrade.taker.missingTakerFeeTx=The taker fee transaction is missing.\n\nWithout this tx, the trade cannot be completed. No funds have been locked and no trade fee has been paid. You can move this trade to failed trades. portfolio.pending.failedTrade.maker.missingTakerFeeTx=The peer's taker fee transaction is missing.\n\nWithout this tx, the trade cannot be completed. No funds have been locked. Your offer is still available to other traders, so you have not lost the maker fee. You can move this trade to failed trades. -portfolio.pending.failedTrade.missingDepositTx=The deposit transaction (the 2-of-2 multisig transaction) is missing.\n\nWithout this tx, the trade cannot be completed. No funds have been locked but your trade fee has been paid. You can make a request to be reimbursed the trade fee here: [HYPERLINK:https://github.com/haveno-dex/haveno/issues]\n\nFeel free to move this trade to failed trades. +portfolio.pending.failedTrade.missingDepositTx=Một giao dịch ký quỹ đang bị thiếu.\n\nGiao dịch này là bắt buộc để hoàn tất giao dịch. Vui lòng đảm bảo ví của bạn được đồng bộ hoàn toàn với blockchain Monero.\n\nBạn có thể chuyển giao dịch này đến mục "Giao dịch thất bại" để vô hiệu hóa nó. portfolio.pending.failedTrade.buyer.existingDepositTxButMissingDelayedPayoutTx=The delayed payout transaction is missing, but funds have been locked in the deposit transaction.\n\nPlease do NOT send the traditional or crypto payment to the XMR seller, because without the delayed payout tx, arbitration cannot be opened. Instead, open a mediation ticket with Cmd/Ctrl+o. The mediator should suggest that both peers each get back the the full amount of their security deposits (with seller receiving full trade amount back as well). This way, there is no security risk, and only trade fees are lost. \n\nYou can request a reimbursement for lost trade fees here: [HYPERLINK:https://github.com/haveno-dex/haveno/issues] portfolio.pending.failedTrade.seller.existingDepositTxButMissingDelayedPayoutTx=The delayed payout transaction is missing but funds have been locked in the deposit transaction.\n\nIf the buyer is also missing the delayed payout transaction, they will be instructed to NOT send the payment and open a mediation ticket instead. You should also open a mediation ticket with Cmd/Ctrl+o. \n\nIf the buyer has not sent payment yet, the mediator should suggest that both peers each get back the full amount of their security deposits (with seller receiving full trade amount back as well). Otherwise the trade amount should go to the buyer. \n\nYou can request a reimbursement for lost trade fees here: [HYPERLINK:https://github.com/haveno-dex/haveno/issues] portfolio.pending.failedTrade.errorMsgSet=There was an error during trade protocol execution.\n\nError: {0}\n\nIt might be that this error is not critical, and the trade can be completed normally. If you are unsure, open a mediation ticket to get advice from Haveno mediators. \n\nIf the error was critical and the trade cannot be completed, you might have lost your trade fee. Request a reimbursement for lost trade fees here: [HYPERLINK:https://github.com/haveno-dex/haveno/issues] diff --git a/core/src/main/resources/i18n/displayStrings_zh-hans.properties b/core/src/main/resources/i18n/displayStrings_zh-hans.properties index f1b086eec3..1e65527036 100644 --- a/core/src/main/resources/i18n/displayStrings_zh-hans.properties +++ b/core/src/main/resources/i18n/displayStrings_zh-hans.properties @@ -820,7 +820,7 @@ portfolio.pending.mediationResult.popup.alreadyAccepted=您已经接受了。 portfolio.pending.failedTrade.taker.missingTakerFeeTx=吃单交易费未找到。\n\n如果没有 tx,交易不能完成。没有资金被锁定以及没有支付交易费用。你可以将交易移至失败的交易。 portfolio.pending.failedTrade.maker.missingTakerFeeTx=挂单费交易未找到。\n\n如果没有 tx,交易不能完成。没有资金被锁定以及没有支付交易费用。你可以将交易移至失败的交易。 -portfolio.pending.failedTrade.missingDepositTx=这个保证金交易(2 对 2 多重签名交易)缺失\n\n没有该 tx,交易不能完成。没有资金被锁定但是您的交易手续费仍然已支出。您可以发起一个请求去赔偿改交易手续费在这里:https://github.com/haveno-dex/haveno/issues\n\n请随意的将该交易移至失败交易 +portfolio.pending.failedTrade.missingDepositTx=缺少一笔保证金交易。\n\n该交易是完成交易所必需的。请确保您的钱包已与 Monero 区块链完全同步。\n\n您可以将此交易移动到“失败的交易”部分以将其停用。 portfolio.pending.failedTrade.buyer.existingDepositTxButMissingDelayedPayoutTx=延迟支付交易缺失,但是资金仍然被锁定在保证金交易中。\n\n请不要给比特币卖家发送法币或数字货币,因为没有延迟交易 tx,不能开启仲裁。使用 Cmd/Ctrl+o开启调解协助。调解员应该建议交易双方分别退回全部的保证金(卖方支付的交易金额也会全数返还)。这样的话不会有任何的安全问题只会损失交易手续费。\n\n你可以在这里为失败的交易提出赔偿要求:https://github.com/haveno-dex/haveno/issues portfolio.pending.failedTrade.seller.existingDepositTxButMissingDelayedPayoutTx=延迟支付交易确实但是资金仍然被锁定在保证金交易中。\n\n如果卖家仍然缺失延迟支付交易,他会接到请勿付款的指示并开启一个调节帮助。你也应该使用 Cmd/Ctrl+O 去打开一个调节协助\n\n如果买家还没有发送付款,调解员应该会建议交易双方分别退回全部的保证金(卖方支付的交易金额也会全数返还)。否则交易额应该判给买方。\n\n你可以在这里为失败的交易提出赔偿要求:https://github.com/haveno-dex/haveno/issues portfolio.pending.failedTrade.errorMsgSet=在处理交易协议是发生了一个错误\n\n错误:{0}\n\n这应该不是致命错误,您可以正常的完成交易。如果你仍担忧,打开一个调解协助并从 Haveno 调解员处得到建议。\n\n如果这个错误是致命的那么这个交易就无法完成,你可能会损失交易费。可以在这里为失败的交易提出赔偿要求:https://github.com/haveno-dex/haveno/issues diff --git a/core/src/main/resources/i18n/displayStrings_zh-hant.properties b/core/src/main/resources/i18n/displayStrings_zh-hant.properties index 94554bc12f..05f8bb5e8d 100644 --- a/core/src/main/resources/i18n/displayStrings_zh-hant.properties +++ b/core/src/main/resources/i18n/displayStrings_zh-hant.properties @@ -820,7 +820,7 @@ portfolio.pending.mediationResult.popup.alreadyAccepted=您已經接受了。 portfolio.pending.failedTrade.taker.missingTakerFeeTx=吃單交易費未找到。\n\n如果沒有 tx,交易不能完成。沒有資金被鎖定以及沒有支付交易費用。你可以將交易移至失敗的交易。 portfolio.pending.failedTrade.maker.missingTakerFeeTx=掛單費交易未找到。\n\n如果沒有 tx,交易不能完成。沒有資金被鎖定以及沒有支付交易費用。你可以將交易移至失敗的交易。 -portfolio.pending.failedTrade.missingDepositTx=這個保證金交易(2 對 2 多重簽名交易)缺失\n\n沒有該 tx,交易不能完成。沒有資金被鎖定但是您的交易手續費仍然已支出。您可以發起一個請求去賠償改交易手續費在這裏:https://github.com/haveno-dex/haveno/issues\n\n請隨意的將該交易移至失敗交易 +portfolio.pending.failedTrade.missingDepositTx=缺少一筆保證金交易。\n\n此交易是完成交易所必需的。請確保您的錢包已與 Monero 區塊鏈完全同步。\n\n您可以將此交易移至「失敗的交易」區段以停用它。 portfolio.pending.failedTrade.buyer.existingDepositTxButMissingDelayedPayoutTx=延遲支付交易缺失,但是資金仍然被鎖定在保證金交易中。\n\n請不要給比特幣賣家發送法幣或數字貨幣,因為沒有延遲交易 tx,不能開啟仲裁。使用 Cmd/Ctrl+o開啟調解協助。調解員應該建議交易雙方分別退回全部的保證金(賣方支付的交易金額也會全數返還)。這樣的話不會有任何的安全問題只會損失交易手續費。\n\n你可以在這裏為失敗的交易提出賠償要求:https://github.com/haveno-dex/haveno/issues portfolio.pending.failedTrade.seller.existingDepositTxButMissingDelayedPayoutTx=延遲支付交易確實但是資金仍然被鎖定在保證金交易中。\n\n如果賣家仍然缺失延遲支付交易,他會接到請勿付款的指示並開啟一個調節幫助。你也應該使用 Cmd/Ctrl+O 去打開一個調節協助\n\n如果買家還沒有發送付款,調解員應該會建議交易雙方分別退回全部的保證金(賣方支付的交易金額也會全數返還)。否則交易額應該判給買方。\n\n你可以在這裏為失敗的交易提出賠償要求:https://github.com/haveno-dex/haveno/issues portfolio.pending.failedTrade.errorMsgSet=在處理交易協議是發生了一個錯誤\n\n錯誤:{0}\n\n這應該不是致命錯誤,您可以正常的完成交易。如果你仍擔憂,打開一個調解協助並從 Haveno 調解員處得到建議。\n\n如果這個錯誤是致命的那麼這個交易就無法完成,你可能會損失交易費。可以在這裏為失敗的交易提出賠償要求:https://github.com/haveno-dex/haveno/issues From 9dd011afc887e71436434dba6da78c405460f184 Mon Sep 17 00:00:00 2001 From: woodser <13068859+woodser@users.noreply.github.com> Date: Sat, 31 May 2025 09:10:52 -0400 Subject: [PATCH 45/74] poll key images in batches --- .../core/xmr/wallet/XmrKeyImagePoller.java | 95 +++++++++++++------ 1 file changed, 64 insertions(+), 31 deletions(-) diff --git a/core/src/main/java/haveno/core/xmr/wallet/XmrKeyImagePoller.java b/core/src/main/java/haveno/core/xmr/wallet/XmrKeyImagePoller.java index 73332dc4fa..606ca29798 100644 --- a/core/src/main/java/haveno/core/xmr/wallet/XmrKeyImagePoller.java +++ b/core/src/main/java/haveno/core/xmr/wallet/XmrKeyImagePoller.java @@ -28,6 +28,7 @@ import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -42,12 +43,15 @@ public class XmrKeyImagePoller { private MoneroDaemon daemon; private long refreshPeriodMs; + private Object lock = new Object(); private Map> keyImageGroups = new HashMap>(); + private LinkedHashSet keyImagePollQueue = new LinkedHashSet<>(); private Set listeners = new HashSet(); private TaskLooper looper; private Map lastStatuses = new HashMap(); private boolean isPolling = false; private Long lastLogPollErrorTimestamp; + private static final int MAX_POLL_SIZE = 200; /** * Construct the listener. @@ -74,8 +78,10 @@ public class XmrKeyImagePoller { * @param listener - the listener to add */ public void addListener(XmrKeyImageListener listener) { - listeners.add(listener); - refreshPolling(); + synchronized (lock) { + listeners.add(listener); + refreshPolling(); + } } /** @@ -84,9 +90,11 @@ public class XmrKeyImagePoller { * @param listener - the listener to remove */ public void removeListener(XmrKeyImageListener listener) { - if (!listeners.contains(listener)) throw new MoneroError("Listener is not registered"); - listeners.remove(listener); - refreshPolling(); + synchronized (lock) { + if (!listeners.contains(listener)) throw new MoneroError("Listener is not registered"); + listeners.remove(listener); + refreshPolling(); + } } /** @@ -140,10 +148,11 @@ public class XmrKeyImagePoller { * @param keyImages - key images to listen to */ public void addKeyImages(Collection keyImages, String groupId) { - synchronized (this.keyImageGroups) { + synchronized (lock) { if (!keyImageGroups.containsKey(groupId)) keyImageGroups.put(groupId, new HashSet()); Set keyImagesGroup = keyImageGroups.get(groupId); keyImagesGroup.addAll(keyImages); + keyImagePollQueue.addAll(keyImages); refreshPolling(); } } @@ -154,17 +163,16 @@ public class XmrKeyImagePoller { * @param keyImages - key images to unlisten to */ public void removeKeyImages(Collection keyImages, String groupId) { - synchronized (keyImageGroups) { + synchronized (lock) { Set keyImagesGroup = keyImageGroups.get(groupId); if (keyImagesGroup == null) return; keyImagesGroup.removeAll(keyImages); if (keyImagesGroup.isEmpty()) keyImageGroups.remove(groupId); Set allKeyImages = getKeyImages(); - synchronized (lastStatuses) { - for (String keyImage : keyImages) { - if (lastStatuses.containsKey(keyImage) && !allKeyImages.contains(keyImage)) { - lastStatuses.remove(keyImage); - } + for (String keyImage : keyImages) { + if (!allKeyImages.contains(keyImage)) { + keyImagePollQueue.remove(keyImage); + lastStatuses.remove(keyImage); } } refreshPolling(); @@ -172,16 +180,15 @@ public class XmrKeyImagePoller { } public void removeKeyImages(String groupId) { - synchronized (keyImageGroups) { + synchronized (lock) { Set keyImagesGroup = keyImageGroups.get(groupId); if (keyImagesGroup == null) return; keyImageGroups.remove(groupId); Set allKeyImages = getKeyImages(); - synchronized (lastStatuses) { - for (String keyImage : keyImagesGroup) { - if (lastStatuses.containsKey(keyImage) && !allKeyImages.contains(keyImage)) { - lastStatuses.remove(keyImage); - } + for (String keyImage : keyImagesGroup) { + if (!allKeyImages.contains(keyImage)) { + keyImagePollQueue.remove(keyImage); + lastStatuses.remove(keyImage); } } refreshPolling(); @@ -192,11 +199,10 @@ public class XmrKeyImagePoller { * Clear the key images which stops polling. */ public void clearKeyImages() { - synchronized (keyImageGroups) { + synchronized (lock) { keyImageGroups.clear(); - synchronized (lastStatuses) { - lastStatuses.clear(); - } + keyImagePollQueue.clear(); + lastStatuses.clear(); refreshPolling(); } } @@ -208,7 +214,7 @@ public class XmrKeyImagePoller { * @return true if the key is spent, false if unspent, null if unknown */ public Boolean isSpent(String keyImage) { - synchronized (lastStatuses) { + synchronized (lock) { if (!lastStatuses.containsKey(keyImage)) return null; return XmrKeyImagePoller.isSpent(lastStatuses.get(keyImage)); } @@ -231,7 +237,7 @@ public class XmrKeyImagePoller { * @return the last known spent status of the key image */ public MoneroKeyImageSpentStatus getLastSpentStatus(String keyImage) { - synchronized (lastStatuses) { + synchronized (lock) { return lastStatuses.get(keyImage); } } @@ -244,7 +250,7 @@ public class XmrKeyImagePoller { // fetch spent statuses List spentStatuses = null; - List keyImages = new ArrayList(getKeyImages()); + List keyImages = new ArrayList(getNextKeyImageBatch()); try { spentStatuses = keyImages.isEmpty() ? new ArrayList() : daemon.getKeyImageSpentStatuses(keyImages); // TODO monero-java: if order of getKeyImageSpentStatuses is guaranteed, then it should take list parameter } catch (Exception e) { @@ -257,10 +263,20 @@ public class XmrKeyImagePoller { return; } - // collect changed statuses + // process spent statuses Map changedStatuses = new HashMap(); - synchronized (lastStatuses) { - for (int i = 0; i < spentStatuses.size(); i++) { + synchronized (lock) { + Set allKeyImages = getKeyImages(); + for (int i = 0; i < keyImages.size(); i++) { + + // skip if key image is removed + if (!allKeyImages.contains(keyImages.get(i))) continue; + + // move key image to the end of the queue + keyImagePollQueue.remove(keyImages.get(i)); + keyImagePollQueue.add(keyImages.get(i)); + + // update spent status if (spentStatuses.get(i) != lastStatuses.get(keyImages.get(i))) { lastStatuses.put(keyImages.get(i), spentStatuses.get(i)); changedStatuses.put(keyImages.get(i), spentStatuses.get(i)); @@ -270,14 +286,18 @@ public class XmrKeyImagePoller { // announce changes if (!changedStatuses.isEmpty()) { - for (XmrKeyImageListener listener : new ArrayList(listeners)) { + List listeners; + synchronized (lock) { + listeners = new ArrayList(this.listeners); + } + for (XmrKeyImageListener listener : listeners) { listener.onSpentStatusChanged(changedStatuses); } } } private void refreshPolling() { - synchronized (keyImageGroups) { + synchronized (lock) { setIsPolling(!getKeyImages().isEmpty() && listeners.size() > 0); } } @@ -296,11 +316,24 @@ public class XmrKeyImagePoller { private Set getKeyImages() { Set allKeyImages = new HashSet(); - synchronized (keyImageGroups) { + synchronized (lock) { for (Set keyImagesGroup : keyImageGroups.values()) { allKeyImages.addAll(keyImagesGroup); } } return allKeyImages; } + + private List getNextKeyImageBatch() { + synchronized (lock) { + List keyImageBatch = new ArrayList<>(); + int count = 0; + for (String keyImage : keyImagePollQueue) { + if (count >= MAX_POLL_SIZE) break; + keyImageBatch.add(keyImage); + count++; + } + return keyImageBatch; + } + } } From 98130499a730c9c4156497b679a272c7ba8d1522 Mon Sep 17 00:00:00 2001 From: woodser <13068859+woodser@users.noreply.github.com> Date: Sat, 31 May 2025 09:14:09 -0400 Subject: [PATCH 46/74] fix log of trade miner fee --- core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java b/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java index f2d16943d1..5a191068e1 100644 --- a/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java +++ b/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java @@ -767,7 +767,7 @@ public class XmrWalletService extends XmrWalletBase { // verify miner fee BigInteger minerFeeEstimate = getFeeEstimate(tx.getWeight()); HavenoUtils.verifyMinerFee(minerFeeEstimate, tx.getFee()); - log.info("Trade miner fee {} is within tolerance"); + log.info("Trade miner fee {} is within tolerance", tx.getFee()); // verify proof to fee address BigInteger actualTradeFee = BigInteger.ZERO; From 3648c1eb0ed5f6d4e3912c3de4d1ba2ca576ee36 Mon Sep 17 00:00:00 2001 From: woodser <13068859+woodser@users.noreply.github.com> Date: Sun, 1 Jun 2025 15:15:35 -0400 Subject: [PATCH 47/74] skip polling if shut down started after acquiring lock --- .../main/java/haveno/core/trade/Trade.java | 3 +++ .../core/xmr/wallet/XmrWalletService.java | 21 +++++++++++-------- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/core/src/main/java/haveno/core/trade/Trade.java b/core/src/main/java/haveno/core/trade/Trade.java index aa2330a78f..29df35e45f 100644 --- a/core/src/main/java/haveno/core/trade/Trade.java +++ b/core/src/main/java/haveno/core/trade/Trade.java @@ -2637,6 +2637,9 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model { // poll wallet try { + // skip if shut down started + if (isShutDownStarted) return; + // skip if payout unlocked if (isPayoutUnlocked()) return; diff --git a/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java b/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java index 5a191068e1..26f300b7c0 100644 --- a/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java +++ b/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java @@ -1994,6 +1994,9 @@ public class XmrWalletService extends XmrWalletBase { // poll wallet try { + // skip if shut down started + if (isShutDownStarted) return; + // skip if daemon not synced MoneroDaemonInfo lastInfo = xmrConnectionService.getLastInfo(); if (lastInfo == null) { @@ -2059,16 +2062,16 @@ public class XmrWalletService extends XmrWalletBase { pollInProgress = false; } } + saveWalletWithDelay(); + } - // cache wallet info last - synchronized (walletLock) { - if (wallet != null && !isShutDownStarted) { - try { - cacheWalletInfo(); - saveWalletWithDelay(); - } catch (Exception e) { - log.warn("Error caching wallet info: " + e.getMessage() + "\n", e); - } + // cache wallet info last + synchronized (walletLock) { + if (wallet != null && !isShutDownStarted) { + try { + cacheWalletInfo(); + } catch (Exception e) { + log.warn("Error caching wallet info: " + e.getMessage() + "\n", e); } } } From fa375b3cbde403ddda3aa1b6ab9c7b52023ae841 Mon Sep 17 00:00:00 2001 From: woodser <13068859+woodser@users.noreply.github.com> Date: Sun, 1 Jun 2025 17:36:57 -0400 Subject: [PATCH 48/74] process connection messages on main run thread --- p2p/src/main/java/haveno/network/p2p/network/Connection.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/p2p/src/main/java/haveno/network/p2p/network/Connection.java b/p2p/src/main/java/haveno/network/p2p/network/Connection.java index 02218f7249..2045c04c5b 100644 --- a/p2p/src/main/java/haveno/network/p2p/network/Connection.java +++ b/p2p/src/main/java/haveno/network/p2p/network/Connection.java @@ -872,7 +872,7 @@ public class Connection implements HasCapabilities, Runnable, MessageListener { log.info("We got a {} from a peer with yet unknown address on connection with uid={}", networkEnvelope.getClass().getSimpleName(), uid); } - ThreadUtils.execute(() -> onMessage(networkEnvelope, this), THREAD_ID); + onMessage(networkEnvelope, this); ThreadUtils.execute(() -> connectionStatistics.addReceivedMsgMetrics(System.currentTimeMillis() - ts, size), THREAD_ID); } } catch (InvalidClassException e) { From 501530d85f3e2fae98a18a766a6bfda4a99fc645 Mon Sep 17 00:00:00 2001 From: woodser <13068859+woodser@users.noreply.github.com> Date: Mon, 2 Jun 2025 08:48:03 -0400 Subject: [PATCH 49/74] read bip39 words once and synchronized --- .../java/haveno/core/trade/HavenoUtils.java | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/haveno/core/trade/HavenoUtils.java b/core/src/main/java/haveno/core/trade/HavenoUtils.java index 74caf5d7f2..32a6d76824 100644 --- a/core/src/main/java/haveno/core/trade/HavenoUtils.java +++ b/core/src/main/java/haveno/core/trade/HavenoUtils.java @@ -123,6 +123,7 @@ public class HavenoUtils { private static final BigInteger XMR_AU_MULTIPLIER = new BigInteger("1000000000000"); public static final DecimalFormat XMR_FORMATTER = new DecimalFormat("##############0.000000000000", DECIMAL_FORMAT_SYMBOLS); public static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss"); + private static List bip39Words = new ArrayList(); // shared references TODO: better way to share references? public static HavenoSetup havenoSetup; @@ -298,10 +299,7 @@ public class HavenoUtils { try { // load bip39 words - String fileName = "bip39_english.txt"; - File bip39File = new File(havenoSetup.getConfig().appDataDir, fileName); - if (!bip39File.exists()) FileUtil.resourceToFile(fileName, bip39File); - List bip39Words = Files.readAllLines(bip39File.toPath(), StandardCharsets.UTF_8); + loadBip39Words(); // select words randomly List passphraseWords = new ArrayList(); @@ -315,13 +313,26 @@ public class HavenoUtils { } } + private static synchronized void loadBip39Words() { + if (bip39Words.isEmpty()) { + try { + String fileName = "bip39_english.txt"; + File bip39File = new File(havenoSetup.getConfig().appDataDir, fileName); + if (!bip39File.exists()) FileUtil.resourceToFile(fileName, bip39File); + bip39Words = Files.readAllLines(bip39File.toPath(), StandardCharsets.UTF_8); + } catch (Exception e) { + throw new IllegalStateException("Failed to load BIP39 words", e); + } + } + } + public static String getChallengeHash(String challenge) { if (challenge == null) return null; // tokenize passphrase String[] words = challenge.toLowerCase().split(" "); - // collect first 4 letters of each word, which are unique in bip39 + // collect up to first 4 letters of each word, which are unique in bip39 List prefixes = new ArrayList(); for (String word : words) prefixes.add(word.substring(0, Math.min(word.length(), 4))); From 264e5f436e96ad26c06744565e22d49cbd9e6bed Mon Sep 17 00:00:00 2001 From: woodser <13068859+woodser@users.noreply.github.com> Date: Mon, 2 Jun 2025 08:48:17 -0400 Subject: [PATCH 50/74] stricter validation for creating private, no deposit offers --- .../main/java/haveno/core/offer/CreateOfferService.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/haveno/core/offer/CreateOfferService.java b/core/src/main/java/haveno/core/offer/CreateOfferService.java index fab646433b..161bf69a16 100644 --- a/core/src/main/java/haveno/core/offer/CreateOfferService.java +++ b/core/src/main/java/haveno/core/offer/CreateOfferService.java @@ -134,10 +134,12 @@ public class CreateOfferService { // must nullify empty string so contracts match if ("".equals(extraInfo)) extraInfo = null; - // verify buyer as taker security deposit + // verify config for private no deposit offers boolean isBuyerMaker = offerUtil.isBuyOffer(direction); - if (!isBuyerMaker && !isPrivateOffer && buyerAsTakerWithoutDeposit) { - throw new IllegalArgumentException("Buyer as taker deposit is required for public offers"); + if (buyerAsTakerWithoutDeposit || isPrivateOffer) { + if (isBuyerMaker) throw new IllegalArgumentException("Buyer must be taker for private offers without deposit"); + if (!buyerAsTakerWithoutDeposit) throw new IllegalArgumentException("Must set buyer as taker without deposit for private offers"); + if (!isPrivateOffer) throw new IllegalArgumentException("Must set offer to private for buyer as taker without deposit"); } // verify fixed price xor market price with margin From 33a91cf98016c6ffcfdbab652f9a93a4459dac12 Mon Sep 17 00:00:00 2001 From: woodser <13068859+woodser@users.noreply.github.com> Date: Sun, 1 Jun 2025 08:54:04 -0400 Subject: [PATCH 51/74] maker recreates reserve tx then cancels offer on trade nacks --- .../java/haveno/core/offer/OpenOffer.java | 4 + .../haveno/core/offer/OpenOfferManager.java | 6 +- .../java/haveno/core/trade/TradeManager.java | 2 +- .../core/trade/protocol/TradeProtocol.java | 74 ++++++++- .../tasks/MakerRecreateReserveTx.java | 147 ++++++++++++++++++ .../tasks/TakerReserveTradeFunds.java | 13 +- 6 files changed, 231 insertions(+), 15 deletions(-) create mode 100644 core/src/main/java/haveno/core/trade/protocol/tasks/MakerRecreateReserveTx.java diff --git a/core/src/main/java/haveno/core/offer/OpenOffer.java b/core/src/main/java/haveno/core/offer/OpenOffer.java index f493b1b584..9c51dead66 100644 --- a/core/src/main/java/haveno/core/offer/OpenOffer.java +++ b/core/src/main/java/haveno/core/offer/OpenOffer.java @@ -276,6 +276,10 @@ public final class OpenOffer implements Tradable { return state == State.AVAILABLE; } + public boolean isReserved() { + return state == State.RESERVED; + } + public boolean isDeactivated() { return state == State.DEACTIVATED; } diff --git a/core/src/main/java/haveno/core/offer/OpenOfferManager.java b/core/src/main/java/haveno/core/offer/OpenOfferManager.java index 710cb92d54..49fa770ae2 100644 --- a/core/src/main/java/haveno/core/offer/OpenOfferManager.java +++ b/core/src/main/java/haveno/core/offer/OpenOfferManager.java @@ -661,7 +661,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe ErrorMessageHandler errorMessageHandler) { log.info("Canceling open offer: {}", openOffer.getId()); if (!offersToBeEdited.containsKey(openOffer.getId())) { - if (openOffer.isAvailable()) { + if (isOnOfferBook(openOffer)) { openOffer.setState(OpenOffer.State.CANCELED); offerBookService.removeOffer(openOffer.getOffer().getOfferPayload(), () -> { @@ -683,6 +683,10 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe } } + private boolean isOnOfferBook(OpenOffer openOffer) { + return openOffer.isAvailable() || openOffer.isReserved(); + } + public void editOpenOfferStart(OpenOffer openOffer, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) { diff --git a/core/src/main/java/haveno/core/trade/TradeManager.java b/core/src/main/java/haveno/core/trade/TradeManager.java index 9befbd6e82..a1f2b5d0b7 100644 --- a/core/src/main/java/haveno/core/trade/TradeManager.java +++ b/core/src/main/java/haveno/core/trade/TradeManager.java @@ -997,7 +997,7 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi log.warn("Unregistering {} {}", trade.getClass().getSimpleName(), trade.getId()); removeTrade(trade, true); removeFailedTrade(trade); - xmrWalletService.swapPayoutAddressEntryToAvailable(trade.getId()); // TODO The address entry should have been removed already. Check and if its the case remove that. + if (!trade.isMaker()) xmrWalletService.swapPayoutAddressEntryToAvailable(trade.getId()); // TODO The address entry should have been removed already. Check and if its the case remove that. requestPersistence(); } diff --git a/core/src/main/java/haveno/core/trade/protocol/TradeProtocol.java b/core/src/main/java/haveno/core/trade/protocol/TradeProtocol.java index 5ff09324fd..4249e967a9 100644 --- a/core/src/main/java/haveno/core/trade/protocol/TradeProtocol.java +++ b/core/src/main/java/haveno/core/trade/protocol/TradeProtocol.java @@ -43,6 +43,7 @@ import haveno.common.handlers.ErrorMessageHandler; import haveno.common.proto.network.NetworkEnvelope; import haveno.common.taskrunner.Task; import haveno.core.network.MessageState; +import haveno.core.offer.OpenOffer; import haveno.core.trade.ArbitratorTrade; import haveno.core.trade.BuyerTrade; import haveno.core.trade.HavenoUtils; @@ -55,13 +56,17 @@ import haveno.core.trade.messages.DepositRequest; import haveno.core.trade.messages.DepositResponse; import haveno.core.trade.messages.DepositsConfirmedMessage; import haveno.core.trade.messages.InitMultisigRequest; +import haveno.core.trade.messages.InitTradeRequest; import haveno.core.trade.messages.PaymentReceivedMessage; import haveno.core.trade.messages.PaymentSentMessage; import haveno.core.trade.messages.SignContractRequest; import haveno.core.trade.messages.SignContractResponse; import haveno.core.trade.messages.TradeMessage; import haveno.core.trade.protocol.FluentProtocol.Condition; +import haveno.core.trade.protocol.FluentProtocol.Event; import haveno.core.trade.protocol.tasks.ApplyFilter; +import haveno.core.trade.protocol.tasks.MakerRecreateReserveTx; +import haveno.core.trade.protocol.tasks.MakerSendInitTradeRequestToArbitrator; import haveno.core.trade.protocol.tasks.MaybeSendSignContractRequest; import haveno.core.trade.protocol.tasks.ProcessDepositResponse; import haveno.core.trade.protocol.tasks.ProcessDepositsConfirmedMessage; @@ -110,6 +115,7 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D private boolean depositsConfirmedTasksCalled; private int reprocessPaymentSentMessageCount; private int reprocessPaymentReceivedMessageCount; + private boolean makerInitTradeRequestNacked = false; /////////////////////////////////////////////////////////////////////////////////////////// // Constructor @@ -758,6 +764,18 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D peer.setNodeAddress(sender); } + // TODO: arbitrator may nack maker's InitTradeRequest if reserve tx has become invalid (e.g. check_tx_key shows 0 funds received). recreate reserve tx in this case + if (!ackMessage.isSuccess() && trade.isMaker() && peer == trade.getArbitrator() && ackMessage.getSourceMsgClassName().equals(InitTradeRequest.class.getSimpleName())) { + if (makerInitTradeRequestNacked) { + handleSecondMakerInitTradeRequestNack(ackMessage); + // use default postprocessing + } else { + makerInitTradeRequestNacked = true; + handleFirstMakerInitTradeRequestNack(ackMessage); + return; + } + } + // handle nack of deposit request if (ackMessage.getSourceMsgClassName().equals(DepositRequest.class.getSimpleName())) { if (!ackMessage.isSuccess()) { @@ -774,12 +792,12 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D // handle ack message for PaymentSentMessage, which automatically re-sends if not ACKed in a certain time if (ackMessage.getSourceMsgClassName().equals(PaymentSentMessage.class.getSimpleName())) { - if (trade.getTradePeer(sender) == trade.getSeller()) { + if (peer == trade.getSeller()) { trade.getSeller().setPaymentSentAckMessage(ackMessage); if (ackMessage.isSuccess()) trade.setStateIfValidTransitionTo(Trade.State.SELLER_RECEIVED_PAYMENT_SENT_MSG); else trade.setState(Trade.State.BUYER_SEND_FAILED_PAYMENT_SENT_MSG); processModel.getTradeManager().requestPersistence(); - } else if (trade.getTradePeer(sender) == trade.getArbitrator()) { + } else if (peer == trade.getArbitrator()) { trade.getArbitrator().setPaymentSentAckMessage(ackMessage); processModel.getTradeManager().requestPersistence(); } else { @@ -792,7 +810,7 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D if (ackMessage.getSourceMsgClassName().equals(PaymentReceivedMessage.class.getSimpleName())) { // ack message from buyer - if (trade.getTradePeer(sender) == trade.getBuyer()) { + if (peer == trade.getBuyer()) { trade.getBuyer().setPaymentReceivedAckMessage(ackMessage); // handle successful ack @@ -819,7 +837,7 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D } // ack message from arbitrator - else if (trade.getTradePeer(sender) == trade.getArbitrator()) { + else if (peer == trade.getArbitrator()) { trade.getArbitrator().setPaymentReceivedAckMessage(ackMessage); // handle nack @@ -856,6 +874,48 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D trade.onAckMessage(ackMessage, sender); } + private void handleFirstMakerInitTradeRequestNack(AckMessage ackMessage) { + log.warn("Maker received NACK to InitTradeRequest from arbitrator for {} {}, messageUid={}, errorMessage={}", trade.getClass().getSimpleName(), trade.getId(), ackMessage.getSourceUid(), ackMessage.getErrorMessage()); + ThreadUtils.execute(() -> { + Event event = new Event() { + @Override + public String name() { + return "MakerRecreateReserveTx"; + } + }; + synchronized (trade.getLock()) { + latchTrade(); + expect(phase(Trade.Phase.INIT) + .with(event)) + .setup(tasks( + MakerRecreateReserveTx.class, + MakerSendInitTradeRequestToArbitrator.class) + .using(new TradeTaskRunner(trade, + () -> { + startTimeout(); + unlatchTrade(); + }, + errorMessage -> { + handleError("Failed to re-send InitTradeRequest to arbitrator for " + trade.getClass().getSimpleName() + " " + trade.getId() + ": " + errorMessage); + })) + .withTimeout(TRADE_STEP_TIMEOUT_SECONDS)) + .executeTasks(true); + awaitTradeLatch(); + } + }, trade.getId()); + } + + private void handleSecondMakerInitTradeRequestNack(AckMessage ackMessage) { + log.warn("Maker received 2nd NACK to InitTradeRequest from arbitrator for {} {}, messageUid={}, errorMessage={}", trade.getClass().getSimpleName(), trade.getId(), ackMessage.getSourceUid(), ackMessage.getErrorMessage()); + String warningMessage = "Your offer (" + trade.getOffer().getShortId() + ") has been removed because there was a problem taking the trade.\n\nError message: " + ackMessage.getErrorMessage(); + OpenOffer openOffer = HavenoUtils.openOfferManager.getOpenOffer(trade.getId()).orElse(null); + if (openOffer != null) { + HavenoUtils.openOfferManager.cancelOpenOffer(openOffer, null, null); + HavenoUtils.setTopError(warningMessage); + } + log.warn(warningMessage); + } + private boolean isPaymentReceivedMessageAckedByEither() { if (trade.getBuyer().getPaymentReceivedMessageStateProperty().get() == MessageState.ACKNOWLEDGED) return true; if (trade.getArbitrator().getPaymentReceivedMessageStateProperty().get() == MessageState.ACKNOWLEDGED) return true; @@ -992,11 +1052,11 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D void handleTaskRunnerFault(NodeAddress ackReceiver, @Nullable TradeMessage message, String source, String errorMessage, String updatedMultisigHex) { log.error("Task runner failed with error {}. Triggered from {}. Monerod={}" , errorMessage, source, trade.getXmrWalletService().getXmrConnectionService().getConnection()); + handleError(errorMessage); + if (message != null) { sendAckMessage(ackReceiver, message, false, errorMessage, updatedMultisigHex); } - - handleError(errorMessage); } // these are not thread safe, so they must be used within a lock on the trade @@ -1006,9 +1066,9 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D log.error(errorMessage); trade.setErrorMessage(errorMessage); processModel.getTradeManager().requestPersistence(); + unlatchTrade(); if (errorMessageHandler != null) errorMessageHandler.handleErrorMessage(errorMessage); errorMessageHandler = null; - unlatchTrade(); } protected void latchTrade() { diff --git a/core/src/main/java/haveno/core/trade/protocol/tasks/MakerRecreateReserveTx.java b/core/src/main/java/haveno/core/trade/protocol/tasks/MakerRecreateReserveTx.java new file mode 100644 index 0000000000..05130dbbf3 --- /dev/null +++ b/core/src/main/java/haveno/core/trade/protocol/tasks/MakerRecreateReserveTx.java @@ -0,0 +1,147 @@ +/* + * This file is part of Haveno. + * + * Haveno is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at + * your option) any later version. + * + * Haveno is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public + * License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Haveno. If not, see . + */ + +package haveno.core.trade.protocol.tasks; + +import haveno.common.taskrunner.TaskRunner; +import haveno.core.offer.Offer; +import haveno.core.offer.OfferDirection; +import haveno.core.offer.OpenOffer; +import haveno.core.trade.HavenoUtils; +import haveno.core.trade.MakerTrade; +import haveno.core.trade.Trade; +import haveno.core.trade.protocol.TradeProtocol; +import haveno.core.xmr.model.XmrAddressEntry; +import lombok.extern.slf4j.Slf4j; +import monero.common.MoneroRpcConnection; +import monero.wallet.model.MoneroTxWallet; + +import java.math.BigInteger; + +@Slf4j +public class MakerRecreateReserveTx extends TradeTask { + + public MakerRecreateReserveTx(TaskRunner taskHandler, Trade trade) { + super(taskHandler, trade); + } + + @Override + protected void run() { + try { + runInterceptHook(); + + // maker trade expected + if (!(trade instanceof MakerTrade)) { + throw new RuntimeException("Expected maker trade but was " + trade.getClass().getSimpleName() + " " + trade.getShortId() + ". That should never happen."); + } + + // get open offer + OpenOffer openOffer = HavenoUtils.openOfferManager.getOpenOffer(trade.getOffer().getId()).orElse(null); + if (openOffer == null) throw new RuntimeException("Open offer not found for " + trade.getClass().getSimpleName() + " " + trade.getId()); + Offer offer = openOffer.getOffer(); + + // reset reserve tx state + trade.getSelf().setReserveTxHex(null); + trade.getSelf().setReserveTxHash(null); + trade.getSelf().setReserveTxKey(null); + trade.getSelf().setReserveTxKeyImages(null); + + // recreate reserve tx + log.warn("Maker is recreating reserve tx for tradeId={}", trade.getShortId()); + MoneroTxWallet reserveTx = null; + synchronized (HavenoUtils.xmrWalletService.getWalletLock()) { + + // check for timeout + if (isTimedOut()) throw new RuntimeException("Trade protocol has timed out while getting lock to create reserve tx, tradeId=" + trade.getShortId()); + trade.startProtocolTimeout(); + + // thaw reserved key images + log.info("Thawing reserve tx key images for tradeId={}", trade.getShortId()); + HavenoUtils.xmrWalletService.thawOutputs(openOffer.getOffer().getOfferPayload().getReserveTxKeyImages()); + + // check for timeout + if (isTimedOut()) throw new RuntimeException("Trade protocol has timed out while thawing key images, tradeId=" + trade.getShortId()); + trade.startProtocolTimeout(); + + // collect relevant info + BigInteger penaltyFee = HavenoUtils.multiply(offer.getAmount(), offer.getPenaltyFeePct()); + BigInteger makerFee = offer.getMaxMakerFee(); + BigInteger sendAmount = offer.getDirection() == OfferDirection.BUY ? BigInteger.ZERO : offer.getAmount(); + BigInteger securityDeposit = offer.getDirection() == OfferDirection.BUY ? offer.getMaxBuyerSecurityDeposit() : offer.getMaxSellerSecurityDeposit(); + String returnAddress = model.getXmrWalletService().getAddressEntry(offer.getId(), XmrAddressEntry.Context.TRADE_PAYOUT).get().getAddressString(); + XmrAddressEntry fundingEntry = model.getXmrWalletService().getAddressEntry(offer.getId(), XmrAddressEntry.Context.OFFER_FUNDING).orElse(null); + Integer preferredSubaddressIndex = fundingEntry == null ? null : fundingEntry.getSubaddressIndex(); + + // attempt re-creating reserve tx + try { + synchronized (HavenoUtils.getWalletFunctionLock()) { + for (int i = 0; i < TradeProtocol.MAX_ATTEMPTS; i++) { + MoneroRpcConnection sourceConnection = trade.getXmrConnectionService().getConnection(); + try { + reserveTx = model.getXmrWalletService().createReserveTx(penaltyFee, makerFee, sendAmount, securityDeposit, returnAddress, openOffer.isReserveExactAmount(), preferredSubaddressIndex); + } catch (IllegalStateException e) { + log.warn("Illegal state creating reserve tx, tradeId={}, error={}", trade.getShortId(), i + 1, e.getMessage()); + throw e; + } catch (Exception e) { + log.warn("Error creating reserve tx, tradeId={}, attempt={}/{}, error={}", trade.getShortId(), i + 1, TradeProtocol.MAX_ATTEMPTS, e.getMessage()); + trade.getXmrWalletService().handleWalletError(e, sourceConnection); + if (isTimedOut()) throw new RuntimeException("Trade protocol has timed out while creating reserve tx, tradeId=" + trade.getShortId()); + if (i == TradeProtocol.MAX_ATTEMPTS - 1) throw e; + HavenoUtils.waitFor(TradeProtocol.REPROCESS_DELAY_MS); // wait before retrying + } + + // check for timeout + if (isTimedOut()) throw new RuntimeException("Trade protocol has timed out while creating reserve tx, tradeId=" + trade.getShortId()); + if (reserveTx != null) break; + } + } + } catch (Exception e) { + + // reset state + if (reserveTx != null) model.getXmrWalletService().thawOutputs(HavenoUtils.getInputKeyImages(reserveTx)); + model.getXmrWalletService().freezeOutputs(offer.getOfferPayload().getReserveTxKeyImages()); + trade.getSelf().setReserveTxKeyImages(null); + throw e; + } + + // reset protocol timeout + trade.startProtocolTimeout(); + + // update state + trade.getSelf().setReserveTxHash(reserveTx.getHash()); + trade.getSelf().setReserveTxHex(reserveTx.getFullHex()); + trade.getSelf().setReserveTxKey(reserveTx.getKey()); + trade.getSelf().setReserveTxKeyImages(HavenoUtils.getInputKeyImages(reserveTx)); + trade.getXmrWalletService().freezeOutputs(HavenoUtils.getInputKeyImages(reserveTx)); + } + + // save process state + processModel.setReserveTx(reserveTx); // TODO: remove this? how is it used? + processModel.getTradeManager().requestPersistence(); + complete(); + } catch (Throwable t) { + trade.setErrorMessage("An error occurred.\n" + + "Error message:\n" + + t.getMessage()); + failed(t); + } + } + + private boolean isTimedOut() { + return !processModel.getTradeManager().hasOpenTrade(trade); + } +} diff --git a/core/src/main/java/haveno/core/trade/protocol/tasks/TakerReserveTradeFunds.java b/core/src/main/java/haveno/core/trade/protocol/tasks/TakerReserveTradeFunds.java index 9a461b2d83..f7116ee0db 100644 --- a/core/src/main/java/haveno/core/trade/protocol/tasks/TakerReserveTradeFunds.java +++ b/core/src/main/java/haveno/core/trade/protocol/tasks/TakerReserveTradeFunds.java @@ -88,7 +88,7 @@ public class TakerReserveTradeFunds extends TradeTask { } } catch (Exception e) { - // reset state with wallet lock + // reset state model.getXmrWalletService().swapPayoutAddressEntryToAvailable(trade.getId()); if (reserveTx != null) { model.getXmrWalletService().thawOutputs(HavenoUtils.getInputKeyImages(reserveTx)); @@ -101,11 +101,12 @@ public class TakerReserveTradeFunds extends TradeTask { // reset protocol timeout trade.startProtocolTimeout(); - // update trade state - trade.getTaker().setReserveTxHash(reserveTx.getHash()); - trade.getTaker().setReserveTxHex(reserveTx.getFullHex()); - trade.getTaker().setReserveTxKey(reserveTx.getKey()); - trade.getTaker().setReserveTxKeyImages(HavenoUtils.getInputKeyImages(reserveTx)); + // update state + trade.getSelf().setReserveTxHash(reserveTx.getHash()); + trade.getSelf().setReserveTxHex(reserveTx.getFullHex()); + trade.getSelf().setReserveTxKey(reserveTx.getKey()); + trade.getSelf().setReserveTxKeyImages(HavenoUtils.getInputKeyImages(reserveTx)); + trade.getXmrWalletService().freezeOutputs(HavenoUtils.getInputKeyImages(reserveTx)); } } From a4345ae709a16c4d9cd59605e98312ff172a6c69 Mon Sep 17 00:00:00 2001 From: woodser <13068859+woodser@users.noreply.github.com> Date: Tue, 3 Jun 2025 21:44:14 -0400 Subject: [PATCH 52/74] arbitrator verifies offers are public xor no deposit from buyer/taker --- .../src/main/java/haveno/core/offer/OpenOfferManager.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/core/src/main/java/haveno/core/offer/OpenOfferManager.java b/core/src/main/java/haveno/core/offer/OpenOfferManager.java index 49fa770ae2..c3188da86c 100644 --- a/core/src/main/java/haveno/core/offer/OpenOfferManager.java +++ b/core/src/main/java/haveno/core/offer/OpenOfferManager.java @@ -1618,6 +1618,14 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe } } else { + // verify public offer (remove to generally allow private offers) + if (offer.isPrivateOffer() || offer.getChallengeHash() != null) { + errorMessage = "Private offer " + request.offerId + " is not valid. It must have direction SELL, taker fee of 0, and a challenge hash."; + log.warn(errorMessage); + sendAckMessage(request.getClass(), peer, request.getPubKeyRing(), request.getOfferId(), request.getUid(), false, errorMessage); + return; + } + // verify maker's trade fee if (offer.getMakerFeePct() != HavenoUtils.MAKER_FEE_PCT) { errorMessage = "Wrong maker fee for offer " + request.offerId + ". Expected " + HavenoUtils.MAKER_FEE_PCT + " but got " + offer.getMakerFeePct(); From ea536bb4eeb7232c73d3035e947133801a39bca7 Mon Sep 17 00:00:00 2001 From: woodser <13068859+woodser@users.noreply.github.com> Date: Thu, 5 Jun 2025 07:00:49 -0400 Subject: [PATCH 53/74] fix npe on startup routine when monero node is not synced --- .../core/xmr/wallet/XmrWalletService.java | 35 ++++++++++--------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java b/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java index 26f300b7c0..65cd28d3d8 100644 --- a/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java +++ b/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java @@ -1394,10 +1394,10 @@ public class XmrWalletService extends XmrWalletBase { maybeInitMainWallet(sync, MAX_SYNC_ATTEMPTS); } - private void maybeInitMainWallet(boolean sync, int numSyncAttempts) { + private void maybeInitMainWallet(boolean sync, int numSyncAttemptsRemaining) { ThreadUtils.execute(() -> { try { - doMaybeInitMainWallet(sync, MAX_SYNC_ATTEMPTS); + doMaybeInitMainWallet(sync, numSyncAttemptsRemaining); } catch (Exception e) { if (isShutDownStarted) return; log.warn("Error initializing main wallet: {}\n", e.getMessage(), e); @@ -1407,7 +1407,7 @@ public class XmrWalletService extends XmrWalletBase { }, THREAD_ID); } - private void doMaybeInitMainWallet(boolean sync, int numSyncAttempts) { + private void doMaybeInitMainWallet(boolean sync, int numSyncAttemptsRemaining) { synchronized (walletLock) { if (isShutDownStarted) return; @@ -1435,7 +1435,7 @@ public class XmrWalletService extends XmrWalletBase { // sync main wallet if applicable // TODO: error handling and re-initialization is jenky, refactor - if (sync && numSyncAttempts > 0) { + if (sync && numSyncAttemptsRemaining > 0) { try { // switch connection if disconnected @@ -1454,13 +1454,14 @@ public class XmrWalletService extends XmrWalletBase { if (wallet != null) log.warn("Error syncing wallet with progress on startup: " + e.getMessage()); forceCloseMainWallet(); requestSwitchToNextBestConnection(sourceConnection); - maybeInitMainWallet(true, numSyncAttempts - 1); // re-initialize wallet and sync again + maybeInitMainWallet(true, numSyncAttemptsRemaining - 1); // re-initialize wallet and sync again return; } log.info("Done syncing main wallet in " + (System.currentTimeMillis() - time) + " ms"); // poll wallet doPollWallet(true); + if (getBalance() == null) throw new RuntimeException("Balance is null after polling main wallet"); if (walletInitListener != null) xmrConnectionService.downloadPercentageProperty().removeListener(walletInitListener); // log wallet balances @@ -1488,9 +1489,9 @@ public class XmrWalletService extends XmrWalletBase { saveWallet(false); } catch (Exception e) { if (isClosingWallet || isShutDownStarted || HavenoUtils.havenoSetup.getWalletInitialized().get()) return; // ignore if wallet closing, shut down started, or app already initialized - log.warn("Error initially syncing main wallet: {}", e.getMessage()); - if (numSyncAttempts <= 1) { - log.warn("Failed to sync main wallet. Opening app without syncing", numSyncAttempts); + log.warn("Error initially syncing main wallet, numSyncAttemptsRemaining={}", numSyncAttemptsRemaining, e); + if (numSyncAttemptsRemaining <= 1) { + log.warn("Failed to sync main wallet. Opening app without syncing."); HavenoUtils.havenoSetup.getWalletInitialized().set(true); saveWallet(false); @@ -1501,7 +1502,7 @@ public class XmrWalletService extends XmrWalletBase { } else { log.warn("Trying again in {} seconds", xmrConnectionService.getRefreshPeriodMs() / 1000); UserThread.runAfter(() -> { - maybeInitMainWallet(true, numSyncAttempts - 1); + maybeInitMainWallet(true, numSyncAttemptsRemaining - 1); }, xmrConnectionService.getRefreshPeriodMs() / 1000); } } @@ -2063,15 +2064,15 @@ public class XmrWalletService extends XmrWalletBase { } } saveWalletWithDelay(); - } - // cache wallet info last - synchronized (walletLock) { - if (wallet != null && !isShutDownStarted) { - try { - cacheWalletInfo(); - } catch (Exception e) { - log.warn("Error caching wallet info: " + e.getMessage() + "\n", e); + // cache wallet info last + synchronized (walletLock) { + if (wallet != null && !isShutDownStarted) { + try { + cacheWalletInfo(); + } catch (Exception e) { + log.warn("Error caching wallet info: " + e.getMessage() + "\n", e); + } } } } From 8ee1bb372b51addaf60bff0cbce865991d028791 Mon Sep 17 00:00:00 2001 From: woodser <13068859+woodser@users.noreply.github.com> Date: Thu, 5 Jun 2025 10:50:12 -0400 Subject: [PATCH 54/74] fix hanging of pending offer with scheduled txs --- core/src/main/java/haveno/core/offer/OpenOfferManager.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/haveno/core/offer/OpenOfferManager.java b/core/src/main/java/haveno/core/offer/OpenOfferManager.java index c3188da86c..03327b1d09 100644 --- a/core/src/main/java/haveno/core/offer/OpenOfferManager.java +++ b/core/src/main/java/haveno/core/offer/OpenOfferManager.java @@ -1175,9 +1175,10 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe return; } else if (openOffer.getScheduledTxHashes() == null) { scheduleWithEarliestTxs(openOffers, openOffer); - resultHandler.handleResult(null); - return; } + + resultHandler.handleResult(null); + return; } } catch (Exception e) { if (!openOffer.isCanceled()) log.error("Error processing offer: {}\n", e.getMessage(), e); From c239f9aac0c6f955c61783bfd6fa6f42d87fd030 Mon Sep 17 00:00:00 2001 From: woodser <13068859+woodser@users.noreply.github.com> Date: Thu, 5 Jun 2025 19:39:17 -0400 Subject: [PATCH 55/74] make updated multisig hex nullable in dispute closed message --- .../support/dispute/messages/DisputeClosedMessage.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/haveno/core/support/dispute/messages/DisputeClosedMessage.java b/core/src/main/java/haveno/core/support/dispute/messages/DisputeClosedMessage.java index 88a1b6a9df..e4f6828c19 100644 --- a/core/src/main/java/haveno/core/support/dispute/messages/DisputeClosedMessage.java +++ b/core/src/main/java/haveno/core/support/dispute/messages/DisputeClosedMessage.java @@ -35,6 +35,7 @@ import static com.google.common.base.Preconditions.checkArgument; public final class DisputeClosedMessage extends DisputeMessage { private final DisputeResult disputeResult; private final NodeAddress senderNodeAddress; + @Nullable private final String updatedMultisigHex; @Nullable private final String unsignedPayoutTxHex; @@ -44,7 +45,7 @@ public final class DisputeClosedMessage extends DisputeMessage { NodeAddress senderNodeAddress, String uid, SupportType supportType, - String updatedMultisigHex, + @Nullable String updatedMultisigHex, @Nullable String unsignedPayoutTxHex, boolean deferPublishPayout) { this(disputeResult, @@ -85,9 +86,9 @@ public final class DisputeClosedMessage extends DisputeMessage { .setSenderNodeAddress(senderNodeAddress.toProtoMessage()) .setUid(uid) .setType(SupportType.toProtoMessage(supportType)) - .setUpdatedMultisigHex(updatedMultisigHex) .setDeferPublishPayout(deferPublishPayout); Optional.ofNullable(unsignedPayoutTxHex).ifPresent(e -> builder.setUnsignedPayoutTxHex(unsignedPayoutTxHex)); + Optional.ofNullable(updatedMultisigHex).ifPresent(e -> builder.setUpdatedMultisigHex(updatedMultisigHex)); return getNetworkEnvelopeBuilder().setDisputeClosedMessage(builder).build(); } @@ -98,7 +99,7 @@ public final class DisputeClosedMessage extends DisputeMessage { proto.getUid(), messageVersion, SupportType.fromProto(proto.getType()), - proto.getUpdatedMultisigHex(), + ProtoUtil.stringOrNullFromProto(proto.getUpdatedMultisigHex()), ProtoUtil.stringOrNullFromProto(proto.getUnsignedPayoutTxHex()), proto.getDeferPublishPayout()); } From aa1eb70d9a9fb7e13b1ba0b6f72dda4c23f02c3d Mon Sep 17 00:00:00 2001 From: woodser Date: Fri, 6 Jun 2025 08:18:51 -0400 Subject: [PATCH 56/74] major update to look and feel of desktop ui (#1733) --- .../java/haveno/core/util/VolumeUtil.java | 32 + .../resources/i18n/displayStrings.properties | 9 +- .../i18n/displayStrings_cs.properties | 5 +- .../i18n/displayStrings_de.properties | 4 +- .../i18n/displayStrings_es.properties | 4 +- .../i18n/displayStrings_fa.properties | 8 +- .../i18n/displayStrings_fr.properties | 4 +- .../i18n/displayStrings_it.properties | 6 +- .../i18n/displayStrings_ja.properties | 4 +- .../i18n/displayStrings_pt-br.properties | 6 +- .../i18n/displayStrings_pt.properties | 6 +- .../i18n/displayStrings_ru.properties | 8 +- .../i18n/displayStrings_th.properties | 8 +- .../i18n/displayStrings_tr.properties | 6 +- .../i18n/displayStrings_vi.properties | 8 +- .../i18n/displayStrings_zh-hans.properties | 4 +- .../i18n/displayStrings_zh-hant.properties | 6 +- .../java/haveno/desktop/CandleStickChart.css | 4 + .../java/haveno/desktop/app/HavenoApp.java | 4 + .../haveno/desktop/app/HavenoAppMain.java | 5 +- .../desktop/components/AddressTextField.java | 26 +- .../desktop/components/AutoTooltipButton.java | 6 +- .../components/AutocompleteComboBox.java | 27 + .../desktop/components/BalanceTextField.java | 1 + .../components/ExplorerAddressTextField.java | 25 +- .../desktop/components/FundsTextField.java | 20 +- .../desktop/components/HavenoTextField.java | 4 +- .../desktop/components/InfoTextField.java | 4 +- .../desktop/components/InputTextField.java | 2 + .../desktop/components/PeerInfoIcon.java | 5 +- .../components/TextFieldWithCopyIcon.java | 32 +- .../desktop/components/TextFieldWithIcon.java | 5 +- .../desktop/components/TxIdTextField.java | 40 +- .../controlsfx/control/PopOver.java | 2 +- .../desktop/components/list/FilterBox.java | 12 +- .../components/paymentmethods/AssetsForm.java | 3 + .../src/main/java/haveno/desktop/haveno.css | 797 +++++++++++++----- .../src/main/java/haveno/desktop/images.css | 86 +- .../java/haveno/desktop/main/MainView.java | 176 +++- .../desktop/main/account/AccountView.java | 12 +- .../cryptoaccounts/CryptoAccountsView.java | 2 +- .../ManageMarketAlertsWindow.java | 2 + .../TraditionalAccountsView.java | 2 +- .../haveno/desktop/main/funds/FundsView.java | 6 +- .../main/funds/deposit/DepositView.java | 49 +- .../desktop/main/funds/locked/LockedView.java | 3 +- .../main/funds/reserved/ReservedView.java | 4 +- .../funds/transactions/TransactionsView.fxml | 10 +- .../funds/transactions/TransactionsView.java | 14 +- .../main/funds/withdrawal/WithdrawalView.java | 2 +- .../desktop/main/market/MarketView.java | 8 +- .../market/offerbook/OfferBookChartView.java | 82 +- .../offerbook/OfferBookChartViewModel.java | 42 +- .../main/market/spread/SpreadView.java | 8 +- .../main/market/trades/TradesChartsView.java | 7 +- .../desktop/main/offer/MutableOfferView.java | 67 +- .../main/offer/MutableOfferViewModel.java | 6 +- .../haveno/desktop/main/offer/OfferView.java | 8 +- .../main/offer/offerbook/OfferBookView.java | 69 +- .../offer/signedoffer/SignedOfferView.java | 6 +- .../main/offer/takeoffer/TakeOfferView.java | 107 ++- .../offer/takeoffer/TakeOfferViewModel.java | 2 +- .../haveno/desktop/main/overlays/Overlay.java | 76 +- .../main/overlays/editor/PasswordPopup.java | 2 +- .../overlays/notifications/Notification.java | 5 +- .../notifications/NotificationCenter.java | 4 +- .../main/overlays/windows/ContractWindow.java | 35 +- .../windows/DisputeSummaryWindow.java | 4 +- .../windows/GenericMessageWindow.java | 17 +- .../overlays/windows/OfferDetailsWindow.java | 140 ++- .../main/overlays/windows/QRCodeWindow.java | 40 +- .../windows/SignPaymentAccountsWindow.java | 2 +- .../windows/SignSpecificWitnessWindow.java | 1 + .../windows/SignUnsignedPubKeysWindow.java | 1 + .../overlays/windows/TradeDetailsWindow.java | 66 +- .../overlays/windows/TradeFeedbackWindow.java | 13 +- .../overlays/windows/TxDetailsWindow.java | 15 +- .../VerifyDisputeResultSignatureWindow.java | 1 + .../desktop/main/portfolio/PortfolioView.java | 6 +- .../closedtrades/ClosedTradesView.java | 8 +- .../failedtrades/FailedTradesView.java | 4 +- .../portfolio/openoffer/OpenOffersView.fxml | 1 - .../portfolio/openoffer/OpenOffersView.java | 15 +- .../pendingtrades/PendingTradesView.java | 9 +- .../pendingtrades/steps/TradeStepView.java | 1 + .../steps/buyer/BuyerStep4View.java | 2 +- .../desktop/main/settings/SettingsView.java | 6 +- .../main/settings/about/AboutView.java | 12 +- .../settings/network/NetworkSettingsView.fxml | 10 +- .../settings/network/NetworkSettingsView.java | 18 +- .../settings/preferences/PreferencesView.java | 12 +- .../haveno/desktop/main/shared/ChatView.java | 45 +- .../desktop/main/support/SupportView.java | 14 +- .../main/support/dispute/DisputeView.java | 12 +- .../dispute/agent/DisputeAgentView.java | 2 - .../main/java/haveno/desktop/theme-dark.css | 267 +++--- .../main/java/haveno/desktop/theme-light.css | 71 +- .../java/haveno/desktop/util/CssTheme.java | 4 + .../haveno/desktop/util/CurrencyList.java | 11 +- .../java/haveno/desktop/util/FormBuilder.java | 54 +- .../java/haveno/desktop/util/GUIUtil.java | 438 +++++++++- .../main/java/haveno/desktop/util/Layout.java | 7 +- .../java/haveno/desktop/util/Transitions.java | 6 +- desktop/src/main/resources/images/account.png | Bin 0 -> 1014 bytes .../src/main/resources/images/alert_round.png | Bin 895 -> 4964 bytes .../src/main/resources/images/bch_logo.png | Bin 0 -> 30750 bytes ...{blue_circle.png => blue_circle_solid.png} | Bin ...circle@2x.png => blue_circle_solid@2x.png} | Bin .../src/main/resources/images/btc_logo.png | Bin 0 -> 21730 bytes .../main/resources/images/connection/tor.png | Bin 3583 -> 3381 bytes .../resources/images/connection/tor@2x.png | Bin 7469 -> 0 bytes .../images/connection/tor_color@2x.png | Bin 3331 -> 0 bytes .../main/resources/images/dai-erc20_logo.png | Bin 0 -> 73562 bytes .../resources/images/dark_mode_toggle.png | Bin 0 -> 1760 bytes .../src/main/resources/images/eth_logo.png | Bin 0 -> 49471 bytes .../main/resources/images/green_circle.png | Bin 3351 -> 5120 bytes .../resources/images/green_circle_solid.png | Bin 0 -> 3351 bytes ...ircle@2x.png => green_circle_solid@2x.png} | Bin .../resources/images/light_mode_toggle.png | Bin 0 -> 2013 bytes desktop/src/main/resources/images/lock@2x.png | Bin 721 -> 629 bytes .../src/main/resources/images/lock_circle.png | Bin 0 -> 629 bytes .../resources/images/logo_landscape_dark.png | Bin 0 -> 49306 bytes .../resources/images/logo_landscape_light.png | Bin 0 -> 22767 bytes .../src/main/resources/images/logo_splash.png | Bin 21326 -> 0 bytes ...ogo_splash@2x.png => logo_splash_dark.png} | Bin .../resources/images/logo_splash_light.png | Bin 0 -> 30317 bytes .../resources/images/logo_splash_testnet.png | Bin 15693 -> 0 bytes .../images/logo_splash_testnet_dark.png | Bin 0 -> 24129 bytes ...t@2x.png => logo_splash_testnet_light.png} | Bin .../src/main/resources/images/ltc_logo.png | Bin 0 -> 77610 bytes .../resources/images/red_circle_solid.png | Bin 0 -> 895 bytes ...t_round@2x.png => red_circle_solid@2x.png} | Bin .../src/main/resources/images/settings.png | Bin 0 -> 1193 bytes desktop/src/main/resources/images/support.png | Bin 0 -> 728 bytes .../main/resources/images/usdc-erc20_logo.png | Bin 0 -> 120909 bytes .../main/resources/images/usdt-erc20_logo.png | Bin 0 -> 61105 bytes .../main/resources/images/usdt-trc20_logo.png | Bin 0 -> 61105 bytes .../src/main/resources/images/xmr_logo.png | Bin 0 -> 37040 bytes .../main/resources/images/yellow_circle.png | Bin 510 -> 4887 bytes .../resources/images/yellow_circle_solid.png | Bin 0 -> 510 bytes .../java/haveno/desktop/ComponentsDemo.java | 1 + 141 files changed, 2395 insertions(+), 995 deletions(-) create mode 100644 desktop/src/main/resources/images/account.png create mode 100644 desktop/src/main/resources/images/bch_logo.png rename desktop/src/main/resources/images/{blue_circle.png => blue_circle_solid.png} (100%) rename desktop/src/main/resources/images/{blue_circle@2x.png => blue_circle_solid@2x.png} (100%) create mode 100644 desktop/src/main/resources/images/btc_logo.png delete mode 100644 desktop/src/main/resources/images/connection/tor@2x.png delete mode 100644 desktop/src/main/resources/images/connection/tor_color@2x.png create mode 100644 desktop/src/main/resources/images/dai-erc20_logo.png create mode 100644 desktop/src/main/resources/images/dark_mode_toggle.png create mode 100644 desktop/src/main/resources/images/eth_logo.png create mode 100644 desktop/src/main/resources/images/green_circle_solid.png rename desktop/src/main/resources/images/{green_circle@2x.png => green_circle_solid@2x.png} (100%) create mode 100644 desktop/src/main/resources/images/light_mode_toggle.png create mode 100644 desktop/src/main/resources/images/lock_circle.png create mode 100644 desktop/src/main/resources/images/logo_landscape_dark.png create mode 100644 desktop/src/main/resources/images/logo_landscape_light.png delete mode 100644 desktop/src/main/resources/images/logo_splash.png rename desktop/src/main/resources/images/{logo_splash@2x.png => logo_splash_dark.png} (100%) create mode 100644 desktop/src/main/resources/images/logo_splash_light.png delete mode 100644 desktop/src/main/resources/images/logo_splash_testnet.png create mode 100644 desktop/src/main/resources/images/logo_splash_testnet_dark.png rename desktop/src/main/resources/images/{logo_splash_testnet@2x.png => logo_splash_testnet_light.png} (100%) create mode 100644 desktop/src/main/resources/images/ltc_logo.png create mode 100644 desktop/src/main/resources/images/red_circle_solid.png rename desktop/src/main/resources/images/{alert_round@2x.png => red_circle_solid@2x.png} (100%) create mode 100644 desktop/src/main/resources/images/settings.png create mode 100644 desktop/src/main/resources/images/support.png create mode 100644 desktop/src/main/resources/images/usdc-erc20_logo.png create mode 100644 desktop/src/main/resources/images/usdt-erc20_logo.png create mode 100644 desktop/src/main/resources/images/usdt-trc20_logo.png create mode 100644 desktop/src/main/resources/images/xmr_logo.png create mode 100644 desktop/src/main/resources/images/yellow_circle_solid.png diff --git a/core/src/main/java/haveno/core/util/VolumeUtil.java b/core/src/main/java/haveno/core/util/VolumeUtil.java index b74a70f226..050b7b1a4d 100644 --- a/core/src/main/java/haveno/core/util/VolumeUtil.java +++ b/core/src/main/java/haveno/core/util/VolumeUtil.java @@ -51,6 +51,7 @@ import org.bitcoinj.utils.MonetaryFormat; import java.math.BigInteger; import java.text.DecimalFormat; import java.text.NumberFormat; +import java.util.Collection; import java.util.Locale; public class VolumeUtil { @@ -187,4 +188,35 @@ public class VolumeUtil { private static MonetaryFormat getMonetaryFormat(String currencyCode) { return CurrencyUtil.isVolumeRoundedToNearestUnit(currencyCode) ? VOLUME_FORMAT_UNIT : VOLUME_FORMAT_PRECISE; } + + public static Volume sum(Collection volumes) { + if (volumes == null || volumes.isEmpty()) { + return null; + } + Volume sum = null; + for (Volume volume : volumes) { + if (sum == null) { + sum = volume; + } else { + if (!sum.getCurrencyCode().equals(volume.getCurrencyCode())) { + throw new IllegalArgumentException("Cannot sum volumes with different currencies"); + } + sum = add(sum, volume); + } + } + return sum; + } + + public static Volume add(Volume volume1, Volume volume2) { + if (volume1 == null) return volume2; + if (volume2 == null) return volume1; + if (!volume1.getCurrencyCode().equals(volume2.getCurrencyCode())) { + throw new IllegalArgumentException("Cannot add volumes with different currencies"); + } + if (volume1.getMonetary() instanceof CryptoMoney) { + return new Volume(((CryptoMoney) volume1.getMonetary()).add((CryptoMoney) volume2.getMonetary())); + } else { + return new Volume(((TraditionalMoney) volume1.getMonetary()).add((TraditionalMoney) volume2.getMonetary())); + } + } } diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index c214a016dd..a0fd2962b1 100644 --- a/core/src/main/resources/i18n/displayStrings.properties +++ b/core/src/main/resources/i18n/displayStrings.properties @@ -315,9 +315,6 @@ market.tabs.spreadCurrency=Offers by Currency market.tabs.spreadPayment=Offers by Payment Method market.tabs.trades=Trades -# OfferBookView -market.offerBook.filterPrompt=Filter - # OfferBookChartView market.offerBook.sellOffersHeaderLabel=Sell {0} to market.offerBook.buyOffersHeaderLabel=Buy {0} from @@ -410,8 +407,10 @@ shared.notSigned.noNeedAlts=Cryptocurrency accounts do not feature signing or ag offerbook.nrOffers=No. of offers: {0} offerbook.volume={0} (min - max) +offerbook.volumeTotal={0} {1} offerbook.deposit=Deposit XMR (%) offerbook.deposit.help=Deposit paid by each trader to guarantee the trade. Will be returned when the trade is completed. +offerbook.XMRTotal=XMR ({0}) offerbook.createNewOffer=Create offer to {0} {1} offerbook.createOfferDisabled.tooltip=You can only create one offer at a time @@ -1162,8 +1161,6 @@ support.tab.refund.support=Refund support.tab.arbitration.support=Arbitration support.tab.legacyArbitration.support=Legacy Arbitration support.tab.ArbitratorsSupportTickets={0}'s tickets -support.filter=Search disputes -support.filter.prompt=Enter trade ID, date, onion address or account data support.tab.SignedOffers=Signed Offers support.prompt.signedOffer.penalty.msg=This will charge the maker a penalty fee and return the remaining trade funds to their wallet. Are you sure you want to send?\n\n\ Offer ID: {0}\n\ @@ -1337,6 +1334,7 @@ setting.preferences.displayOptions=Display options setting.preferences.showOwnOffers=Show my own offers in offer book setting.preferences.useAnimations=Use animations setting.preferences.useDarkMode=Use dark mode +setting.preferences.useLightMode=Use light mode setting.preferences.sortWithNumOffers=Sort market lists with no. of offers/trades setting.preferences.onlyShowPaymentMethodsFromAccount=Hide non-supported payment methods setting.preferences.denyApiTaker=Deny takers using the API @@ -2012,6 +2010,7 @@ offerDetailsWindow.confirm.takerCrypto=Confirm: Take offer to {0} {1} offerDetailsWindow.creationDate=Creation date offerDetailsWindow.makersOnion=Maker's onion address offerDetailsWindow.challenge=Offer passphrase +offerDetailsWindow.challenge.copy=Copy passphrase to share with your peer qRCodeWindow.headline=QR Code qRCodeWindow.msg=Please use this QR code for funding your Haveno wallet from your external wallet. diff --git a/core/src/main/resources/i18n/displayStrings_cs.properties b/core/src/main/resources/i18n/displayStrings_cs.properties index ce414afabd..53658b4038 100644 --- a/core/src/main/resources/i18n/displayStrings_cs.properties +++ b/core/src/main/resources/i18n/displayStrings_cs.properties @@ -315,7 +315,6 @@ market.tabs.spreadPayment=Nabídky podle způsobů platby market.tabs.trades=Obchody # OfferBookView -market.offerBook.filterPrompt=Filtr # OfferBookChartView market.offerBook.sellOffersHeaderLabel=Prodat {0} kupujícímu @@ -1125,8 +1124,6 @@ support.tab.refund.support=Vrácení peněz support.tab.arbitration.support=Arbitráž support.tab.legacyArbitration.support=Starší arbitráž support.tab.ArbitratorsSupportTickets=Úkoly pro {0} -support.filter=Hledat spory -support.filter.prompt=Zadejte ID obchodu, datum, onion adresu nebo údaje o účtu support.tab.SignedOffers=Podepsané nabídky support.prompt.signedOffer.penalty.msg=Tím se tvůrci účtuje sankční poplatek a zbývající prostředky z obchodu se vrátí do jeho peněženky. Jste si jisti, že chcete odeslat?\n\n\ ID nabídky: {0}\n\ @@ -1300,6 +1297,7 @@ setting.preferences.displayOptions=Zobrazit možnosti setting.preferences.showOwnOffers=Zobrazit mé vlastní nabídky v seznamu nabídek setting.preferences.useAnimations=Použít animace setting.preferences.useDarkMode=Použít tmavý režim +setting.preferences.useLightMode=Použijte světlý režim setting.preferences.sortWithNumOffers=Seřadit seznamy trhů s počtem nabídek/obchodů setting.preferences.onlyShowPaymentMethodsFromAccount=Skrýt nepodporované způsoby platby setting.preferences.denyApiTaker=Odmítat příjemce, kteří používají API @@ -1975,6 +1973,7 @@ offerDetailsWindow.confirm.takerCrypto=Potvrďte: Přijmout nabídku {0} {1} offerDetailsWindow.creationDate=Datum vzniku offerDetailsWindow.makersOnion=Onion adresa tvůrce offerDetailsWindow.challenge=Passphrase nabídky +offerDetailsWindow.challenge.copy=Zkopírujte přístupovou frázi pro sdílení s protějškem qRCodeWindow.headline=QR Kód qRCodeWindow.msg=Použijte tento QR kód k financování vaší peněženky Haveno z vaší externí peněženky. diff --git a/core/src/main/resources/i18n/displayStrings_de.properties b/core/src/main/resources/i18n/displayStrings_de.properties index 43b664b1cf..57fd93df67 100644 --- a/core/src/main/resources/i18n/displayStrings_de.properties +++ b/core/src/main/resources/i18n/displayStrings_de.properties @@ -927,8 +927,6 @@ support.tab.mediation.support=Mediation support.tab.arbitration.support=Vermittlung support.tab.legacyArbitration.support=Legacy-Vermittlung support.tab.ArbitratorsSupportTickets={0} Tickets -support.filter=Konflikte durchsuchen -support.filter.prompt=Tragen sie Handel ID, Datum, Onion Adresse oder Kontodaten support.sigCheck.button=Signatur überprüfen support.sigCheck.popup.info=Fügen Sie die Zusammenfassungsnachricht des Schiedsverfahrens ein. Mit diesem Tool kann jeder Benutzer überprüfen, ob die Unterschrift des Schiedsrichters mit der Zusammenfassungsnachricht übereinstimmt. @@ -1035,6 +1033,7 @@ setting.preferences.displayOptions=Darstellungsoptionen setting.preferences.showOwnOffers=Eigenen Angebote im Angebotsbuch zeigen setting.preferences.useAnimations=Animationen abspielen setting.preferences.useDarkMode=Nacht-Modus benutzen +setting.preferences.useLightMode=Leichtmodus verwenden setting.preferences.sortWithNumOffers=Marktlisten nach Anzahl der Angebote/Trades sortieren setting.preferences.onlyShowPaymentMethodsFromAccount=Nicht unterstützte Zahlungsmethoden ausblenden setting.preferences.denyApiTaker=Taker die das API nutzen vermeiden @@ -1477,6 +1476,7 @@ offerDetailsWindow.confirm.taker=Bestätigen: Angebot annehmen monero zu {0} offerDetailsWindow.creationDate=Erstellungsdatum offerDetailsWindow.makersOnion=Onion-Adresse des Erstellers offerDetailsWindow.challenge=Angebots-Passphrase +offerDetailsWindow.challenge.copy=Passphrase kopieren, um sie mit Ihrem Handelspartner zu teilen qRCodeWindow.headline=QR Code qRCodeWindow.msg=Bitte nutzen Sie diesen QR Code um Ihr Haveno Wallet von Ihrem externen Wallet aufzuladen. diff --git a/core/src/main/resources/i18n/displayStrings_es.properties b/core/src/main/resources/i18n/displayStrings_es.properties index a4001cb075..b5754592dd 100644 --- a/core/src/main/resources/i18n/displayStrings_es.properties +++ b/core/src/main/resources/i18n/displayStrings_es.properties @@ -928,8 +928,6 @@ support.tab.mediation.support=Mediación support.tab.arbitration.support=Arbitraje support.tab.legacyArbitration.support=Legado de arbitraje support.tab.ArbitratorsSupportTickets=Tickets de {0} -support.filter=Buscar disputas -support.filter.prompt=Introduzca ID de transacción, fecha, dirección onion o datos de cuenta. support.sigCheck.button=Comprobar firma support.sigCheck.popup.info=Pegue el mensaje resumido del proceso de arbitraje. Con esta herramienta, cualquier usuario puede verificar si la firma del árbitro coincide con el mensaje resumido. @@ -1036,6 +1034,7 @@ setting.preferences.displayOptions=Mostrar opciones setting.preferences.showOwnOffers=Mostrar mis propias ofertas en el libro de ofertas setting.preferences.useAnimations=Usar animaciones setting.preferences.useDarkMode=Usar modo oscuro +setting.preferences.useLightMode=Usar modo claro setting.preferences.sortWithNumOffers=Ordenar listas de mercado por número de ofertas/intercambios setting.preferences.onlyShowPaymentMethodsFromAccount=Ocultar métodos de pago no soportados setting.preferences.denyApiTaker=Denegar tomadores usando la misma API @@ -1478,6 +1477,7 @@ offerDetailsWindow.confirm.taker=Confirmar: Tomar oferta {0} monero offerDetailsWindow.creationDate=Fecha de creación offerDetailsWindow.makersOnion=Dirección onion del creador offerDetailsWindow.challenge=Frase de contraseña de la oferta +offerDetailsWindow.challenge.copy=Copiar frase de contraseña para compartir con tu contraparte qRCodeWindow.headline=Código QR qRCodeWindow.msg=Por favor, utilice este código QR para fondear su billetera Haveno desde su billetera externa. diff --git a/core/src/main/resources/i18n/displayStrings_fa.properties b/core/src/main/resources/i18n/displayStrings_fa.properties index 891dc297da..44693d7e01 100644 --- a/core/src/main/resources/i18n/displayStrings_fa.properties +++ b/core/src/main/resources/i18n/displayStrings_fa.properties @@ -218,7 +218,7 @@ shared.delayedPayoutTxId=Delayed payout transaction ID shared.delayedPayoutTxReceiverAddress=Delayed payout transaction sent to shared.unconfirmedTransactionsLimitReached=You have too many unconfirmed transactions at the moment. Please try again later. shared.numItemsLabel=Number of entries: {0} -shared.filter=Filter +shared.filter=فیلتر shared.enabled=Enabled @@ -926,8 +926,6 @@ support.tab.mediation.support=Mediation support.tab.arbitration.support=Arbitration support.tab.legacyArbitration.support=Legacy Arbitration support.tab.ArbitratorsSupportTickets={0}'s tickets -support.filter=Search disputes -support.filter.prompt=Enter trade ID, date, onion address or account data support.sigCheck.button=Check signature support.sigCheck.popup.header=Verify dispute result signature @@ -1031,7 +1029,8 @@ setting.preferences.addCrypto=افزودن آلتکوین setting.preferences.displayOptions=نمایش گزینه‌ها setting.preferences.showOwnOffers=نمایش پیشنهادهای من در دفتر پیشنهاد setting.preferences.useAnimations=استفاده از انیمیشن‌ها -setting.preferences.useDarkMode=Use dark mode +setting.preferences.useDarkMode=حالت تاریک را استفاده کنید +setting.preferences.useLightMode=حالت روشن را استفاده کنید setting.preferences.sortWithNumOffers=مرتب سازی لیست‌ها با تعداد معاملات/پیشنهادها setting.preferences.onlyShowPaymentMethodsFromAccount=Hide non-supported payment methods setting.preferences.denyApiTaker=Deny takers using the API @@ -1473,6 +1472,7 @@ offerDetailsWindow.confirm.taker=تأیید: پیشنهاد را به {0} بپذ offerDetailsWindow.creationDate=تاریخ ایجاد offerDetailsWindow.makersOnion=آدرس Onion سفارش گذار offerDetailsWindow.challenge=Passphrase de l'offre +offerDetailsWindow.challenge.copy=عبارت عبور را برای به اشتراک‌گذاری با همتا کپی کنید qRCodeWindow.headline=QR Code qRCodeWindow.msg=Please use this QR code for funding your Haveno wallet from your external wallet. diff --git a/core/src/main/resources/i18n/displayStrings_fr.properties b/core/src/main/resources/i18n/displayStrings_fr.properties index 0a0e1274ad..478ab4c6c3 100644 --- a/core/src/main/resources/i18n/displayStrings_fr.properties +++ b/core/src/main/resources/i18n/displayStrings_fr.properties @@ -929,8 +929,6 @@ support.tab.mediation.support=Médiation support.tab.arbitration.support=Arbitrage support.tab.legacyArbitration.support=Conclusion d'arbitrage support.tab.ArbitratorsSupportTickets=Tickets de {0} -support.filter=Chercher les litiges -support.filter.prompt=Saisissez l'ID du trade, la date, l'adresse "onion" ou les données du compte. support.sigCheck.button=Vérifier la signature support.sigCheck.popup.info=Collez le message récapitulatif du processus d'arbitrage. Avec cet outil, n'importe quel utilisateur peut vérifier si la signature de l'arbitre correspond au message récapitulatif. @@ -1037,6 +1035,7 @@ setting.preferences.displayOptions=Afficher les options setting.preferences.showOwnOffers=Montrer mes ordres dans le livre des ordres setting.preferences.useAnimations=Utiliser des animations setting.preferences.useDarkMode=Utiliser le mode sombre +setting.preferences.useLightMode=Utiliser le mode clair setting.preferences.sortWithNumOffers=Trier les listes de marché avec le nombre d'ordres/de transactions setting.preferences.onlyShowPaymentMethodsFromAccount=Masquer les méthodes de paiement non supportées setting.preferences.denyApiTaker=Refuser les preneurs utilisant l'API @@ -1479,6 +1478,7 @@ offerDetailsWindow.confirm.taker=Confirmer: Acceptez l''ordre de {0} monero offerDetailsWindow.creationDate=Date de création offerDetailsWindow.makersOnion=Adresse onion du maker offerDetailsWindow.challenge=Phrase secrète de l'offre +offerDetailsWindow.challenge.copy=Copier la phrase secrète à partager avec votre pair qRCodeWindow.headline=QR Code qRCodeWindow.msg=Veuillez utiliser le code QR pour recharger du portefeuille externe au portefeuille Haveno. diff --git a/core/src/main/resources/i18n/displayStrings_it.properties b/core/src/main/resources/i18n/displayStrings_it.properties index 7847646806..784b65234f 100644 --- a/core/src/main/resources/i18n/displayStrings_it.properties +++ b/core/src/main/resources/i18n/displayStrings_it.properties @@ -218,7 +218,7 @@ shared.delayedPayoutTxId=Delayed payout transaction ID shared.delayedPayoutTxReceiverAddress=Delayed payout transaction sent to shared.unconfirmedTransactionsLimitReached=Al momento, hai troppe transazioni non confermate. Per favore riprova più tardi. shared.numItemsLabel=Number of entries: {0} -shared.filter=Filter +shared.filter=Filtro shared.enabled=Enabled @@ -927,8 +927,6 @@ support.tab.mediation.support=Mediazione support.tab.arbitration.support=Arbitrato support.tab.legacyArbitration.support=Arbitrato Legacy support.tab.ArbitratorsSupportTickets=I ticket di {0} -support.filter=Search disputes -support.filter.prompt=Inserisci ID commerciale, data, indirizzo onion o dati dell'account support.sigCheck.button=Check signature support.sigCheck.popup.header=Verify dispute result signature @@ -1034,6 +1032,7 @@ setting.preferences.displayOptions=Mostra opzioni setting.preferences.showOwnOffers=Mostra le mie offerte nel libro delle offerte setting.preferences.useAnimations=Usa animazioni setting.preferences.useDarkMode=Usa modalità notte +setting.preferences.useLightMode=Usa la modalità chiara setting.preferences.sortWithNumOffers=Ordina le liste di mercato con n. di offerte/scambi setting.preferences.onlyShowPaymentMethodsFromAccount=Hide non-supported payment methods setting.preferences.denyApiTaker=Deny takers using the API @@ -1476,6 +1475,7 @@ offerDetailsWindow.confirm.taker=Conferma: Accetta l'offerta a {0} monero offerDetailsWindow.creationDate=Data di creazione offerDetailsWindow.makersOnion=Indirizzo .onion del maker offerDetailsWindow.challenge=Passphrase dell'offerta +offerDetailsWindow.challenge.copy=Copia la frase segreta da condividere con il tuo interlocutore qRCodeWindow.headline=QR Code qRCodeWindow.msg=Please use this QR code for funding your Haveno wallet from your external wallet. diff --git a/core/src/main/resources/i18n/displayStrings_ja.properties b/core/src/main/resources/i18n/displayStrings_ja.properties index 9036bd8e2d..ccc24ab657 100644 --- a/core/src/main/resources/i18n/displayStrings_ja.properties +++ b/core/src/main/resources/i18n/displayStrings_ja.properties @@ -927,8 +927,6 @@ support.tab.mediation.support=調停 support.tab.arbitration.support=仲裁 support.tab.legacyArbitration.support=レガシー仲裁 support.tab.ArbitratorsSupportTickets={0} のチケット -support.filter=係争を検索 -support.filter.prompt=トレードID、日付、onionアドレスまたはアカウントデータを入力してください support.sigCheck.button=Check signature support.sigCheck.popup.info=仲裁プロセスの要約メッセージを貼り付けてください。このツールを使用すると、どんなユーザーでも仲裁者の署名が要約メッセージと一致するかどうかを確認できます。 @@ -1035,6 +1033,7 @@ setting.preferences.displayOptions=表示設定 setting.preferences.showOwnOffers=オファーブックに自分のオファーを表示 setting.preferences.useAnimations=アニメーションを使用 setting.preferences.useDarkMode=ダークモードを利用 +setting.preferences.useLightMode=ライトモードを使用する setting.preferences.sortWithNumOffers=市場リストをオファー/トレードの数で並び替える setting.preferences.onlyShowPaymentMethodsFromAccount=サポートされていない支払い方法を非表示にする setting.preferences.denyApiTaker=APIを使用するテイカーを拒否する @@ -1477,6 +1476,7 @@ offerDetailsWindow.confirm.taker=承認: ビットコインを{0}オファーを offerDetailsWindow.creationDate=作成日 offerDetailsWindow.makersOnion=メイカーのonionアドレス offerDetailsWindow.challenge=オファーパスフレーズ +offerDetailsWindow.challenge.copy=ピアと共有するためにパスフレーズをコピーする qRCodeWindow.headline=QRコード qRCodeWindow.msg=外部ウォレットからHavenoウォレットへ送金するのに、このQRコードを利用して下さい。 diff --git a/core/src/main/resources/i18n/displayStrings_pt-br.properties b/core/src/main/resources/i18n/displayStrings_pt-br.properties index f6520e8436..c28a530456 100644 --- a/core/src/main/resources/i18n/displayStrings_pt-br.properties +++ b/core/src/main/resources/i18n/displayStrings_pt-br.properties @@ -221,7 +221,7 @@ shared.delayedPayoutTxId=Delayed payout transaction ID shared.delayedPayoutTxReceiverAddress=Delayed payout transaction sent to shared.unconfirmedTransactionsLimitReached=No momento, você possui muitas transações não-confirmadas. Tente novamente mais tarde. shared.numItemsLabel=Number of entries: {0} -shared.filter=Filter +shared.filter=Filtro shared.enabled=Enabled @@ -929,8 +929,6 @@ support.tab.mediation.support=Mediação support.tab.arbitration.support=Arbitragem support.tab.legacyArbitration.support=Arbitração antiga support.tab.ArbitratorsSupportTickets=Tickets de {0} -support.filter=Search disputes -support.filter.prompt=Insira ID da negociação. data. endereço onion ou dados da conta support.sigCheck.button=Check signature support.sigCheck.popup.header=Verify dispute result signature @@ -1036,6 +1034,7 @@ setting.preferences.displayOptions=Opções de exibição setting.preferences.showOwnOffers=Exibir minhas ofertas no livro de ofertas setting.preferences.useAnimations=Usar animações setting.preferences.useDarkMode=Usar modo escuro +setting.preferences.useLightMode=Usar modo claro setting.preferences.sortWithNumOffers=Ordenar pelo nº de ofertas/negociações setting.preferences.onlyShowPaymentMethodsFromAccount=Hide non-supported payment methods setting.preferences.denyApiTaker=Deny takers using the API @@ -1480,6 +1479,7 @@ offerDetailsWindow.confirm.taker=Confirmar: Aceitar oferta de {0} monero offerDetailsWindow.creationDate=Criada em offerDetailsWindow.makersOnion=Endereço onion do ofertante offerDetailsWindow.challenge=Passphrase da oferta +offerDetailsWindow.challenge.copy=Copiar frase secreta para compartilhar com seu par qRCodeWindow.headline=QR Code qRCodeWindow.msg=Please use this QR code for funding your Haveno wallet from your external wallet. diff --git a/core/src/main/resources/i18n/displayStrings_pt.properties b/core/src/main/resources/i18n/displayStrings_pt.properties index c69236f8b1..d14b41668c 100644 --- a/core/src/main/resources/i18n/displayStrings_pt.properties +++ b/core/src/main/resources/i18n/displayStrings_pt.properties @@ -218,7 +218,7 @@ shared.delayedPayoutTxId=Delayed payout transaction ID shared.delayedPayoutTxReceiverAddress=Delayed payout transaction sent to shared.unconfirmedTransactionsLimitReached=You have too many unconfirmed transactions at the moment. Please try again later. shared.numItemsLabel=Number of entries: {0} -shared.filter=Filter +shared.filter=Filtro shared.enabled=Enabled @@ -926,8 +926,6 @@ support.tab.mediation.support=Mediação support.tab.arbitration.support=Arbitragem support.tab.legacyArbitration.support=Arbitragem Antiga support.tab.ArbitratorsSupportTickets=Bilhetes de {0} -support.filter=Search disputes -support.filter.prompt=Insira o ID do negócio, data, endereço onion ou dados da conta support.sigCheck.button=Check signature support.sigCheck.popup.header=Verify dispute result signature @@ -1033,6 +1031,7 @@ setting.preferences.displayOptions=Mostrar opções setting.preferences.showOwnOffers=Mostrar as minhas próprias ofertas no livro de ofertas setting.preferences.useAnimations=Usar animações setting.preferences.useDarkMode=Usar o modo escuro +setting.preferences.useLightMode=Usar modo claro setting.preferences.sortWithNumOffers=Ordenar listas de mercado por nº de ofertas/negociações: setting.preferences.onlyShowPaymentMethodsFromAccount=Hide non-supported payment methods setting.preferences.denyApiTaker=Deny takers using the API @@ -1473,6 +1472,7 @@ offerDetailsWindow.confirm.taker=Confirmar: Aceitar oferta de {0} monero offerDetailsWindow.creationDate=Data de criação offerDetailsWindow.makersOnion=Endereço onion do ofertante offerDetailsWindow.challenge=Passphrase da oferta +offerDetailsWindow.challenge.copy=Copiar frase secreta para compartilhar com seu parceiro qRCodeWindow.headline=QR Code qRCodeWindow.msg=Please use this QR code for funding your Haveno wallet from your external wallet. diff --git a/core/src/main/resources/i18n/displayStrings_ru.properties b/core/src/main/resources/i18n/displayStrings_ru.properties index f5ded7cdb2..848b765ae2 100644 --- a/core/src/main/resources/i18n/displayStrings_ru.properties +++ b/core/src/main/resources/i18n/displayStrings_ru.properties @@ -218,7 +218,7 @@ shared.delayedPayoutTxId=Delayed payout transaction ID shared.delayedPayoutTxReceiverAddress=Delayed payout transaction sent to shared.unconfirmedTransactionsLimitReached=You have too many unconfirmed transactions at the moment. Please try again later. shared.numItemsLabel=Number of entries: {0} -shared.filter=Filter +shared.filter=Фильтр shared.enabled=Enabled @@ -926,8 +926,6 @@ support.tab.mediation.support=Mediation support.tab.arbitration.support=Arbitration support.tab.legacyArbitration.support=Legacy Arbitration support.tab.ArbitratorsSupportTickets={0}'s tickets -support.filter=Search disputes -support.filter.prompt=Введите идентификатор сделки, дату, onion-адрес или данные учётной записи support.sigCheck.button=Check signature support.sigCheck.popup.header=Verify dispute result signature @@ -1031,7 +1029,8 @@ setting.preferences.addCrypto=Добавить альткойн setting.preferences.displayOptions=Параметры отображения setting.preferences.showOwnOffers=Показать мои предложения в списке предложений setting.preferences.useAnimations=Использовать анимацию -setting.preferences.useDarkMode=Use dark mode +setting.preferences.useDarkMode=Использовать тёмный режим +setting.preferences.useLightMode=Использовать светлый режим setting.preferences.sortWithNumOffers=Сортировать списки по кол-ву предложений/сделок setting.preferences.onlyShowPaymentMethodsFromAccount=Hide non-supported payment methods setting.preferences.denyApiTaker=Deny takers using the API @@ -1474,6 +1473,7 @@ offerDetailsWindow.confirm.taker=Подтвердите: принять пред offerDetailsWindow.creationDate=Дата создания offerDetailsWindow.makersOnion=Onion-адрес мейкера offerDetailsWindow.challenge=Пароль предложения +offerDetailsWindow.challenge.copy=Скопируйте кодовую фразу, чтобы поделиться с партнёром qRCodeWindow.headline=QR Code qRCodeWindow.msg=Please use this QR code for funding your Haveno wallet from your external wallet. diff --git a/core/src/main/resources/i18n/displayStrings_th.properties b/core/src/main/resources/i18n/displayStrings_th.properties index d2d89f3b83..df37fc3f28 100644 --- a/core/src/main/resources/i18n/displayStrings_th.properties +++ b/core/src/main/resources/i18n/displayStrings_th.properties @@ -218,7 +218,7 @@ shared.delayedPayoutTxId=Delayed payout transaction ID shared.delayedPayoutTxReceiverAddress=Delayed payout transaction sent to shared.unconfirmedTransactionsLimitReached=You have too many unconfirmed transactions at the moment. Please try again later. shared.numItemsLabel=Number of entries: {0} -shared.filter=Filter +shared.filter=ตัวกรอง shared.enabled=Enabled @@ -926,8 +926,6 @@ support.tab.mediation.support=Mediation support.tab.arbitration.support=Arbitration support.tab.legacyArbitration.support=Legacy Arbitration support.tab.ArbitratorsSupportTickets={0}'s tickets -support.filter=Search disputes -support.filter.prompt=Enter trade ID, date, onion address or account data support.sigCheck.button=Check signature support.sigCheck.popup.header=Verify dispute result signature @@ -1031,7 +1029,8 @@ setting.preferences.addCrypto=เพิ่ม crypto setting.preferences.displayOptions=แสดงตัวเลือกเพิ่มเติม setting.preferences.showOwnOffers=แสดงข้อเสนอของฉันเองในสมุดข้อเสนอ setting.preferences.useAnimations=ใช้ภาพเคลื่อนไหว -setting.preferences.useDarkMode=Use dark mode +setting.preferences.useDarkMode=ใช้โหมดมืด +setting.preferences.useLightMode=ใช้โหมดสว่าง setting.preferences.sortWithNumOffers=จัดเรียงรายการโดยเลขของข้อเสนอ / การซื้อขาย setting.preferences.onlyShowPaymentMethodsFromAccount=Hide non-supported payment methods setting.preferences.denyApiTaker=Deny takers using the API @@ -1474,6 +1473,7 @@ offerDetailsWindow.confirm.taker=ยืนยัน: รับข้อเสน offerDetailsWindow.creationDate=วันที่สร้าง offerDetailsWindow.makersOnion=ที่อยู่ onion ของผู้สร้าง offerDetailsWindow.challenge=รหัสผ่านสำหรับข้อเสนอ +offerDetailsWindow.challenge.copy=คัดลอกวลีรหัสเพื่อแชร์กับเพื่อนของคุณ qRCodeWindow.headline=QR Code qRCodeWindow.msg=Please use this QR code for funding your Haveno wallet from your external wallet. diff --git a/core/src/main/resources/i18n/displayStrings_tr.properties b/core/src/main/resources/i18n/displayStrings_tr.properties index 426c99500f..9b7f12e466 100644 --- a/core/src/main/resources/i18n/displayStrings_tr.properties +++ b/core/src/main/resources/i18n/displayStrings_tr.properties @@ -231,7 +231,7 @@ shared.delayedPayoutTxId=Gecikmiş ödeme işlem kimliği shared.delayedPayoutTxReceiverAddress=Gecikmiş ödeme işlemi gönderildi shared.unconfirmedTransactionsLimitReached=Şu anda çok fazla onaylanmamış işleminiz var. Lütfen daha sonra tekrar deneyin. shared.numItemsLabel=Girdi sayısı: {0} -shared.filter=Filtrele +shared.filter=Filtre shared.enabled=Etkin shared.pending=Beklemede shared.me=Ben @@ -1120,8 +1120,6 @@ support.tab.refund.support=Geri Ödeme support.tab.arbitration.support=Arbitraj support.tab.legacyArbitration.support=Eski Arbitraj support.tab.ArbitratorsSupportTickets={0}'nin biletleri -support.filter=Uyuşmazlıkları ara -support.filter.prompt=İşlem ID'si, tarih, onion adresi veya hesap verilerini girin support.tab.SignedOffers=İmzalı Teklifler support.prompt.signedOffer.penalty.msg=Bu, üreticiden bir ceza ücreti alacak ve kalan işlem fonlarını cüzdanına iade edecektir. Göndermek istediğinizden emin misiniz?\n\n\ Teklif ID'si: {0}\n\ @@ -1295,6 +1293,7 @@ setting.preferences.displayOptions=Görüntüleme seçenekleri setting.preferences.showOwnOffers=Teklif defterinde kendi tekliflerini göster setting.preferences.useAnimations=Animasyonları kullan setting.preferences.useDarkMode=Karanlık modu kullan +setting.preferences.useLightMode=Aydınlık modu kullan setting.preferences.sortWithNumOffers=Piyasaları teklif sayısına göre sırala setting.preferences.onlyShowPaymentMethodsFromAccount=Olmayan ödeme yöntemlerini gizle setting.preferences.denyApiTaker=API kullanan alıcıları reddet @@ -1970,6 +1969,7 @@ offerDetailsWindow.confirm.takerCrypto=Onayla: {0} {1} teklifi al offerDetailsWindow.creationDate=Oluşturma tarihi offerDetailsWindow.makersOnion=Yapıcı'nın onion adresi offerDetailsWindow.challenge=Teklif şifresi +offerDetailsWindow.challenge.copy=Parolanızı eşinizle paylaşmak için kopyalayın qRCodeWindow.headline=QR Kodu qRCodeWindow.msg=Harici cüzdanınızdan Haveno cüzdanınızı finanse etmek için bu QR kodunu kullanın. diff --git a/core/src/main/resources/i18n/displayStrings_vi.properties b/core/src/main/resources/i18n/displayStrings_vi.properties index 4df8f44b16..52854a4208 100644 --- a/core/src/main/resources/i18n/displayStrings_vi.properties +++ b/core/src/main/resources/i18n/displayStrings_vi.properties @@ -218,7 +218,7 @@ shared.delayedPayoutTxId=Delayed payout transaction ID shared.delayedPayoutTxReceiverAddress=Delayed payout transaction sent to shared.unconfirmedTransactionsLimitReached=You have too many unconfirmed transactions at the moment. Please try again later. shared.numItemsLabel=Number of entries: {0} -shared.filter=Filter +shared.filter=Bộ lọc shared.enabled=Enabled @@ -928,8 +928,6 @@ support.tab.mediation.support=Mediation support.tab.arbitration.support=Arbitration support.tab.legacyArbitration.support=Legacy Arbitration support.tab.ArbitratorsSupportTickets={0}'s tickets -support.filter=Search disputes -support.filter.prompt=Nhập ID giao dịch, ngày tháng, địa chỉ onion hoặc dữ liệu tài khoản support.sigCheck.button=Check signature support.sigCheck.popup.header=Verify dispute result signature @@ -1033,7 +1031,8 @@ setting.preferences.addCrypto=Bổ sung crypto setting.preferences.displayOptions=Hiển thị các phương án setting.preferences.showOwnOffers=Hiển thị Báo giá của tôi trong danh mục Báo giá setting.preferences.useAnimations=Sử dụng hoạt ảnh -setting.preferences.useDarkMode=Use dark mode +setting.preferences.useDarkMode=Sử dụng chế độ tối +setting.preferences.useLightMode=Sử dụng chế độ sáng setting.preferences.sortWithNumOffers=Sắp xếp danh sách thị trường với số chào giá/giao dịch setting.preferences.onlyShowPaymentMethodsFromAccount=Hide non-supported payment methods setting.preferences.denyApiTaker=Deny takers using the API @@ -1476,6 +1475,7 @@ offerDetailsWindow.confirm.taker=Xác nhận: Nhận chào giáo cho {0} monero offerDetailsWindow.creationDate=Ngày tạo offerDetailsWindow.makersOnion=Địa chỉ onion của người tạo offerDetailsWindow.challenge=Mã bảo vệ giao dịch +offerDetailsWindow.challenge.copy=Sao chép cụm mật khẩu để chia sẻ với đối tác của bạn qRCodeWindow.headline=QR Code qRCodeWindow.msg=Please use this QR code for funding your Haveno wallet from your external wallet. diff --git a/core/src/main/resources/i18n/displayStrings_zh-hans.properties b/core/src/main/resources/i18n/displayStrings_zh-hans.properties index 1e65527036..7e6b9be4d5 100644 --- a/core/src/main/resources/i18n/displayStrings_zh-hans.properties +++ b/core/src/main/resources/i18n/displayStrings_zh-hans.properties @@ -927,8 +927,6 @@ support.tab.mediation.support=调解 support.tab.arbitration.support=仲裁 support.tab.legacyArbitration.support=历史仲裁 support.tab.ArbitratorsSupportTickets={0} 的工单 -support.filter=查找纠纷 -support.filter.prompt=输入 交易 ID、日期、洋葱地址或账户信息 support.sigCheck.button=Check signature support.sigCheck.popup.info=请粘贴仲裁过程的摘要信息。使用这个工具,任何用户都可以检查仲裁者的签名是否与摘要信息相符。 @@ -1035,6 +1033,7 @@ setting.preferences.displayOptions=显示选项 setting.preferences.showOwnOffers=在报价列表中显示我的报价 setting.preferences.useAnimations=使用动画 setting.preferences.useDarkMode=使用夜间模式 +setting.preferences.useLightMode=使用浅色模式 setting.preferences.sortWithNumOffers=使用“报价ID/交易ID”筛选列表 setting.preferences.onlyShowPaymentMethodsFromAccount=Hide non-supported payment methods setting.preferences.denyApiTaker=Deny takers using the API @@ -1478,6 +1477,7 @@ offerDetailsWindow.confirm.taker=确定:下单买入 {0} 比特币 offerDetailsWindow.creationDate=创建时间 offerDetailsWindow.makersOnion=卖家的匿名地址 offerDetailsWindow.challenge=提供密码 +offerDetailsWindow.challenge.copy=复制助记词以与您的交易对手共享 qRCodeWindow.headline=二维码 qRCodeWindow.msg=请使用二维码从外部钱包充值至 Haveno 钱包 diff --git a/core/src/main/resources/i18n/displayStrings_zh-hant.properties b/core/src/main/resources/i18n/displayStrings_zh-hant.properties index 05f8bb5e8d..a1694e3999 100644 --- a/core/src/main/resources/i18n/displayStrings_zh-hant.properties +++ b/core/src/main/resources/i18n/displayStrings_zh-hant.properties @@ -218,7 +218,7 @@ shared.delayedPayoutTxId=延遲支付交易 ID shared.delayedPayoutTxReceiverAddress=延遲交易交易已發送至 shared.unconfirmedTransactionsLimitReached=你現在有過多的未確認交易。請稍後嘗試 shared.numItemsLabel=Number of entries: {0} -shared.filter=Filter +shared.filter=篩選 shared.enabled=啟用 @@ -927,8 +927,6 @@ support.tab.mediation.support=調解 support.tab.arbitration.support=仲裁 support.tab.legacyArbitration.support=歷史仲裁 support.tab.ArbitratorsSupportTickets={0} 的工單 -support.filter=查找糾紛 -support.filter.prompt=輸入 交易 ID、日期、洋葱地址或賬户信息 support.sigCheck.button=Check signature support.sigCheck.popup.info=請貼上仲裁程序的摘要訊息。利用這個工具,任何使用者都可以檢查仲裁者的簽名是否與摘要訊息相符。 @@ -1035,6 +1033,7 @@ setting.preferences.displayOptions=顯示選項 setting.preferences.showOwnOffers=在報價列表中顯示我的報價 setting.preferences.useAnimations=使用動畫 setting.preferences.useDarkMode=使用夜間模式 +setting.preferences.useLightMode=使用淺色模式 setting.preferences.sortWithNumOffers=使用“報價ID/交易ID”篩選列表 setting.preferences.onlyShowPaymentMethodsFromAccount=Hide non-supported payment methods setting.preferences.denyApiTaker=Deny takers using the API @@ -1478,6 +1477,7 @@ offerDetailsWindow.confirm.taker=確定:下單買入 {0} 比特幣 offerDetailsWindow.creationDate=創建時間 offerDetailsWindow.makersOnion=賣家的匿名地址 offerDetailsWindow.challenge=提供密碼 +offerDetailsWindow.challenge.copy=複製密語以與對方分享 qRCodeWindow.headline=二維碼 qRCodeWindow.msg=請使用二維碼從外部錢包充值至 Haveno 錢包 diff --git a/desktop/src/main/java/haveno/desktop/CandleStickChart.css b/desktop/src/main/java/haveno/desktop/CandleStickChart.css index 376e0db666..4e5a05d7ea 100644 --- a/desktop/src/main/java/haveno/desktop/CandleStickChart.css +++ b/desktop/src/main/java/haveno/desktop/CandleStickChart.css @@ -62,6 +62,8 @@ -demo-bar-fill: -bs-sell; -fx-background-color: -demo-bar-fill; -fx-background-insets: 0; + -fx-background-radius: 2px; + -fx-border-radius: 2px; } .candlestick-bar.close-above-open { @@ -80,6 +82,8 @@ -fx-padding: 5; -fx-background-color: -bs-volume-transparent; -fx-background-insets: 0; + -fx-background-radius: 2px; + -fx-border-radius: 2px; } .chart-alternative-row-fill { diff --git a/desktop/src/main/java/haveno/desktop/app/HavenoApp.java b/desktop/src/main/java/haveno/desktop/app/HavenoApp.java index 16370c2cdb..41b5ad09bc 100644 --- a/desktop/src/main/java/haveno/desktop/app/HavenoApp.java +++ b/desktop/src/main/java/haveno/desktop/app/HavenoApp.java @@ -74,6 +74,7 @@ import javafx.scene.Scene; import javafx.scene.input.KeyCode; import javafx.scene.input.KeyEvent; import javafx.scene.layout.StackPane; +import javafx.scene.paint.Color; import javafx.stage.Modality; import javafx.stage.Screen; import javafx.stage.Stage; @@ -223,6 +224,9 @@ public class HavenoApp extends Application implements UncaughtExceptionHandler { CssTheme.loadSceneStyles(scene, preferences.getCssTheme(), config.useDevModeHeader); }); CssTheme.loadSceneStyles(scene, preferences.getCssTheme(), config.useDevModeHeader); + + // set initial background color + scene.setFill(CssTheme.isDarkTheme() ? Color.BLACK : Color.WHITE); return scene; } diff --git a/desktop/src/main/java/haveno/desktop/app/HavenoAppMain.java b/desktop/src/main/java/haveno/desktop/app/HavenoAppMain.java index ed7e956eba..73e78ab6d7 100644 --- a/desktop/src/main/java/haveno/desktop/app/HavenoAppMain.java +++ b/desktop/src/main/java/haveno/desktop/app/HavenoAppMain.java @@ -216,7 +216,10 @@ public class HavenoAppMain extends HavenoExecutable { // Set the dialog content VBox vbox = new VBox(10); - vbox.getChildren().addAll(new ImageView(ImageUtil.getImageByPath("logo_splash.png")), passwordField, errorMessageField, versionField); + ImageView logoImageView = new ImageView(ImageUtil.getImageByPath("logo_splash_light.png")); + logoImageView.setFitWidth(342); + logoImageView.setPreserveRatio(true); + vbox.getChildren().addAll(logoImageView, passwordField, errorMessageField, versionField); vbox.setAlignment(Pos.TOP_CENTER); getDialogPane().setContent(vbox); diff --git a/desktop/src/main/java/haveno/desktop/components/AddressTextField.java b/desktop/src/main/java/haveno/desktop/components/AddressTextField.java index ec6f284379..8edfbd222a 100644 --- a/desktop/src/main/java/haveno/desktop/components/AddressTextField.java +++ b/desktop/src/main/java/haveno/desktop/components/AddressTextField.java @@ -20,14 +20,17 @@ package haveno.desktop.components; import com.jfoenix.controls.JFXTextField; import de.jensd.fx.fontawesome.AwesomeDude; import de.jensd.fx.fontawesome.AwesomeIcon; +import haveno.common.UserThread; import haveno.common.util.Utilities; import haveno.core.locale.Res; import haveno.desktop.main.overlays.popups.Popup; import haveno.desktop.util.GUIUtil; +import haveno.desktop.util.Layout; import javafx.beans.property.ObjectProperty; import javafx.beans.property.SimpleObjectProperty; import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; +import javafx.scene.Node; import javafx.scene.control.Label; import javafx.scene.control.Tooltip; import javafx.scene.layout.AnchorPane; @@ -55,6 +58,7 @@ public class AddressTextField extends AnchorPane { textField.setId("address-text-field"); textField.setEditable(false); textField.setLabelFloat(true); + textField.getStyleClass().add("label-float"); textField.setPromptText(label); textField.textProperty().bind(address); @@ -70,28 +74,32 @@ public class AddressTextField extends AnchorPane { textField.focusTraversableProperty().set(focusTraversableProperty().get()); Label extWalletIcon = new Label(); - extWalletIcon.setLayoutY(3); + extWalletIcon.setLayoutY(Layout.FLOATING_ICON_Y); extWalletIcon.getStyleClass().addAll("icon", "highlight"); extWalletIcon.setTooltip(new Tooltip(tooltipText)); AwesomeDude.setIcon(extWalletIcon, AwesomeIcon.SIGNIN); extWalletIcon.setOnMouseClicked(e -> openWallet()); - Label copyIcon = new Label(); - copyIcon.setLayoutY(3); - copyIcon.getStyleClass().addAll("icon", "highlight"); - Tooltip.install(copyIcon, new Tooltip(Res.get("addressTextField.copyToClipboard"))); - AwesomeDude.setIcon(copyIcon, AwesomeIcon.COPY); - copyIcon.setOnMouseClicked(e -> { + Label copyLabel = new Label(); + copyLabel.setLayoutY(Layout.FLOATING_ICON_Y); + copyLabel.getStyleClass().addAll("icon", "highlight"); + Tooltip.install(copyLabel, new Tooltip(Res.get("addressTextField.copyToClipboard"))); + copyLabel.setGraphic(GUIUtil.getCopyIcon()); + copyLabel.setOnMouseClicked(e -> { if (address.get() != null && address.get().length() > 0) Utilities.copyToClipboard(address.get()); + Tooltip tp = new Tooltip(Res.get("shared.copiedToClipboard")); + Node node = (Node) e.getSource(); + UserThread.runAfter(() -> tp.hide(), 1); + tp.show(node, e.getScreenX() + Layout.PADDING, e.getScreenY() + Layout.PADDING); }); - AnchorPane.setRightAnchor(copyIcon, 30.0); + AnchorPane.setRightAnchor(copyLabel, 30.0); AnchorPane.setRightAnchor(extWalletIcon, 5.0); AnchorPane.setRightAnchor(textField, 55.0); AnchorPane.setLeftAnchor(textField, 0.0); - getChildren().addAll(textField, copyIcon, extWalletIcon); + getChildren().addAll(textField, copyLabel, extWalletIcon); } private void openWallet() { diff --git a/desktop/src/main/java/haveno/desktop/components/AutoTooltipButton.java b/desktop/src/main/java/haveno/desktop/components/AutoTooltipButton.java index 83261ba212..11521e93a1 100644 --- a/desktop/src/main/java/haveno/desktop/components/AutoTooltipButton.java +++ b/desktop/src/main/java/haveno/desktop/components/AutoTooltipButton.java @@ -31,15 +31,15 @@ public class AutoTooltipButton extends JFXButton { } public AutoTooltipButton(String text) { - super(text.toUpperCase()); + super(text); } public AutoTooltipButton(String text, Node graphic) { - super(text.toUpperCase(), graphic); + super(text, graphic); } public void updateText(String text) { - setText(text.toUpperCase()); + setText(text); } @Override diff --git a/desktop/src/main/java/haveno/desktop/components/AutocompleteComboBox.java b/desktop/src/main/java/haveno/desktop/components/AutocompleteComboBox.java index 2b1adc5769..e6701cdb4c 100644 --- a/desktop/src/main/java/haveno/desktop/components/AutocompleteComboBox.java +++ b/desktop/src/main/java/haveno/desktop/components/AutocompleteComboBox.java @@ -24,6 +24,7 @@ import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.event.Event; import javafx.event.EventHandler; +import javafx.scene.control.ListView; import javafx.scene.input.KeyCode; import javafx.scene.input.KeyEvent; import org.apache.commons.lang3.StringUtils; @@ -44,6 +45,7 @@ public class AutocompleteComboBox extends JFXComboBox { private List extendedList; private List matchingList; private JFXComboBoxListViewSkin comboBoxListViewSkin; + private boolean selectAllShortcut = false; public AutocompleteComboBox() { this(FXCollections.observableArrayList()); @@ -153,6 +155,27 @@ public class AutocompleteComboBox extends JFXComboBox { private void reactToQueryChanges() { getEditor().addEventHandler(KeyEvent.KEY_RELEASED, (KeyEvent event) -> { + + // ignore ctrl and command keys + if (event.getCode() == KeyCode.CONTROL || event.getCode() == KeyCode.COMMAND || event.getCode() == KeyCode.META) { + event.consume(); + return; + } + + // handle select all + boolean isSelectAll = event.getCode() == KeyCode.A && (event.isControlDown() || event.isMetaDown()); + if (isSelectAll) { + getEditor().selectAll(); + selectAllShortcut = true; + event.consume(); + return; + } + if (event.getCode() == KeyCode.A && selectAllShortcut) { // 'A' can be received after ctrl/cmd + selectAllShortcut = false; + event.consume(); + return; + } + UserThread.execute(() -> { String query = getEditor().getText(); var exactMatch = list.stream().anyMatch(item -> asString(item).equalsIgnoreCase(query)); @@ -180,6 +203,10 @@ public class AutocompleteComboBox extends JFXComboBox { if (matchingListSize() > 0) { comboBoxListViewSkin.getPopupContent().autosize(); show(); + if (comboBoxListViewSkin.getPopupContent() instanceof ListView listView) { + listView.applyCss(); + listView.layout(); + } } else { hide(); } diff --git a/desktop/src/main/java/haveno/desktop/components/BalanceTextField.java b/desktop/src/main/java/haveno/desktop/components/BalanceTextField.java index 4e00789dc8..7775b0b812 100644 --- a/desktop/src/main/java/haveno/desktop/components/BalanceTextField.java +++ b/desktop/src/main/java/haveno/desktop/components/BalanceTextField.java @@ -47,6 +47,7 @@ public class BalanceTextField extends AnchorPane { public BalanceTextField(String label) { textField = new HavenoTextField(); textField.setLabelFloat(true); + textField.getStyleClass().add("label-float"); textField.setPromptText(label); textField.setFocusTraversable(false); textField.setEditable(false); diff --git a/desktop/src/main/java/haveno/desktop/components/ExplorerAddressTextField.java b/desktop/src/main/java/haveno/desktop/components/ExplorerAddressTextField.java index 7dff009b9e..7e31272ca8 100644 --- a/desktop/src/main/java/haveno/desktop/components/ExplorerAddressTextField.java +++ b/desktop/src/main/java/haveno/desktop/components/ExplorerAddressTextField.java @@ -23,6 +23,7 @@ import de.jensd.fx.fontawesome.AwesomeIcon; import haveno.common.util.Utilities; import haveno.core.locale.Res; import haveno.core.user.Preferences; +import haveno.desktop.util.GUIUtil; import javafx.scene.control.Label; import javafx.scene.control.TextField; import javafx.scene.control.Tooltip; @@ -38,19 +39,19 @@ public class ExplorerAddressTextField extends AnchorPane { @Getter private final TextField textField; - private final Label copyIcon, missingAddressWarningIcon; + private final Label copyLabel, missingAddressWarningIcon; /////////////////////////////////////////////////////////////////////////////////////////// // Constructor /////////////////////////////////////////////////////////////////////////////////////////// public ExplorerAddressTextField() { - copyIcon = new Label(); - copyIcon.setLayoutY(3); - copyIcon.getStyleClass().addAll("icon", "highlight"); - copyIcon.setTooltip(new Tooltip(Res.get("explorerAddressTextField.copyToClipboard"))); - AwesomeDude.setIcon(copyIcon, AwesomeIcon.COPY); - AnchorPane.setRightAnchor(copyIcon, 30.0); + copyLabel = new Label(); + copyLabel.setLayoutY(3); + copyLabel.getStyleClass().addAll("icon", "highlight"); + copyLabel.setTooltip(new Tooltip(Res.get("explorerAddressTextField.copyToClipboard"))); + copyLabel.setGraphic(GUIUtil.getCopyIcon()); + AnchorPane.setRightAnchor(copyLabel, 30.0); Tooltip tooltip = new Tooltip(Res.get("explorerAddressTextField.blockExplorerIcon.tooltip")); @@ -71,27 +72,27 @@ public class ExplorerAddressTextField extends AnchorPane { AnchorPane.setRightAnchor(textField, 80.0); AnchorPane.setLeftAnchor(textField, 0.0); textField.focusTraversableProperty().set(focusTraversableProperty().get()); - getChildren().addAll(textField, missingAddressWarningIcon, copyIcon); + getChildren().addAll(textField, missingAddressWarningIcon, copyLabel); } public void setup(@Nullable String address) { if (address == null) { textField.setText(Res.get("shared.na")); textField.setId("address-text-field-error"); - copyIcon.setVisible(false); - copyIcon.setManaged(false); + copyLabel.setVisible(false); + copyLabel.setManaged(false); missingAddressWarningIcon.setVisible(true); missingAddressWarningIcon.setManaged(true); return; } textField.setText(address); - copyIcon.setOnMouseClicked(e -> Utilities.copyToClipboard(address)); + copyLabel.setOnMouseClicked(e -> Utilities.copyToClipboard(address)); } public void cleanup() { textField.setOnMouseClicked(null); - copyIcon.setOnMouseClicked(null); + copyLabel.setOnMouseClicked(null); textField.setText(""); } } diff --git a/desktop/src/main/java/haveno/desktop/components/FundsTextField.java b/desktop/src/main/java/haveno/desktop/components/FundsTextField.java index 0804750972..aa01093469 100644 --- a/desktop/src/main/java/haveno/desktop/components/FundsTextField.java +++ b/desktop/src/main/java/haveno/desktop/components/FundsTextField.java @@ -17,9 +17,10 @@ package haveno.desktop.components; -import de.jensd.fx.fontawesome.AwesomeIcon; import haveno.common.util.Utilities; import haveno.core.locale.Res; +import haveno.desktop.util.GUIUtil; +import haveno.desktop.util.Layout; import javafx.beans.binding.Bindings; import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; @@ -29,8 +30,6 @@ import javafx.scene.layout.AnchorPane; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import static haveno.desktop.util.FormBuilder.getIcon; - public class FundsTextField extends InfoTextField { public static final Logger log = LoggerFactory.getLogger(FundsTextField.class); @@ -46,11 +45,12 @@ public class FundsTextField extends InfoTextField { textField.textProperty().unbind(); textField.textProperty().bind(Bindings.concat(textProperty())); // TODO: removed `, " ", fundsStructure` for haveno to fix "Funds needed: .123 XMR (null)" bug - Label copyIcon = getIcon(AwesomeIcon.COPY); - copyIcon.setLayoutY(5); - copyIcon.getStyleClass().addAll("icon", "highlight"); - Tooltip.install(copyIcon, new Tooltip(Res.get("shared.copyToClipboard"))); - copyIcon.setOnMouseClicked(e -> { + Label copyLabel = new Label(); + copyLabel.setLayoutY(Layout.FLOATING_ICON_Y); + copyLabel.getStyleClass().addAll("icon", "highlight"); + Tooltip.install(copyLabel, new Tooltip(Res.get("shared.copyToClipboard"))); + copyLabel.setGraphic(GUIUtil.getCopyIcon()); + copyLabel.setOnMouseClicked(e -> { String text = getText(); if (text != null && text.length() > 0) { String copyText; @@ -64,11 +64,11 @@ public class FundsTextField extends InfoTextField { } }); - AnchorPane.setRightAnchor(copyIcon, 30.0); + AnchorPane.setRightAnchor(copyLabel, 30.0); AnchorPane.setRightAnchor(infoIcon, 62.0); AnchorPane.setRightAnchor(textField, 55.0); - getChildren().add(copyIcon); + getChildren().add(copyLabel); } /////////////////////////////////////////////////////////////////////////////////////////// diff --git a/desktop/src/main/java/haveno/desktop/components/HavenoTextField.java b/desktop/src/main/java/haveno/desktop/components/HavenoTextField.java index 96c9e2571d..bb8c24eecf 100644 --- a/desktop/src/main/java/haveno/desktop/components/HavenoTextField.java +++ b/desktop/src/main/java/haveno/desktop/components/HavenoTextField.java @@ -1,16 +1,18 @@ package haveno.desktop.components; import com.jfoenix.controls.JFXTextField; +import haveno.desktop.util.GUIUtil; import javafx.scene.control.Skin; public class HavenoTextField extends JFXTextField { public HavenoTextField(String value) { super(value); + GUIUtil.applyFilledStyle(this); } public HavenoTextField() { - super(); + this(null); } @Override diff --git a/desktop/src/main/java/haveno/desktop/components/InfoTextField.java b/desktop/src/main/java/haveno/desktop/components/InfoTextField.java index beafcf9494..7e47b9339f 100644 --- a/desktop/src/main/java/haveno/desktop/components/InfoTextField.java +++ b/desktop/src/main/java/haveno/desktop/components/InfoTextField.java @@ -21,6 +21,7 @@ import com.jfoenix.controls.JFXTextField; import de.jensd.fx.fontawesome.AwesomeIcon; import de.jensd.fx.glyphs.materialdesignicons.MaterialDesignIcon; import haveno.desktop.components.controlsfx.control.PopOver; +import haveno.desktop.util.Layout; import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; import javafx.scene.Node; @@ -51,13 +52,14 @@ public class InfoTextField extends AnchorPane { arrowLocation = PopOver.ArrowLocation.RIGHT_TOP; textField = new HavenoTextField(); textField.setLabelFloat(true); + textField.getStyleClass().add("label-float"); textField.setEditable(false); textField.textProperty().bind(text); textField.setFocusTraversable(false); textField.setId("info-field"); infoIcon = getIcon(AwesomeIcon.INFO_SIGN); - infoIcon.setLayoutY(5); + infoIcon.setLayoutY(Layout.FLOATING_ICON_Y - 2); infoIcon.getStyleClass().addAll("icon", "info"); AnchorPane.setRightAnchor(infoIcon, 7.0); diff --git a/desktop/src/main/java/haveno/desktop/components/InputTextField.java b/desktop/src/main/java/haveno/desktop/components/InputTextField.java index 8c4ace02b8..b46e7f84b1 100644 --- a/desktop/src/main/java/haveno/desktop/components/InputTextField.java +++ b/desktop/src/main/java/haveno/desktop/components/InputTextField.java @@ -20,6 +20,7 @@ package haveno.desktop.components; import com.jfoenix.controls.JFXTextField; import haveno.core.util.validation.InputValidator; +import haveno.desktop.util.GUIUtil; import haveno.desktop.util.validation.JFXInputValidator; import javafx.beans.property.ObjectProperty; import javafx.beans.property.SimpleObjectProperty; @@ -67,6 +68,7 @@ public class InputTextField extends JFXTextField { public InputTextField() { super(); + GUIUtil.applyFilledStyle(this); getValidators().add(jfxValidationWrapper); diff --git a/desktop/src/main/java/haveno/desktop/components/PeerInfoIcon.java b/desktop/src/main/java/haveno/desktop/components/PeerInfoIcon.java index da8dcb4a70..af854c437f 100644 --- a/desktop/src/main/java/haveno/desktop/components/PeerInfoIcon.java +++ b/desktop/src/main/java/haveno/desktop/components/PeerInfoIcon.java @@ -124,7 +124,8 @@ public class PeerInfoIcon extends Group { numTradesPane.relocate(scaleFactor * 18, scaleFactor * 14); numTradesPane.setMouseTransparent(true); ImageView numTradesCircle = new ImageView(); - numTradesCircle.setId("image-green_circle"); + numTradesCircle.setId("image-green_circle_solid"); + numTradesLabel = new AutoTooltipLabel(); numTradesLabel.relocate(scaleFactor * 5, scaleFactor * 1); numTradesLabel.setId("ident-num-label"); @@ -134,7 +135,7 @@ public class PeerInfoIcon extends Group { tagPane.relocate(Math.round(scaleFactor * 18), scaleFactor * -2); tagPane.setMouseTransparent(true); ImageView tagCircle = new ImageView(); - tagCircle.setId("image-blue_circle"); + tagCircle.setId("image-blue_circle_solid"); tagLabel = new AutoTooltipLabel(); tagLabel.relocate(Math.round(scaleFactor * 5), scaleFactor * 1); tagLabel.setId("ident-num-label"); diff --git a/desktop/src/main/java/haveno/desktop/components/TextFieldWithCopyIcon.java b/desktop/src/main/java/haveno/desktop/components/TextFieldWithCopyIcon.java index d337916da3..1dbd47eaaa 100644 --- a/desktop/src/main/java/haveno/desktop/components/TextFieldWithCopyIcon.java +++ b/desktop/src/main/java/haveno/desktop/components/TextFieldWithCopyIcon.java @@ -18,12 +18,15 @@ package haveno.desktop.components; import com.jfoenix.controls.JFXTextField; -import de.jensd.fx.fontawesome.AwesomeDude; -import de.jensd.fx.fontawesome.AwesomeIcon; + +import haveno.common.UserThread; import haveno.common.util.Utilities; import haveno.core.locale.Res; +import haveno.desktop.util.GUIUtil; +import haveno.desktop.util.Layout; import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; +import javafx.scene.Node; import javafx.scene.control.Label; import javafx.scene.control.TextField; import javafx.scene.control.Tooltip; @@ -45,12 +48,13 @@ public class TextFieldWithCopyIcon extends AnchorPane { } public TextFieldWithCopyIcon(String customStyleClass) { - Label copyIcon = new Label(); - copyIcon.setLayoutY(3); - copyIcon.getStyleClass().addAll("icon", "highlight"); - copyIcon.setTooltip(new Tooltip(Res.get("shared.copyToClipboard"))); - AwesomeDude.setIcon(copyIcon, AwesomeIcon.COPY); - copyIcon.setOnMouseClicked(e -> { + Label copyLabel = new Label(); + copyLabel.setLayoutY(Layout.FLOATING_ICON_Y); + copyLabel.getStyleClass().addAll("icon", "highlight"); + if (customStyleClass != null) copyLabel.getStyleClass().add(customStyleClass + "-icon"); + copyLabel.setTooltip(new Tooltip(Res.get("shared.copyToClipboard"))); + copyLabel.setGraphic(GUIUtil.getCopyIcon()); + copyLabel.setOnMouseClicked(e -> { String text = getText(); if (text != null && text.length() > 0) { String copyText; @@ -70,17 +74,25 @@ public class TextFieldWithCopyIcon extends AnchorPane { copyText = text; } Utilities.copyToClipboard(copyText); + Tooltip tp = new Tooltip(Res.get("shared.copiedToClipboard")); + Node node = (Node) e.getSource(); + UserThread.runAfter(() -> tp.hide(), 1); + tp.show(node, e.getScreenX() + Layout.PADDING, e.getScreenY() + Layout.PADDING); } }); textField = new JFXTextField(); textField.setEditable(false); if (customStyleClass != null) textField.getStyleClass().add(customStyleClass); textField.textProperty().bindBidirectional(text); - AnchorPane.setRightAnchor(copyIcon, 5.0); + AnchorPane.setRightAnchor(copyLabel, 5.0); AnchorPane.setRightAnchor(textField, 30.0); AnchorPane.setLeftAnchor(textField, 0.0); + AnchorPane.setTopAnchor(copyLabel, 0.0); + AnchorPane.setBottomAnchor(copyLabel, 0.0); + AnchorPane.setTopAnchor(textField, 0.0); + AnchorPane.setBottomAnchor(textField, 0.0); textField.focusTraversableProperty().set(focusTraversableProperty().get()); - getChildren().addAll(textField, copyIcon); + getChildren().addAll(textField, copyLabel); } public void setPromptText(String value) { diff --git a/desktop/src/main/java/haveno/desktop/components/TextFieldWithIcon.java b/desktop/src/main/java/haveno/desktop/components/TextFieldWithIcon.java index 2e66a0e026..1db14cf73a 100644 --- a/desktop/src/main/java/haveno/desktop/components/TextFieldWithIcon.java +++ b/desktop/src/main/java/haveno/desktop/components/TextFieldWithIcon.java @@ -21,6 +21,7 @@ import com.jfoenix.controls.JFXTextField; import de.jensd.fx.fontawesome.AwesomeDude; import de.jensd.fx.fontawesome.AwesomeIcon; import haveno.common.UserThread; +import haveno.desktop.util.Layout; import javafx.geometry.Pos; import javafx.scene.control.Label; import javafx.scene.control.TextField; @@ -53,10 +54,10 @@ public class TextFieldWithIcon extends AnchorPane { iconLabel = new Label(); iconLabel.setLayoutX(0); - iconLabel.setLayoutY(3); + iconLabel.setLayoutY(Layout.FLOATING_ICON_Y); dummyTextField.widthProperty().addListener((observable, oldValue, newValue) -> { - iconLabel.setLayoutX(dummyTextField.widthProperty().get() + 20); + iconLabel.setLayoutX(dummyTextField.widthProperty().get() + 20 + Layout.FLOATING_ICON_Y); }); getChildren().addAll(textField, dummyTextField, iconLabel); diff --git a/desktop/src/main/java/haveno/desktop/components/TxIdTextField.java b/desktop/src/main/java/haveno/desktop/components/TxIdTextField.java index e9ced56cdf..2a55dba7b0 100644 --- a/desktop/src/main/java/haveno/desktop/components/TxIdTextField.java +++ b/desktop/src/main/java/haveno/desktop/components/TxIdTextField.java @@ -29,7 +29,10 @@ import haveno.core.user.Preferences; import haveno.core.xmr.wallet.XmrWalletService; import haveno.desktop.components.indicator.TxConfidenceIndicator; import haveno.desktop.util.GUIUtil; +import haveno.desktop.util.Layout; import javafx.beans.value.ChangeListener; +import javafx.scene.Cursor; +import javafx.scene.Node; import javafx.scene.control.Label; import javafx.scene.control.TextField; import javafx.scene.control.Tooltip; @@ -51,7 +54,7 @@ public class TxIdTextField extends AnchorPane { private final TextField textField; private final Tooltip progressIndicatorTooltip; private final TxConfidenceIndicator txConfidenceIndicator; - private final Label copyIcon, blockExplorerIcon, missingTxWarningIcon; + private final Label copyLabel, blockExplorerIcon, missingTxWarningIcon; private MoneroWalletListener walletListener; private ChangeListener tradeListener; @@ -70,16 +73,17 @@ public class TxIdTextField extends AnchorPane { txConfidenceIndicator.setProgress(0); txConfidenceIndicator.setVisible(false); AnchorPane.setRightAnchor(txConfidenceIndicator, 0.0); - AnchorPane.setTopAnchor(txConfidenceIndicator, 3.0); + AnchorPane.setTopAnchor(txConfidenceIndicator, Layout.FLOATING_ICON_Y); progressIndicatorTooltip = new Tooltip("-"); txConfidenceIndicator.setTooltip(progressIndicatorTooltip); - copyIcon = new Label(); - copyIcon.setLayoutY(3); - copyIcon.getStyleClass().addAll("icon", "highlight"); - copyIcon.setTooltip(new Tooltip(Res.get("txIdTextField.copyIcon.tooltip"))); - AwesomeDude.setIcon(copyIcon, AwesomeIcon.COPY); - AnchorPane.setRightAnchor(copyIcon, 30.0); + copyLabel = new Label(); + copyLabel.setLayoutY(Layout.FLOATING_ICON_Y); + copyLabel.getStyleClass().addAll("icon", "highlight"); + copyLabel.setTooltip(new Tooltip(Res.get("txIdTextField.copyIcon.tooltip"))); + copyLabel.setGraphic(GUIUtil.getCopyIcon()); + copyLabel.setCursor(Cursor.HAND); + AnchorPane.setRightAnchor(copyLabel, 30.0); Tooltip tooltip = new Tooltip(Res.get("txIdTextField.blockExplorerIcon.tooltip")); @@ -89,7 +93,7 @@ public class TxIdTextField extends AnchorPane { AwesomeDude.setIcon(blockExplorerIcon, AwesomeIcon.EXTERNAL_LINK); blockExplorerIcon.setMinWidth(20); AnchorPane.setRightAnchor(blockExplorerIcon, 52.0); - AnchorPane.setTopAnchor(blockExplorerIcon, 4.0); + AnchorPane.setTopAnchor(blockExplorerIcon, Layout.FLOATING_ICON_Y); missingTxWarningIcon = new Label(); missingTxWarningIcon.getStyleClass().addAll("icon", "error-icon"); @@ -97,7 +101,7 @@ public class TxIdTextField extends AnchorPane { missingTxWarningIcon.setTooltip(new Tooltip(Res.get("txIdTextField.missingTx.warning.tooltip"))); missingTxWarningIcon.setMinWidth(20); AnchorPane.setRightAnchor(missingTxWarningIcon, 52.0); - AnchorPane.setTopAnchor(missingTxWarningIcon, 4.0); + AnchorPane.setTopAnchor(missingTxWarningIcon, Layout.FLOATING_ICON_Y); missingTxWarningIcon.setVisible(false); missingTxWarningIcon.setManaged(false); @@ -108,7 +112,7 @@ public class TxIdTextField extends AnchorPane { AnchorPane.setRightAnchor(textField, 80.0); AnchorPane.setLeftAnchor(textField, 0.0); textField.focusTraversableProperty().set(focusTraversableProperty().get()); - getChildren().addAll(textField, missingTxWarningIcon, blockExplorerIcon, copyIcon, txConfidenceIndicator); + getChildren().addAll(textField, missingTxWarningIcon, blockExplorerIcon, copyLabel, txConfidenceIndicator); } public void setup(@Nullable String txId) { @@ -131,8 +135,8 @@ public class TxIdTextField extends AnchorPane { textField.setId("address-text-field-error"); blockExplorerIcon.setVisible(false); blockExplorerIcon.setManaged(false); - copyIcon.setVisible(false); - copyIcon.setManaged(false); + copyLabel.setVisible(false); + copyLabel.setManaged(false); txConfidenceIndicator.setVisible(false); missingTxWarningIcon.setVisible(true); missingTxWarningIcon.setManaged(true); @@ -158,7 +162,13 @@ public class TxIdTextField extends AnchorPane { textField.setText(txId); textField.setOnMouseClicked(mouseEvent -> openBlockExplorer(txId)); blockExplorerIcon.setOnMouseClicked(mouseEvent -> openBlockExplorer(txId)); - copyIcon.setOnMouseClicked(e -> Utilities.copyToClipboard(txId)); + copyLabel.setOnMouseClicked(e -> { + Utilities.copyToClipboard(txId); + Tooltip tp = new Tooltip(Res.get("shared.copiedToClipboard")); + Node node = (Node) e.getSource(); + UserThread.runAfter(() -> tp.hide(), 1); + tp.show(node, e.getScreenX() + Layout.PADDING, e.getScreenY() + Layout.PADDING); + }); txConfidenceIndicator.setVisible(true); // update off main thread @@ -177,7 +187,7 @@ public class TxIdTextField extends AnchorPane { trade = null; textField.setOnMouseClicked(null); blockExplorerIcon.setOnMouseClicked(null); - copyIcon.setOnMouseClicked(null); + copyLabel.setOnMouseClicked(null); textField.setText(""); } diff --git a/desktop/src/main/java/haveno/desktop/components/controlsfx/control/PopOver.java b/desktop/src/main/java/haveno/desktop/components/controlsfx/control/PopOver.java index f04dee5960..85b7eaceba 100644 --- a/desktop/src/main/java/haveno/desktop/components/controlsfx/control/PopOver.java +++ b/desktop/src/main/java/haveno/desktop/components/controlsfx/control/PopOver.java @@ -494,7 +494,7 @@ public class PopOver extends PopupControl { * @since 1.0 */ public final void hide(Duration fadeOutDuration) { - log.info("hide:" + fadeOutDuration.toString()); + log.debug("hide:" + fadeOutDuration.toString()); //We must remove EventFilter in order to prevent memory leak. if (ownerWindow != null) { ownerWindow.removeEventFilter(WindowEvent.WINDOW_CLOSE_REQUEST, diff --git a/desktop/src/main/java/haveno/desktop/components/list/FilterBox.java b/desktop/src/main/java/haveno/desktop/components/list/FilterBox.java index 9bed491f49..adb4707564 100644 --- a/desktop/src/main/java/haveno/desktop/components/list/FilterBox.java +++ b/desktop/src/main/java/haveno/desktop/components/list/FilterBox.java @@ -17,13 +17,10 @@ package haveno.desktop.components.list; -import haveno.core.locale.Res; -import haveno.desktop.components.AutoTooltipLabel; import haveno.desktop.components.InputTextField; import haveno.desktop.util.filtering.FilterableListItem; import javafx.beans.value.ChangeListener; import javafx.collections.transformation.FilteredList; -import javafx.geometry.Insets; import javafx.scene.control.TableView; import javafx.scene.layout.HBox; @@ -37,13 +34,10 @@ public class FilterBox extends HBox { super(); setSpacing(5.0); - AutoTooltipLabel label = new AutoTooltipLabel(Res.get("shared.filter")); - HBox.setMargin(label, new Insets(5.0, 0, 0, 10.0)); - textField = new InputTextField(); textField.setMinWidth(500); - getChildren().addAll(label, textField); + getChildren().addAll(textField); } public void initialize(FilteredList filteredList, @@ -67,4 +61,8 @@ public class FilterBox extends HBox { private void applyFilteredListPredicate(String filterString) { filteredList.setPredicate(item -> item.match(filterString)); } + + public void setPromptText(String promptText) { + textField.setPromptText(promptText); + } } diff --git a/desktop/src/main/java/haveno/desktop/components/paymentmethods/AssetsForm.java b/desktop/src/main/java/haveno/desktop/components/paymentmethods/AssetsForm.java index f57c3f154f..7baa76c564 100644 --- a/desktop/src/main/java/haveno/desktop/components/paymentmethods/AssetsForm.java +++ b/desktop/src/main/java/haveno/desktop/components/paymentmethods/AssetsForm.java @@ -36,6 +36,7 @@ import haveno.desktop.components.AutocompleteComboBox; import haveno.desktop.components.InputTextField; import haveno.desktop.main.overlays.popups.Popup; import haveno.desktop.util.FormBuilder; +import haveno.desktop.util.GUIUtil; import haveno.desktop.util.Layout; import javafx.geometry.Insets; import javafx.scene.control.CheckBox; @@ -202,6 +203,8 @@ public class AssetsForm extends PaymentMethodForm { CurrencyUtil.getActiveSortedCryptoCurrencies(filterManager)); currencyComboBox.setVisibleRowCount(Math.min(currencyComboBox.getItems().size(), 10)); + currencyComboBox.setCellFactory(GUIUtil.getTradeCurrencyCellFactoryNameAndCode()); + currencyComboBox.setConverter(new StringConverter<>() { @Override public String toString(TradeCurrency tradeCurrency) { diff --git a/desktop/src/main/java/haveno/desktop/haveno.css b/desktop/src/main/java/haveno/desktop/haveno.css index e3cfac8c0e..7c24526aaf 100644 --- a/desktop/src/main/java/haveno/desktop/haveno.css +++ b/desktop/src/main/java/haveno/desktop/haveno.css @@ -39,7 +39,7 @@ -fx-text-fill: -bs-color-primary; } -.highlight, .highlight-static { +.highlight, .highlight-static, .highlight.label .glyph-icon { -fx-text-fill: -fx-accent; -fx-fill: -fx-accent; } @@ -105,6 +105,11 @@ -fx-font-size: 1.077em; -fx-font-family: "IBM Plex Mono"; -fx-padding: 0 !important; + -fx-border-width: 0; + -fx-text-fill: -bs-rd-font-dark-gray !important; +} + +.confirmation-text-field-as-label-icon { } /* Other UI Elements */ @@ -150,9 +155,9 @@ -fx-text-fill: -bs-rd-font-dark-gray; -fx-font-size: 0.923em; -fx-font-weight: normal; - -fx-background-radius: 2px; - -fx-pref-height: 32; - -fx-min-height: -fx-pref-height; + -fx-background-radius: 999; + -fx-border-radius: 999; + -fx-min-height: 32; -fx-padding: 0 40 0 40; -fx-effect: dropshadow(gaussian, -bs-text-color-transparent, 2, 0, 0, 0, 1); -fx-cursor: hand; @@ -171,8 +176,12 @@ -fx-text-fill: -bs-background-color; } -.compact-button, .table-cell .jfx-button, .action-button.compact-button { - -fx-padding: 0 10 0 10; +.action-button.compact-button, .compact-button { + -fx-padding: 0 15 0 15; +} + +.table-cell .jfx-button { + -fx-padding: 0 7 0 7; } .tiny-button, @@ -217,10 +226,59 @@ -fx-border-width: 1; } +.jfx-combo-box, .jfx-text-field, .jfx-text-area, .jfx-password-field, .toggle-button-no-slider { + -fx-padding: 7 14 7 14; + -fx-background-radius: 999; + -fx-border-radius: 999; + -fx-border-color: transparent; +} + .jfx-combo-box { - -jfx-focus-color: -bs-color-primary; - -jfx-unfocus-color: -bs-color-gray-line; - -fx-background-color: -bs-background-color; + -fx-background-color: -bs-color-background-form-field; +} + +.input-line, .input-focused-line { + -fx-background-color: transparent; + visibility: hidden; + -fx-max-height: 0; +} + +.jfx-text-field { + -fx-background-radius: 999; + -fx-border-radius: 999; + -fx-background-color: -bs-color-background-form-field; +} + +.jfx-text-field.label-float .prompt-container { + -fx-translate-y: 0px; +} + +.jfx-text-field.filled.label-float .prompt-container, +.jfx-text-field.label-float:focused .prompt-container, +.jfx-combo-box.filled.label-float .prompt-container, +.jfx-combo-box.label-float:focused .prompt-container, +.jfx-password-field.filled.label-float .prompt-container, +.jfx-password-field.label-float:focused .prompt-container { + -fx-translate-x: -14px; + -fx-translate-y: -5.5px; +} + +.jfx-combo-box .arrow-button { + -fx-background-radius: 999; + -fx-border-radius: 999; + -fx-padding: 0 0 0 10; +} + +.jfx-combo-box:hover { + -fx-cursor: hand; +} + +.jfx-combo-box:editable:hover { + -fx-cursor: null; +} + +.jfx-combo-box .arrow-button:hover { + -fx-cursor: hand; } .jfx-combo-box > .list-cell { @@ -228,15 +286,66 @@ -fx-font-family: "IBM Plex Sans Medium"; } +/* TODO: otherwise combo box with "odd" class is opacity 0.4? */ +.jfx-combo-box > .list-cell:odd, .jfx-combo-box > .list-cell:even { + -fx-opacity: 1.0; +} + +.jfx-combo-box > .list-cell, +.jfx-combo-box > .text-field { + -fx-padding: 0 !important; +} + .jfx-combo-box > .arrow-button > .arrow { -fx-background-color: null; - -fx-border-color: -jfx-unfocus-color; + -fx-border-color: -bs-color-gray-line; -fx-shape: "M 0 0 l 3.5 4 l 3.5 -4"; } +.combo-box-popup { + -fx-background-color: -bs-color-background-pane; + -fx-background-radius: 15; + -fx-border-radius: 15; + -fx-padding: 5; +} + +.combo-box-popup .scroll-pane { + -fx-background-color: -bs-color-background-pane; + -fx-background-radius: 15; + -fx-border-radius: 15; + -fx-padding: 5; +} + +.combo-box-popup > .list-view { + -fx-background-color: -bs-color-background-pane; + -fx-border-color: -bs-color-border-form-field; + -fx-translate-y: 4; + -fx-background-radius: 15; + -fx-border-radius: 15; + -fx-padding: 5; +} + +/* Rounds the first and last list cells to create full round illusion */ +.combo-box-popup .list-cell:first-child { + -fx-background-radius: 10 10 0 0; +} +.combo-box-popup .list-cell:last-child { + -fx-background-radius: 0 0 10 10; +} + +.combo-box-popup .list-cell:hover { + -fx-background-radius: 8; +} + +.combo-box-popup > .list-view:hover { + -fx-cursor: hand; +} + .combo-box-popup > .list-view > .virtual-flow > .clipped-container > .sheet > .list-cell:filled:selected { -fx-background: -fx-selection-bar; -fx-background-color: -fx-selection-bar; + -fx-background-radius: 15; + -fx-border-radius: 15; } .combo-box-popup > .list-view > .virtual-flow > .clipped-container > .sheet > .list-cell:filled:hover, @@ -301,19 +410,12 @@ tree-table-view:focused { -fx-background-insets: 0; } -.jfx-text-field { - -jfx-focus-color: -bs-color-primary; - -fx-background-color: -bs-background-color; - -fx-background-radius: 3 3 0 0; - -fx-padding: 0.333333em 0.333333em 0.333333em 0.333333em; +/* combo box list view */ +.combo-box .list-view .list-cell:odd { + -fx-background-color: -bs-color-background-pane; } - -.jfx-text-field > .input-line { - -fx-translate-x: -0.333333em; -} - -.jfx-text-field > .input-focused-line { - -fx-translate-x: -0.333333em; +.combo-box .list-view .list-cell:even { + -fx-background-color: -bs-color-background-pane; } .jfx-text-field-top-label { @@ -321,8 +423,7 @@ tree-table-view:focused { } .jfx-text-field:readonly, .hyperlink-with-icon { - -fx-background-color: -bs-color-gray-1; - -fx-padding: 0.333333em 0.333333em 0.333333em 0.333333em; + -fx-background-color: -bs-color-background-form-field-readonly; } .jfx-text-field:readonly > .input-line { @@ -348,23 +449,16 @@ tree-table-view:focused { } .jfx-password-field { - -fx-background-color: -bs-background-color; - -fx-background-radius: 3 3 0 0; - -jfx-focus-color: -bs-color-primary; - -fx-padding: 0.333333em 0.333333em 0.333333em 0.333333em; + -fx-background-color: -bs-color-background-form-field; } .jfx-password-field > .input-line { -fx-translate-x: -0.333333em; } -.jfx-password-field > .input-focused-line { - -fx-translate-x: -0.333333em; -} - -.jfx-text-field:error, .jfx-password-field:error, .jfx-text-area:error { - -jfx-focus-color: -bs-rd-error-red; - -jfx-unfocus-color: -bs-rd-error-red; +.jfx-combo-box:error, +.jfx-text-field:error { + -fx-text-fill: -bs-rd-error-red; } .jfx-text-field .error-label, .jfx-password-field .error-label, .jfx-text-area .error-label { @@ -378,58 +472,69 @@ tree-table-view:focused { -fx-font-size: 1em; } -.input-with-border { - -fx-background-color: -bs-background-color; - -fx-border-width: 1; +.offer-input { + -fx-background-color: -bs-color-background-form-field; -fx-border-color: -bs-background-gray; - -fx-border-radius: 3; -fx-pref-height: 43; -fx-pref-width: 310; - -fx-effect: innershadow(gaussian, -bs-text-color-transparent, 3, 0, 0, 1); -} - -.input-with-border .text-field { - -fx-alignment: center-right; - -fx-pref-height: 43; - -fx-font-size: 1.385em; -} - -.input-with-border > .input-label { - -fx-font-size: 0.692em; - -fx-min-width: 60; - -fx-padding: 16; + -fx-effect: innershadow(gaussian, -bs-text-color-transparent, 3, 0, 0, 0); + -fx-background-radius: 999; + -fx-border-radius: 999; -fx-alignment: center; } -.input-with-border .icon { +.offer-input .text-field { + -fx-alignment: center-right; + -fx-pref-height: 44; + -fx-font-size: 1.385em; + -fx-background-radius: 999 0 0 999; + -fx-border-radius: 999 0 0 999; + -fx-background-color: -bs-color-background-form-field; + -fx-border-color: transparent; +} + +.offer-input > .input-label { + -fx-font-size: 0.692em; + -fx-min-width: 45; + -fx-padding: 8; + -fx-alignment: center; + -fx-background-radius: 999; + -fx-border-radius: 999; + -fx-background-color: derive(-bs-color-background-form-field, 15%); +} + +.offer-input .icon { -fx-padding: 10; } -.input-with-border-readonly { +.offer-input-readonly { -fx-background-color: -bs-color-gray-1; -fx-border-width: 0; -fx-pref-width: 300; + -fx-background-radius: 999; + -fx-border-radius: 999; } -.input-with-border-readonly .text-field { +.offer-input-readonly .text-field { -fx-alignment: center-right; -fx-font-size: 1em; -fx-background-color: -bs-color-gray-1; + -fx-border-width: 0; } -.input-with-border-readonly .text-field > .input-line { +.offer-input-readonly .text-field > .input-line { -fx-background-color: transparent; } -.input-with-border-readonly > .input-label { +.offer-input-readonly > .input-label { -fx-font-size: 0.692em; -fx-min-width: 30; -fx-padding: 8; -fx-alignment: center; } -.input-with-border-readonly .icon { - -fx-padding: 2; +.offer-input-readonly .icon { + -fx-padding: 3; } .jfx-badge .badge-pane { @@ -456,7 +561,8 @@ tree-table-view:focused { } .jfx-badge { - -fx-padding: -3 0 0 0; + -fx-padding: -2 0 0 0; + -fx-border-insets: 0 0 0 0; } .jfx-toggle-button, @@ -469,11 +575,15 @@ tree-table-view:focused { -jfx-size: 8; } +.jfx-toggle-button:hover { + -fx-cursor: hand; +} + .jfx-text-area { - -jfx-focus-color: -bs-color-primary; - -jfx-unfocus-color: -bs-color-gray-line; - -fx-background-color: -bs-background-color; - -fx-padding: 0.333333em 0.333333em 0.333333em 0.333333em; + -fx-background-color: -bs-color-background-form-field; + -fx-padding: 9 9 9 9; + -fx-background-radius: 15; + -fx-border-radius: 15; } .jfx-text-area:readonly { @@ -484,8 +594,10 @@ tree-table-view:focused { -fx-translate-x: -0.333333em; } -.jfx-text-area > .input-focused-line { - -fx-translate-x: -0.333333em; +.text-area .viewport { + -fx-background-color: transparent; + -fx-background-radius: 15; + -fx-border-radius: 15; } .wallet-seed-words { @@ -501,16 +613,18 @@ tree-table-view:focused { -jfx-default-color: -bs-color-primary; } -.jfx-date-picker .jfx-text-field .jfx-text-area { +.jfx-date-picker { -fx-padding: 0.333333em 0em 0.333333em 0em; } .jfx-date-picker .jfx-text-field .jfx-text-area > .input-line { -fx-translate-x: 0em; + -fx-background-color: transparent; } .jfx-date-picker .jfx-text-field .jfx-text-area > .input-focused-line { -fx-translate-x: 0em; + -fx-background-color: transparent; } .jfx-date-picker > .arrow-button > .arrow { @@ -535,14 +649,14 @@ tree-table-view:focused { .scroll-bar:horizontal .track, .scroll-bar:vertical .track { - -fx-background-color: -bs-background-color; - -fx-border-color: -bs-background-color; + -fx-background-color: -bs-color-background-pane; + -fx-border-color: -bs-color-background-pane; -fx-background-radius: 0; } .scroll-bar:vertical .track-background, .scroll-bar:horizontal .track-background { - -fx-background-color: -bs-background-color; + -fx-background-color: -bs-color-background-pane; -fx-background-insets: 0; -fx-background-radius: 0; } @@ -573,7 +687,7 @@ tree-table-view:focused { .scroll-bar:vertical .decrement-button, .scroll-bar:horizontal .increment-button, .scroll-bar:horizontal .decrement-button { - -fx-background-color: -bs-background-color; + -fx-background-color: -bs-color-background-pane; -fx-padding: 1; } @@ -582,12 +696,12 @@ tree-table-view:focused { .scroll-bar:horizontal .decrement-arrow, .scroll-bar:vertical .decrement-arrow { -fx-shape: null; - -fx-background-color: -bs-background-color; + -fx-background-color: -bs-color-background-pane; } .scroll-bar:vertical:focused, .scroll-bar:horizontal:focused { - -fx-background-color: -bs-background-color, -bs-color-gray-ccc, -bs-color-gray-ddd; + -fx-background-color: -bs-color-background-pane; } /* Behavior */ @@ -632,7 +746,7 @@ tree-table-view:focused { /* Main UI */ #base-content-container { - -fx-background-color: -bs-background-gray; + -fx-background-color: -bs-color-gray-background; } .content-pane { @@ -659,8 +773,8 @@ tree-table-view:focused { -fx-background-color: -bs-rd-nav-background; -fx-border-width: 0 0 1 0; -fx-border-color: -bs-rd-nav-primary-border; - -fx-pref-height: 57; - -fx-padding: 0 11 0 0; + -fx-background-radius: 999; + -fx-border-radius: 999; } .top-navigation .separator:vertical .line { @@ -669,50 +783,54 @@ tree-table-view:focused { -fx-border-insets: 0 0 0 1; } +.nav-logo { + -fx-max-width: 190; + -fx-min-width: 155; +} + .nav-primary { -fx-background-color: -bs-rd-nav-primary-background; - -fx-padding: 0 11 0 11; - -fx-border-width: 0 1 0 0; + -fx-border-width: 0 0 0 0; -fx-border-color: -bs-rd-nav-primary-border; - -fx-min-width: 410; + -fx-background-radius: 999; + -fx-border-radius: 999; + -fx-padding: 9 0 9 20; } .nav-secondary { - -fx-padding: 0 11 0 11; - -fx-min-width: 296; + -fx-padding: 0 14 0 0; } -.nav-price-balance { - -fx-background-color: -bs-color-gray-background; - -fx-background-radius: 3; - -fx-effect: innershadow(gaussian, -bs-text-color-transparent, 3, 0, 0, 1); - -fx-pref-height: 41; - -fx-padding: 0 10 0 0; -} - -.nav-price-balance .separator:vertical .line { +.nav-separator { + -fx-max-width: 1; + -fx-min-width: 1; -fx-border-color: transparent transparent transparent -bs-rd-separator-dark; -fx-border-width: 1; -fx-border-insets: 0 0 0 1; } -.nav-price-balance .jfx-combo-box > .input-line { - -fx-pref-height: 0px; +.nav-spacer { + -fx-max-width: 10; + -fx-min-width: 10; } -.jfx-badge > .nav-button { + +.jfx-badge > .nav-button, +.jfx-badge > .nav-secondary-button { -fx-translate-y: 1; } .nav-button { -fx-cursor: hand; -fx-background-color: transparent; - -fx-padding: 11; + -fx-padding: 9 15; + -fx-background-radius: 999; + -fx-border-radius: 999; } .nav-button .text { - -fx-font-size: 0.769em; - -fx-font-weight: bold; + -fx-font-size: 0.95em; + -fx-font-weight: 500; -fx-fill: -bs-rd-nav-deselected; } @@ -722,23 +840,84 @@ tree-table-view:focused { .nav-button:selected { -fx-background-color: -bs-background-color; - -fx-border-radius: 4; -fx-effect: dropshadow(gaussian, -bs-text-color-transparent, 4, 0, 0, 0, 2); } +.top-navigation .nav-button:hover { + -fx-background-color: -bs-rd-nav-button-hover; +} + +.nav-primary .nav-button:hover { + -fx-background-color: -bs-rd-nav-primary-button-hover; +} + .nav-button:selected .text { -fx-fill: -bs-rd-nav-selected; } +.nav-secondary-button { + -fx-cursor: hand; + -fx-padding: 9 2 9 2; + -fx-border-insets: 0 12 1 12; + -fx-border-color: transparent; + -fx-border-width: 0 0 1px 0; +} + +.nav-secondary-button .text { + -fx-font-size: 0.95em; + -fx-font-weight: 500; + -fx-fill: -bs-rd-nav-secondary-deselected; +} + +.nav-secondary-button-japanese .text { + -fx-font-size: 1em; +} + +.nav-secondary-button:selected { + -fx-border-color: transparent transparent -bs-rd-nav-secondary-selected transparent; + -fx-border-width: 0 0 1px 0; +} + +.nav-secondary-button:hover { +} + +.nav-secondary-button:selected .text { + -fx-fill: -bs-rd-nav-secondary-selected; +} + .nav-balance-display { -fx-alignment: center-left; -fx-text-fill: -bs-rd-font-balance; } +.nav-price-balance { + -fx-background-color: -bs-rd-nav-background; + -fx-background-radius: 999; + -fx-border-radius: 999; + -fx-padding: 0 20 0 20; +} + +.nav-price-balance .separator:vertical .line { + -fx-border-color: transparent transparent transparent -bs-rd-separator-dark; + -fx-border-width: 1; + -fx-border-insets: 0 0 0 1; +} + +.nav-price-balance .jfx-combo-box { + -fx-border-color: transparent; + -fx-padding: 0; + -fx-pref-width: 180; +} + +.nav-price-balance .jfx-combo-box > .input-line { + -fx-pref-height: 0px; +} + .nav-balance-label { -fx-font-size: 0.769em; -fx-alignment: center-left; -fx-text-fill: -bs-rd-font-balance-label; + -fx-padding: 0; } #nav-alert-label { @@ -794,6 +973,10 @@ tree-table-view:focused { -fx-text-fill: -bs-background-color; } +.copy-icon-disputes.label .glyph-icon { + -fx-fill: -bs-background-color; +} + .copy-icon:hover { -fx-text-fill: -bs-text-color; } @@ -948,12 +1131,18 @@ textfield */ * * ******************************************************************************/ .table-view .table-row-cell:even .table-cell { - -fx-background-color: derive(-bs-background-color, 5%); - -fx-border-color: derive(-bs-background-color,5%); + -fx-background-color: -bs-color-background-row-even; + -fx-border-color: -bs-color-background-row-even; } .table-view .table-row-cell:odd .table-cell { - -fx-background-color: derive(-bs-background-color,-5%); - -fx-border-color: derive(-bs-background-color,-5%); + -fx-background-color: -bs-color-background-row-odd; + -fx-border-color: -bs-color-background-row-odd; +} +.table-view .table-row-cell.row-faded .table-cell .text { + -fx-fill: -bs-color-table-cell-dim; +} +.cell-faded { + -fx-opacity: 0.4; } .table-view .table-row-cell:hover .table-cell, .table-view .table-row-cell:selected .table-cell { @@ -975,42 +1164,41 @@ textfield */ .table-view .table-cell { -fx-alignment: center-left; - -fx-padding: 2 0 2 0; + -fx-padding: 6 0 4 0; + -fx-text-fill: -bs-text-color; /*-fx-padding: 3 0 2 0;*/ } .table-view .table-cell.last-column { - -fx-alignment: center-right; - -fx-padding: 2 10 2 0; + -fx-padding: 6 0 4 0; } -.table-view .table-cell.last-column.avatar-column { - -fx-alignment: center; - -fx-padding: 2 0 2 0; -} - -.table-view .column-header.last-column { - -fx-padding: 0 10 0 0; -} - -.table-view .column-header.last-column .label { - -fx-alignment: center-right; -} - -.table-view .column-header.last-column.avatar-column { - -fx-padding: 0; -} - -.table-view .column-header.last-column.avatar-column .label { +.table-view .table-cell.avatar-column { -fx-alignment: center; + -fx-padding: 6 0 4 0; } .table-view .table-cell.first-column { - -fx-padding: 2 0 2 10; + -fx-padding: 6 0 4 0; +} + +.table-view .column-header.last-column .label { } .table-view .column-header.first-column { - -fx-padding: 0 0 0 10; + -fx-padding: 0 0 0 0; +} + +.table-view .column-header.last-column { + -fx-padding: 0 0 0 0; +} + +.table-view .column-header.avatar-column { + -fx-padding: 0; +} + +.table-view .column-header.avatar-column .label { + -fx-alignment: center; } .number-column.table-cell { @@ -1019,31 +1207,31 @@ textfield */ } .table-view .filler { - -fx-background-color: -bs-color-gray-0; + -fx-background-color: transparent; } .table-view { -fx-control-inner-background-alt: -fx-control-inner-background; + -fx-padding: 0; +} + +.table-view .column-header-background { + -fx-background-color: -bs-color-background-pane; + -fx-border-color: -bs-color-border-form-field; + -fx-border-width: 0 0 1 0; } .table-view .column-header .label { -fx-alignment: center-left; -fx-font-weight: normal; -fx-font-size: 0.923em; - -fx-padding: 0; + -fx-padding: 6 0 6 0; + -fx-text-fill: -bs-text-color; } .table-view .column-header { - -fx-background-color: -bs-color-gray-0; - -fx-padding: 0; -} - -.table-view .focus { - -fx-alignment: center-left; -} - -.table-view .text { - -fx-fill: -bs-text-color; + -fx-border-color: transparent; + -fx-background-color: -bs-color-background-pane; } /* horizontal scrollbars are never needed and are flickering at scaling so lets turn them off */ @@ -1051,17 +1239,6 @@ textfield */ -fx-opacity: 0; } -.table-view:focused { - -fx-background-color: -fx-box-border, -fx-control-inner-background; - -fx-background-insets: 0, 1; - -fx-padding: 1; -} - -.table-view:focused .table-row-cell:focused { - -fx-background-color: -fx-table-cell-border-color, -fx-background; - -fx-background-insets: 0, 0 0 1 0; -} - .offer-table .table-row-cell { -fx-border-color: -bs-background-color; -fx-table-cell-border-color: -bs-background-color; @@ -1141,6 +1318,46 @@ textfield */ -fx-cell-size: 47px; } +.table-view.offer-table { + -fx-background-radius: 0; + -fx-border-radius: 0; +} + +.table-view.offer-table .column-header.first-column { + -fx-background-radius: 0; + -fx-border-radius: 0; +} + +.table-view.offer-table .column-header.last-column { + -fx-background-radius: 0; + -fx-border-radius: 0; +} + +.table-view.offer-table .table-row-cell { + -fx-background: -fx-accent; + -fx-background-color: -bs-color-gray-6; +} + +.offer-table-top { + -fx-background-color: -bs-color-background-pane; + -fx-padding: 15 15 5 15; + -fx-background-radius: 15 15 0 0; + -fx-border-radius: 15 15 0 0; + -fx-border-width: 0 0 0 0; +} + +.offer-table-top .label { + -fx-text-fill: -bs-text-color; + -fx-font-size: 1.1em; + -fx-font-weight: bold; +} + +.offer-table-top .jfx-button { + -fx-pref-width: 300px; + -fx-min-height: 35px; + -fx-padding: 5 25 5 25; +} + /******************************************************************************* * * * Icons * @@ -1204,17 +1421,28 @@ textfield */ -fx-border-color: -bs-background-gray; } -.text-area-no-border { - -fx-border-color: -bs-background-color; +.text-area-popup { + -fx-border-color: -bs-color-background-popup-blur; } -.text-area-no-border .content { - -fx-background-color: -bs-background-color; +.text-area-popup .content { + -fx-background-color: -bs-color-background-popup-blur; } -.text-area-no-border:focused { - -fx-focus-color: -bs-background-color; - -fx-faint-focus-color: -bs-background-color; +.text-area-popup:focused { + -fx-faint-focus-color: -bs-color-background-popup-blur; +} + +.notification-popup-bg .text-area-popup, .peer-info-popup-bg .text-area-popup { + -fx-border-color: -bs-color-background-popup; +} + +.notification-popup-bg .text-area-popup .content, .peer-info-popup-bg .text-area-popup .content{ + -fx-background-color: -bs-color-background-popup; +} + +.notification-popup-bg .text-area-popup:focused, .peer-info-popup-bg .text-area-popup:focused { + -fx-faint-focus-color: -bs-color-background-popup; } /******************************************************************************* @@ -1238,7 +1466,7 @@ textfield */ } .jfx-tab-pane .headers-region .tab .tab-container .tab-close-button .jfx-rippler { - -jfx-rippler-fill: -fx-accent; + -jfx-rippler-fill: none; } .tab:disabled .jfx-rippler { @@ -1256,7 +1484,7 @@ textfield */ } .jfx-tab-pane .headers-region .tab .tab-container .tab-close-button { - -fx-padding: 0 0 2 0; + -fx-padding: 0 0 0 0; } .jfx-tab-pane .headers-region .tab:selected .tab-container .tab-close-button > .jfx-svg-glyph { @@ -1276,8 +1504,8 @@ textfield */ .jfx-tab-pane .headers-region .tab .tab-container .tab-label { -fx-text-fill: -bs-rd-font-light; - -fx-padding: 14; - -fx-font-size: 0.769em; + -fx-padding: 9 14; + -fx-font-size: .95em; -fx-font-weight: normal; -fx-cursor: hand; } @@ -1291,7 +1519,7 @@ textfield */ } .jfx-tab-pane .headers-region > .tab > .jfx-rippler { - -jfx-rippler-fill: -fx-accent; + -jfx-rippler-fill: none; } .jfx-tab-pane .headers-region .tab:closable { @@ -1395,7 +1623,7 @@ textfield */ } #payment-info { - -fx-background-color: -bs-content-background-gray; + -fx-background-color: -bs-color-gray-fafa; } .toggle-button-active { @@ -1406,6 +1634,19 @@ textfield */ -fx-background-color: -bs-color-gray-1; } +.toggle-button-no-slider { + -fx-border-width: 1px; + -fx-border-color: -bs-color-border-form-field; + -fx-background-insets: 0; + -fx-pref-height: 36px; + -fx-focus-color: transparent; + -fx-faint-focus-color: transparent; +} + +.toggle-button-no-slider:hover { + -fx-cursor: hand; +} + #trade-fee-textfield { -fx-font-size: 0.9em; -fx-alignment: center-right; @@ -1425,7 +1666,7 @@ textfield */ .combo-box-editor-bold { -fx-font-weight: bold; - -fx-padding: 5 8 5 8 !important; + -fx-padding: 0 !important; -fx-text-fill: -bs-text-color; -fx-font-family: "IBM Plex Sans Medium"; } @@ -1456,6 +1697,15 @@ textfield */ -fx-pref-height: 35px; } +.offer-label { + -fx-background-color: rgb(50, 95, 182); + -fx-text-fill: white; + -fx-font-weight: normal; + -fx-background-radius: 999; + -fx-border-radius: 999; + -fx-padding: 0 6 0 6; +} + /* Offer */ .percentage-label { -fx-alignment: center; @@ -1619,7 +1869,7 @@ textfield */ .titled-group-bg, .titled-group-bg-active { -fx-body-color: -bs-color-gray-background; -fx-border-color: -bs-rd-separator; - -fx-border-width: 0 0 1 0; + -fx-border-width: 0 0 0 0; -fx-background-color: transparent; -fx-background-insets: 0; } @@ -1729,11 +1979,23 @@ textfield */ * * ******************************************************************************/ .grid-pane { - -fx-background-color: -bs-content-background-gray; - -fx-background-radius: 5; - -fx-effect: null; - -fx-effect: dropshadow(gaussian, -bs-color-gray-10, 10, 0, 0, 0); + -fx-background-color: -bs-color-background-popup-blur; -fx-background-insets: 10; + -fx-background-radius: 15; + -fx-border-radius: 15; + -fx-padding: 35, 40, 30, 40; +} + +.grid-pane-separator { + -fx-border-color: -bs-rd-separator; + -fx-border-width: 0 0 1 0; + -fx-translate-y: -2; +} + +.grid-pane .text-area { + -fx-border-width: 1; + -fx-border-color: -bs-color-border-form-field; + -fx-text-fill: -bs-text-color; } /******************************************************************************************************************** @@ -1762,24 +2024,26 @@ textfield */ -fx-text-alignment: center; } -#charts .chart-plot-background, #charts-dao .chart-plot-background { - -fx-background-color: -bs-background-color; +.chart-pane, .chart-plot-background, #charts .chart-plot-background { + -fx-background-color: transparent; } #charts .default-color0.chart-area-symbol { - -fx-background-color: -bs-sell, -bs-background-color; -} - -#charts .default-color1.chart-area-symbol, #charts-dao .default-color0.chart-area-symbol { -fx-background-color: -bs-buy, -bs-background-color; } +#charts .default-color1.chart-area-symbol, #charts-dao .default-color0.chart-area-symbol { + -fx-background-color: -bs-sell, -bs-background-color; +} + #charts .default-color0.chart-series-area-line { - -fx-stroke: -bs-sell; + -fx-stroke: -bs-buy; + -fx-stroke-width: 2px; } #charts .default-color1.chart-series-area-line, #charts-dao .default-color0.chart-series-area-line { - -fx-stroke: -bs-buy; + -fx-stroke: -bs-sell; + -fx-stroke-width: 2px; } /* The .chart-line-symbol rules change the color of the legend symbol */ @@ -1907,13 +2171,6 @@ textfield */ -fx-stroke-width: 2px; } -#charts .default-color0.chart-series-area-fill { - -fx-fill: -bs-sell-transparent; -} - -#charts .default-color1.chart-series-area-fill, #charts-dao .default-color0.chart-series-area-fill { - -fx-fill: -bs-buy-transparent; -} .chart-vertical-grid-lines { -fx-stroke: transparent; } @@ -2013,22 +2270,41 @@ textfield */ -fx-text-fill: -bs-rd-error-red; } -.popup-bg, .notification-popup-bg, .peer-info-popup-bg { +.popup-headline-information.label .glyph-icon, +.popup-headline-warning.label .glyph-icon, +.popup-icon-information.label .glyph-icon, +.popup-icon-warning.label .glyph-icon { + -fx-fill: -bs-color-primary; +} + +.popup-bg { -fx-font-size: 1.077em; - -fx-text-fill: -bs-rd-font-dark; - -fx-background-color: -bs-background-color; - -fx-background-radius: 0; + -fx-background-color: -bs-color-background-popup-blur; -fx-background-insets: 44; - -fx-effect: dropshadow(gaussian, -bs-text-color-transparent-dark, 44, 0, 0, 0); + -fx-background-radius: 15; + -fx-border-radius: 15; +} + +.notification-popup-bg, .peer-info-popup-bg { + -fx-font-size: 0.846em; + -fx-text-fill: -bs-rd-font-dark; + -fx-background-color: -bs-color-background-popup; + -fx-background-insets: 44; + -fx-effect: dropshadow(gaussian, -bs-text-color-dropshadow-light-mode, 44, 0, 0, 0); + -fx-background-radius: 15; + -fx-border-radius: 15; } .popup-bg-top { -fx-font-size: 1.077em; -fx-text-fill: -bs-rd-font-dark; - -fx-background-color: -bs-background-color; - -fx-background-radius: 0; + -fx-background-color: -bs-color-background-popup-blur; -fx-background-insets: 44; - -fx-effect: dropshadow(gaussian, -bs-text-color-transparent-dark, 44, 0, 0, 0); + -fx-background-radius: 0 0 15px 15px; +} + +.popup-dropshadow { + -fx-effect: dropshadow(gaussian, -bs-text-color-dropshadow, 20, 0, 0, 0); } .notification-popup-headline, peer-info-popup-headline { @@ -2037,18 +2313,6 @@ textfield */ -fx-text-fill: -bs-color-primary; } -.notification-popup-bg { - -fx-font-size: 0.846em; - -fx-background-insets: 44; - -fx-effect: dropshadow(gaussian, -bs-text-color-transparent-dark, 44, 0, -1, 3); -} - -.peer-info-popup-bg { - -fx-font-size: 0.846em; - -fx-background-insets: 44; - -fx-effect: dropshadow(gaussian, -bs-text-color-transparent-dark, 44, 0, -1, 3); -} - .account-status-title { -fx-font-size: 0.769em; -fx-font-family: "IBM Plex Sans Medium"; @@ -2069,7 +2333,7 @@ textfield */ } #price-feed-combo > .list-cell { - -fx-text-fill: -bs-text-color; + -fx-text-fill: -bs-rd-font-balance; -fx-font-family: "IBM Plex Sans"; } @@ -2089,42 +2353,48 @@ textfield */ } #toggle-left { - -fx-border-radius: 4 0 0 4; -fx-border-color: -bs-rd-separator-dark; + -fx-border-radius: 4 0 0 4; -fx-border-style: solid; - -fx-border-width: 0 1 0 0; + -fx-border-width: 1 1 1 1; -fx-background-radius: 4 0 0 4; + -fx-border-insets: 0; + -fx-background-insets: 1 1 1 1; -fx-background-color: -bs-background-color; -fx-effect: dropshadow(gaussian, -bs-text-color-transparent, 4, 0, 0, 0, 2); } #toggle-center { - -fx-border-radius: 0; -fx-border-color: -bs-rd-separator-dark; + -fx-border-radius: 0; -fx-border-style: solid; - -fx-border-width: 0 1 0 0; + -fx-border-width: 1 1 1 0; -fx-border-insets: 0; - -fx-background-insets: 0; + -fx-background-insets: 1 1 1 0; -fx-background-radius: 0; -fx-background-color: -bs-background-color; -fx-effect: dropshadow(gaussian, -bs-text-color-transparent, 4, 0, 0, 0, 2); } -#toggle-center:selected, #toggle-left:selected, #toggle-right:selected { - -fx-text-fill: -bs-background-color; - -fx-background-color: -bs-toggle-selected; -} - #toggle-right { + -fx-border-color: -bs-rd-separator-dark; -fx-border-radius: 0 4 4 0; - -fx-border-width: 0; + -fx-border-width: 1 1 1 0; + -fx-border-insets: 0; + -fx-background-insets: 1 1 1 0; -fx-background-radius: 0 4 4 0; -fx-background-color: -bs-background-color; -fx-effect: dropshadow(gaussian, -bs-text-color-transparent, 4, 0, 0, 0, 2); } +#toggle-center:selected, #toggle-left:selected, #toggle-right:selected { + -fx-text-fill: white; + -fx-background-color: -bs-toggle-selected; +} + #toggle-left:hover, #toggle-right:hover, #toggle-center:hover { -fx-background-color: -bs-toggle-selected; + -fx-cursor: hand; } /******************************************************************************************************************** @@ -2136,10 +2406,18 @@ textfield */ -fx-text-fill: -bs-text-color; } +.message.label .glyph-icon { + -fx-fill: -bs-text-color; +} + .my-message { -fx-text-fill: -bs-background-color; } +.my-message.label .glyph-icon { + -fx-fill: -bs-background-color; +} + .message-header { -fx-text-fill: -bs-color-gray-3; -fx-font-size: 0.846em; @@ -2308,18 +2586,103 @@ textfield */ /******************************************************************************************************************** * * - * Popover * + * Popover * * * ********************************************************************************************************************/ .popover > .content { -fx-padding: 10; + -fx-background-color: -bs-color-background-popup; + -fx-border-radius: 3; + -fx-background-radius: 3; + -fx-background-insets: 1; } .popover > .content .default-text { -fx-text-fill: -bs-text-color; } -.popover > .border { - -fx-stroke: linear-gradient(to bottom, -bs-text-color-transparent, -bs-text-color-transparent-dark) !important; - -fx-fill: -bs-background-color !important; +.popover > .content .text-field { + -fx-background-color: -bs-color-background-form-field-readonly !important; + -fx-border-radius: 4; + -fx-background-radius: 4; +} + +.popover > .border { + -fx-stroke: linear-gradient(to bottom, -bs-text-color-transparent, -bs-text-color-dropshadow) !important; + -fx-fill: -bs-color-background-popup !important; +} + +/******************************************************************************************************************** + * * + * Other * + * * + ********************************************************************************************************************/ +.input-with-border { + -fx-border-width: 1; + -fx-border-color: -bs-color-border-form-field; + -fx-border-insets: 1 0 1 0; + -fx-background-insets: 1 0 1 0; +} + +.table-view.non-interactive-table .column-header .label { + -fx-text-fill: -bs-text-color-dim2; +} + +.highlight-text { + -fx-text-fill: -fx-dark-text-color !important; +} + +.grid-pane .text-area, +.flat-text-area-with-border { + -fx-background-radius: 8; + -fx-border-radius: 8; + -fx-font-size: 1.077em; + -fx-font-family: "IBM Plex Sans"; + -fx-font-weight: normal; + -fx-text-fill: -bs-rd-font-dark-gray !important; + -fx-border-width: 1; + -fx-border-color: -bs-color-border-form-field !important; +} + +.grid-pane .text-area:readonly, +.flat-text-area-with-border { + -fx-background-color: transparent !important; +} + +.grid-pane .text-area { + -fx-max-height: 150 !important; +} + +.passphrase-copy-box { + -fx-border-width: 1; + -fx-border-color: -bs-color-border-form-field; + -fx-background-radius: 8; + -fx-border-radius: 8; + -fx-padding: 13; + -fx-background-insets: 0; +} + +.passphrase-copy-box > .jfx-text-field { + -fx-padding: 0; + -fx-background-color: transparent; + -fx-border-width: 0; +} + +.passphrase-copy-box .label { + -fx-text-fill: white; + -fx-padding: 0; +} + +.passphrase-copy-box .jfx-button { + -fx-padding: 5 15 5 15; + -fx-background-radius: 999; + -fx-border-radius: 999; + -fx-min-height: 0; + -fx-font-size: 1.077em; + -fx-font-family: "IBM Plex Sans"; + -fx-font-weight: normal; +} + +.popup-with-input { + -fx-background-color: -bs-color-background-popup-input; } diff --git a/desktop/src/main/java/haveno/desktop/images.css b/desktop/src/main/java/haveno/desktop/images.css index aacd4c6b1b..1e36427ee9 100644 --- a/desktop/src/main/java/haveno/desktop/images.css +++ b/desktop/src/main/java/haveno/desktop/images.css @@ -1,16 +1,3 @@ -/* splash screen */ -/*noinspection CssUnknownTarget*/ -#image-splash-logo { - -fx-image: url("../../images/logo_splash.png"); -} - -/* splash screen testnet */ -/*noinspection CssUnknownTarget*/ -#image-splash-testnet-logo { - -fx-image: url("../../images/logo_splash_testnet.png"); -} - -/* shared*/ #image-info { -fx-image: url("../../images/info.png"); } @@ -23,16 +10,29 @@ -fx-image: url("../../images/alert_round.png"); } +#image-red_circle_solid { + -fx-image: url("../../images/red_circle_solid.png"); +} + + #image-green_circle { -fx-image: url("../../images/green_circle.png"); } +#image-green_circle_solid { + -fx-image: url("../../images/green_circle_solid.png"); +} + #image-yellow_circle { -fx-image: url("../../images/yellow_circle.png"); } -#image-blue_circle { - -fx-image: url("../../images/blue_circle.png"); +#image-yellow_circle_solid { + -fx-image: url("../../images/yellow_circle_solid.png"); +} + +#image-blue_circle_solid { + -fx-image: url("../../images/blue_circle_solid.png"); } #image-remove { @@ -300,3 +300,59 @@ #image-new-trade-protocol-screenshot { -fx-image: url("../../images/new_trade_protocol_screenshot.png"); } + +#image-support { + -fx-image: url("../../images/support.png"); +} + +#image-account { + -fx-image: url("../../images/account.png"); +} + +#image-settings { + -fx-image: url("../../images/settings.png"); +} + +#image-btc-logo { + -fx-image: url("../../images/btc_logo.png"); +} + +#image-bch-logo { + -fx-image: url("../../images/bch_logo.png"); +} + +#image-dai-erc20-logo { + -fx-image: url("../../images/dai-erc20_logo.png"); +} + +#image-eth-logo { + -fx-image: url("../../images/eth_logo.png"); +} + +#image-ltc-logo { + -fx-image: url("../../images/ltc_logo.png"); +} + +#image-usdc-erc20-logo { + -fx-image: url("../../images/usdc-erc20_logo.png"); +} + +#image-usdt-erc20-logo { + -fx-image: url("../../images/usdt-erc20_logo.png"); +} + +#image-usdt-trc20-logo { + -fx-image: url("../../images/usdt-trc20_logo.png"); +} + +#image-xmr-logo { + -fx-image: url("../../images/xmr_logo.png"); +} + +#image-dark-mode-toggle { + -fx-image: url("../../images/dark_mode_toggle.png"); +} + +#image-light-mode-toggle { + -fx-image: url("../../images/light_mode_toggle.png"); +} diff --git a/desktop/src/main/java/haveno/desktop/main/MainView.java b/desktop/src/main/java/haveno/desktop/main/MainView.java index 7856a2021f..ee7434324c 100644 --- a/desktop/src/main/java/haveno/desktop/main/MainView.java +++ b/desktop/src/main/java/haveno/desktop/main/MainView.java @@ -32,6 +32,7 @@ import haveno.core.locale.GlobalSettings; import haveno.core.locale.LanguageUtil; import haveno.core.locale.Res; import haveno.core.provider.price.MarketPrice; +import haveno.core.user.Preferences; import haveno.desktop.Navigation; import haveno.desktop.common.view.CachingViewLoader; import haveno.desktop.common.view.FxmlView; @@ -73,6 +74,7 @@ import javafx.geometry.Insets; import javafx.geometry.NodeOrientation; import javafx.geometry.Orientation; import javafx.geometry.Pos; +import javafx.scene.Cursor; import javafx.scene.control.Button; import javafx.scene.control.ComboBox; import javafx.scene.control.Label; @@ -92,6 +94,7 @@ import static javafx.scene.layout.AnchorPane.setRightAnchor; import static javafx.scene.layout.AnchorPane.setTopAnchor; import javafx.scene.layout.BorderPane; import javafx.scene.layout.HBox; +import javafx.scene.layout.Pane; import javafx.scene.layout.Priority; import javafx.scene.layout.Region; import javafx.scene.layout.StackPane; @@ -125,17 +128,19 @@ public class MainView extends InitializableView { private Label xmrSplashInfo; private Popup p2PNetworkWarnMsgPopup, xmrNetworkWarnMsgPopup; private final TorNetworkSettingsWindow torNetworkSettingsWindow; + private final Preferences preferences; + private static final int networkIconSize = 20; public static StackPane getRootContainer() { return MainView.rootContainer; } public static void blurLight() { - transitions.blur(MainView.rootContainer, Transitions.DEFAULT_DURATION, -0.6, false, 5); + transitions.blur(MainView.rootContainer, Transitions.DEFAULT_DURATION, -0.6, false, 15); } public static void blurUltraLight() { - transitions.blur(MainView.rootContainer, Transitions.DEFAULT_DURATION, -0.6, false, 2); + transitions.blur(MainView.rootContainer, Transitions.DEFAULT_DURATION, -0.6, false, 15); } public static void darken() { @@ -151,12 +156,14 @@ public class MainView extends InitializableView { CachingViewLoader viewLoader, Navigation navigation, Transitions transitions, - TorNetworkSettingsWindow torNetworkSettingsWindow) { + TorNetworkSettingsWindow torNetworkSettingsWindow, + Preferences preferences) { super(model); this.viewLoader = viewLoader; this.navigation = navigation; MainView.transitions = transitions; this.torNetworkSettingsWindow = torNetworkSettingsWindow; + this.preferences = preferences; } @Override @@ -165,15 +172,15 @@ public class MainView extends InitializableView { if (LanguageUtil.isDefaultLanguageRTL()) MainView.rootContainer.setNodeOrientation(NodeOrientation.RIGHT_TO_LEFT); - ToggleButton marketButton = new NavButton(MarketView.class, Res.get("mainView.menu.market").toUpperCase()); - ToggleButton buyButton = new NavButton(BuyOfferView.class, Res.get("mainView.menu.buyXmr").toUpperCase()); - ToggleButton sellButton = new NavButton(SellOfferView.class, Res.get("mainView.menu.sellXmr").toUpperCase()); - ToggleButton portfolioButton = new NavButton(PortfolioView.class, Res.get("mainView.menu.portfolio").toUpperCase()); - ToggleButton fundsButton = new NavButton(FundsView.class, Res.get("mainView.menu.funds").toUpperCase()); + ToggleButton marketButton = new NavButton(MarketView.class, Res.get("mainView.menu.market")); + ToggleButton buyButton = new NavButton(BuyOfferView.class, Res.get("mainView.menu.buyXmr")); + ToggleButton sellButton = new NavButton(SellOfferView.class, Res.get("mainView.menu.sellXmr")); + ToggleButton portfolioButton = new NavButton(PortfolioView.class, Res.get("mainView.menu.portfolio")); + ToggleButton fundsButton = new NavButton(FundsView.class, Res.get("mainView.menu.funds")); - ToggleButton supportButton = new NavButton(SupportView.class, Res.get("mainView.menu.support")); - ToggleButton accountButton = new NavButton(AccountView.class, Res.get("mainView.menu.account")); - ToggleButton settingsButton = new NavButton(SettingsView.class, Res.get("mainView.menu.settings")); + ToggleButton supportButton = new SecondaryNavButton(SupportView.class, Res.get("mainView.menu.support"), "image-support"); + ToggleButton accountButton = new SecondaryNavButton(AccountView.class, Res.get("mainView.menu.account"), "image-account"); + ToggleButton settingsButton = new SecondaryNavButton(SettingsView.class, Res.get("mainView.menu.settings"), "image-settings"); JFXBadge portfolioButtonWithBadge = new JFXBadge(portfolioButton); JFXBadge supportButtonWithBadge = new JFXBadge(supportButton); @@ -298,47 +305,56 @@ public class MainView extends InitializableView { } }); - HBox primaryNav = new HBox(marketButton, getNavigationSeparator(), buyButton, getNavigationSeparator(), - sellButton, getNavigationSeparator(), portfolioButtonWithBadge, getNavigationSeparator(), fundsButton); + HBox primaryNav = new HBox(getLogoPane(), marketButton, getNavigationSpacer(), buyButton, getNavigationSpacer(), + sellButton, getNavigationSpacer(), portfolioButtonWithBadge, getNavigationSpacer(), fundsButton); primaryNav.setAlignment(Pos.CENTER_LEFT); primaryNav.getStyleClass().add("nav-primary"); HBox.setHgrow(primaryNav, Priority.SOMETIMES); - HBox secondaryNav = new HBox(supportButtonWithBadge, getNavigationSpacer(), accountButton, - getNavigationSpacer(), settingsButtonWithBadge, getNavigationSpacer()); - secondaryNav.getStyleClass().add("nav-secondary"); - HBox.setHgrow(secondaryNav, Priority.SOMETIMES); - - secondaryNav.setAlignment(Pos.CENTER); - HBox priceAndBalance = new HBox(marketPriceBox.second, getNavigationSeparator(), availableBalanceBox.second, getNavigationSeparator(), pendingBalanceBox.second, getNavigationSeparator(), reservedBalanceBox.second); - priceAndBalance.setMaxHeight(41); priceAndBalance.setAlignment(Pos.CENTER); - priceAndBalance.setSpacing(9); + priceAndBalance.setSpacing(12); priceAndBalance.getStyleClass().add("nav-price-balance"); - HBox navPane = new HBox(primaryNav, secondaryNav, getNavigationSpacer(), - priceAndBalance) {{ - setLeftAnchor(this, 0d); - setRightAnchor(this, 0d); - setTopAnchor(this, 0d); + HBox navPane = new HBox(primaryNav, priceAndBalance) {{ + setLeftAnchor(this, 25d); + setRightAnchor(this, 25d); + setTopAnchor(this, 20d); setPadding(new Insets(0, 0, 0, 0)); getStyleClass().add("top-navigation"); }}; navPane.setAlignment(Pos.CENTER); + HBox secondaryNav = new HBox(supportButtonWithBadge, accountButton, settingsButtonWithBadge); + secondaryNav.getStyleClass().add("nav-secondary"); + secondaryNav.setAlignment(Pos.CENTER_RIGHT); + secondaryNav.setPickOnBounds(false); + HBox.setHgrow(secondaryNav, Priority.ALWAYS); + AnchorPane.setLeftAnchor(secondaryNav, 0.0); + AnchorPane.setRightAnchor(secondaryNav, 0.0); + AnchorPane.setTopAnchor(secondaryNav, 0.0); + + AnchorPane secondaryNavContainer = new AnchorPane() {{ + setId("nav-secondary-container"); + setLeftAnchor(this, 0d); + setRightAnchor(this, 0d); + setTopAnchor(this, 94d); + }}; + secondaryNavContainer.setPickOnBounds(false); + secondaryNavContainer.getChildren().add(secondaryNav); + AnchorPane contentContainer = new AnchorPane() {{ getStyleClass().add("content-pane"); setLeftAnchor(this, 0d); setRightAnchor(this, 0d); - setTopAnchor(this, 57d); + setTopAnchor(this, 95d); setBottomAnchor(this, 0d); }}; - AnchorPane applicationContainer = new AnchorPane(navPane, contentContainer) {{ + AnchorPane applicationContainer = new AnchorPane(navPane, contentContainer, secondaryNavContainer) {{ setId("application-container"); }}; @@ -398,15 +414,32 @@ public class MainView extends InitializableView { private Separator getNavigationSeparator() { final Separator separator = new Separator(Orientation.VERTICAL); HBox.setHgrow(separator, Priority.ALWAYS); - separator.setMaxHeight(22); separator.setMaxWidth(Double.MAX_VALUE); + separator.getStyleClass().add("nav-separator"); return separator; } + @NotNull + private Pane getLogoPane() { + ImageView logo = new ImageView(); + logo.setId("image-logo-landscape"); + logo.setPreserveRatio(true); + logo.setFitHeight(40); + logo.setSmooth(true); + logo.setCache(true); + + final Pane pane = new Pane(); + HBox.setHgrow(pane, Priority.ALWAYS); + pane.getStyleClass().add("nav-logo"); + pane.getChildren().add(logo); + return pane; + } + @NotNull private Region getNavigationSpacer() { final Region spacer = new Region(); HBox.setHgrow(spacer, Priority.ALWAYS); + spacer.getStyleClass().add("nav-spacer"); return spacer; } @@ -447,7 +480,6 @@ public class MainView extends InitializableView { priceComboBox.setVisibleRowCount(12); priceComboBox.setFocusTraversable(false); priceComboBox.setId("price-feed-combo"); - priceComboBox.setPadding(new Insets(0, -4, -4, 0)); priceComboBox.setCellFactory(p -> getPriceFeedComboBoxListCell()); ListCell buttonCell = getPriceFeedComboBoxListCell(); buttonCell.setId("price-feed-combo"); @@ -458,7 +490,6 @@ public class MainView extends InitializableView { updateMarketPriceLabel(marketPriceLabel); marketPriceLabel.getStyleClass().add("nav-balance-label"); - marketPriceLabel.setPadding(new Insets(-2, 0, 4, 9)); marketPriceBox.getChildren().addAll(priceComboBox, marketPriceLabel); @@ -509,7 +540,10 @@ public class MainView extends InitializableView { vBox.setId("splash"); ImageView logo = new ImageView(); - logo.setId(Config.baseCurrencyNetwork() == BaseCurrencyNetwork.XMR_MAINNET ? "image-splash-logo" : "image-splash-testnet-logo"); + logo.setId(Config.baseCurrencyNetwork() == BaseCurrencyNetwork.XMR_MAINNET ? "image-logo-splash" : "image-logo-splash-testnet"); + logo.setFitWidth(400); + logo.setPreserveRatio(true); + logo.setSmooth(true); // createBitcoinInfoBox xmrSplashInfo = new AutoTooltipLabel(); @@ -552,7 +586,7 @@ public class MainView extends InitializableView { // create P2PNetworkBox splashP2PNetworkLabel = new AutoTooltipLabel(); splashP2PNetworkLabel.setWrapText(true); - splashP2PNetworkLabel.setMaxWidth(500); + splashP2PNetworkLabel.setMaxWidth(700); splashP2PNetworkLabel.setTextAlignment(TextAlignment.CENTER); splashP2PNetworkLabel.getStyleClass().add("sub-info"); splashP2PNetworkLabel.textProperty().bind(model.getP2PNetworkInfo()); @@ -587,9 +621,11 @@ public class MainView extends InitializableView { ImageView splashP2PNetworkIcon = new ImageView(); splashP2PNetworkIcon.setId("image-connection-tor"); + splashP2PNetworkIcon.setFitWidth(networkIconSize); + splashP2PNetworkIcon.setFitHeight(networkIconSize); splashP2PNetworkIcon.setVisible(false); splashP2PNetworkIcon.setManaged(false); - HBox.setMargin(splashP2PNetworkIcon, new Insets(0, 0, 5, 0)); + HBox.setMargin(splashP2PNetworkIcon, new Insets(0, 0, 0, 0)); splashP2PNetworkIcon.setOnMouseClicked(e -> { torNetworkSettingsWindow.show(); }); @@ -603,6 +639,8 @@ public class MainView extends InitializableView { splashP2PNetworkIcon.setId(newValue); splashP2PNetworkIcon.setVisible(true); splashP2PNetworkIcon.setManaged(true); + splashP2PNetworkIcon.setFitWidth(networkIconSize); + splashP2PNetworkIcon.setFitHeight(networkIconSize); // if we can connect in 10 sec. we know that tor is working showTorNetworkSettingsTimer.stop(); @@ -725,15 +763,39 @@ public class MainView extends InitializableView { setRightAnchor(versionBox, 10d); setBottomAnchor(versionBox, 7d); + // Dark mode toggle + ImageView useDarkModeIcon = new ImageView(); + useDarkModeIcon.setId(preferences.getCssTheme() == 1 ? "image-dark-mode-toggle" : "image-light-mode-toggle"); + useDarkModeIcon.setFitHeight(networkIconSize); + useDarkModeIcon.setPreserveRatio(true); + useDarkModeIcon.setPickOnBounds(true); + useDarkModeIcon.setCursor(Cursor.HAND); + setRightAnchor(useDarkModeIcon, 8d); + setBottomAnchor(useDarkModeIcon, 6d); + Tooltip modeToolTip = new Tooltip(); + Tooltip.install(useDarkModeIcon, modeToolTip); + useDarkModeIcon.setOnMouseEntered(e -> modeToolTip.setText(Res.get(preferences.getCssTheme() == 1 ? "setting.preferences.useLightMode" : "setting.preferences.useDarkMode"))); + useDarkModeIcon.setOnMouseClicked(e -> { + preferences.setCssTheme(preferences.getCssTheme() != 1); + }); + preferences.getCssThemeProperty().addListener((observable, oldValue, newValue) -> { + useDarkModeIcon.setId(preferences.getCssTheme() == 1 ? "image-dark-mode-toggle" : "image-light-mode-toggle"); + }); + // P2P Network Label p2PNetworkLabel = new AutoTooltipLabel(); p2PNetworkLabel.setId("footer-pane"); p2PNetworkLabel.textProperty().bind(model.getP2PNetworkInfo()); + double networkIconRightAnchor = 54d; ImageView p2PNetworkIcon = new ImageView(); - setRightAnchor(p2PNetworkIcon, 10d); - setBottomAnchor(p2PNetworkIcon, 5d); + setRightAnchor(p2PNetworkIcon, networkIconRightAnchor); + setBottomAnchor(p2PNetworkIcon, 6d); + p2PNetworkIcon.setPickOnBounds(true); + p2PNetworkIcon.setCursor(Cursor.HAND); p2PNetworkIcon.setOpacity(0.4); + p2PNetworkIcon.setFitWidth(networkIconSize); + p2PNetworkIcon.setFitHeight(networkIconSize); p2PNetworkIcon.idProperty().bind(model.getP2PNetworkIconId()); p2PNetworkLabel.idProperty().bind(model.getP2pNetworkLabelId()); model.getP2pNetworkWarnMsg().addListener((ov, oldValue, newValue) -> { @@ -749,8 +811,12 @@ public class MainView extends InitializableView { }); ImageView p2PNetworkStatusIcon = new ImageView(); - setRightAnchor(p2PNetworkStatusIcon, 30d); - setBottomAnchor(p2PNetworkStatusIcon, 7d); + p2PNetworkStatusIcon.setPickOnBounds(true); + p2PNetworkStatusIcon.setCursor(Cursor.HAND); + p2PNetworkStatusIcon.setFitWidth(networkIconSize); + p2PNetworkStatusIcon.setFitHeight(networkIconSize); + setRightAnchor(p2PNetworkStatusIcon, networkIconRightAnchor + 22); + setBottomAnchor(p2PNetworkStatusIcon, 6d); Tooltip p2pNetworkStatusToolTip = new Tooltip(); Tooltip.install(p2PNetworkStatusIcon, p2pNetworkStatusToolTip); p2PNetworkStatusIcon.setOnMouseEntered(e -> p2pNetworkStatusToolTip.setText(model.getP2pConnectionSummary())); @@ -791,10 +857,10 @@ public class MainView extends InitializableView { VBox vBox = new VBox(); vBox.setAlignment(Pos.CENTER_RIGHT); vBox.getChildren().addAll(p2PNetworkLabel, p2pNetworkProgressBar); - setRightAnchor(vBox, 53d); + setRightAnchor(vBox, networkIconRightAnchor + 45); setBottomAnchor(vBox, 5d); - return new AnchorPane(separator, xmrInfoLabel, versionBox, vBox, p2PNetworkStatusIcon, p2PNetworkIcon) {{ + return new AnchorPane(separator, xmrInfoLabel, versionBox, vBox, p2PNetworkStatusIcon, p2PNetworkIcon, useDarkModeIcon) {{ setId("footer-pane"); setMinHeight(30); setMaxHeight(30); @@ -825,6 +891,9 @@ public class MainView extends InitializableView { this.setToggleGroup(navButtons); this.getStyleClass().add("nav-button"); + this.setMinWidth(Region.USE_PREF_SIZE); // prevent squashing content + this.setPrefWidth(Region.USE_COMPUTED_SIZE); + // Japanese fonts are dense, increase top nav button text size if (model.getPreferences() != null && "ja".equals(model.getPreferences().getUserLanguage())) { this.getStyleClass().add("nav-button-japanese"); @@ -836,4 +905,29 @@ public class MainView extends InitializableView { } } + + private class SecondaryNavButton extends NavButton { + + SecondaryNavButton(Class viewClass, String title, String iconId) { + super(viewClass, title); + this.getStyleClass().setAll("nav-secondary-button"); + + // Japanese fonts are dense, increase top nav button text size + if (model.getPreferences() != null && "ja".equals(model.getPreferences().getUserLanguage())) { + this.getStyleClass().setAll("nav-secondary-button-japanese"); + } + + // add icon + ImageView imageView = new ImageView(); + imageView.setId(iconId); + imageView.setFitWidth(15); + imageView.setPreserveRatio(true); + setGraphicTextGap(10); + setGraphic(imageView); + + // show cursor hand on any hover + this.setPickOnBounds(true); + } + + } } diff --git a/desktop/src/main/java/haveno/desktop/main/account/AccountView.java b/desktop/src/main/java/haveno/desktop/main/account/AccountView.java index 30c434cccd..028276a83a 100644 --- a/desktop/src/main/java/haveno/desktop/main/account/AccountView.java +++ b/desktop/src/main/java/haveno/desktop/main/account/AccountView.java @@ -86,12 +86,12 @@ public class AccountView extends ActivatableView { root.setTabClosingPolicy(TabPane.TabClosingPolicy.ALL_TABS); - traditionalAccountsTab.setText(Res.get("account.menu.paymentAccount").toUpperCase()); - cryptoAccountsTab.setText(Res.get("account.menu.altCoinsAccountView").toUpperCase()); - passwordTab.setText(Res.get("account.menu.password").toUpperCase()); - seedWordsTab.setText(Res.get("account.menu.seedWords").toUpperCase()); - //walletInfoTab.setText(Res.get("account.menu.walletInfo").toUpperCase()); - backupTab.setText(Res.get("account.menu.backup").toUpperCase()); + traditionalAccountsTab.setText(Res.get("account.menu.paymentAccount")); + cryptoAccountsTab.setText(Res.get("account.menu.altCoinsAccountView")); + passwordTab.setText(Res.get("account.menu.password")); + seedWordsTab.setText(Res.get("account.menu.seedWords")); + //walletInfoTab.setText(Res.get("account.menu.walletInfo")); + backupTab.setText(Res.get("account.menu.backup")); navigationListener = (viewPath, data) -> { if (viewPath.size() == 3 && viewPath.indexOf(AccountView.class) == 1) { diff --git a/desktop/src/main/java/haveno/desktop/main/account/content/cryptoaccounts/CryptoAccountsView.java b/desktop/src/main/java/haveno/desktop/main/account/content/cryptoaccounts/CryptoAccountsView.java index 8abcd98a9d..bdadfffbda 100644 --- a/desktop/src/main/java/haveno/desktop/main/account/content/cryptoaccounts/CryptoAccountsView.java +++ b/desktop/src/main/java/haveno/desktop/main/account/content/cryptoaccounts/CryptoAccountsView.java @@ -216,7 +216,7 @@ public class CryptoAccountsView extends PaymentAccountsView private void addContent() { TableView tableView = new TableView<>(); + GUIUtil.applyTableStyle(tableView); GridPane.setRowIndex(tableView, ++rowIndex); GridPane.setColumnSpan(tableView, 2); GridPane.setMargin(tableView, new Insets(10, 0, 0, 0)); diff --git a/desktop/src/main/java/haveno/desktop/main/account/content/traditionalaccounts/TraditionalAccountsView.java b/desktop/src/main/java/haveno/desktop/main/account/content/traditionalaccounts/TraditionalAccountsView.java index a5f7332c81..79243691bc 100644 --- a/desktop/src/main/java/haveno/desktop/main/account/content/traditionalaccounts/TraditionalAccountsView.java +++ b/desktop/src/main/java/haveno/desktop/main/account/content/traditionalaccounts/TraditionalAccountsView.java @@ -535,7 +535,7 @@ public class TraditionalAccountsView extends PaymentAccountsView { @Override public void initialize() { - depositTab.setText(Res.get("funds.tab.deposit").toUpperCase()); - withdrawalTab.setText(Res.get("funds.tab.withdrawal").toUpperCase()); - transactionsTab.setText(Res.get("funds.tab.transactions").toUpperCase()); + depositTab.setText(Res.get("funds.tab.deposit")); + withdrawalTab.setText(Res.get("funds.tab.withdrawal")); + transactionsTab.setText(Res.get("funds.tab.transactions")); navigationListener = (viewPath, data) -> { if (viewPath.size() == 3 && viewPath.indexOf(FundsView.class) == 1) diff --git a/desktop/src/main/java/haveno/desktop/main/funds/deposit/DepositView.java b/desktop/src/main/java/haveno/desktop/main/funds/deposit/DepositView.java index 884df454e7..72f7945f65 100644 --- a/desktop/src/main/java/haveno/desktop/main/funds/deposit/DepositView.java +++ b/desktop/src/main/java/haveno/desktop/main/funds/deposit/DepositView.java @@ -40,6 +40,7 @@ import com.google.inject.name.Named; import haveno.common.ThreadUtils; import haveno.common.UserThread; import haveno.common.app.DevEnv; +import haveno.common.util.Tuple2; import haveno.common.util.Tuple3; import haveno.core.locale.Res; import haveno.core.trade.HavenoUtils; @@ -89,10 +90,9 @@ import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.layout.GridPane; import javafx.scene.layout.HBox; +import javafx.scene.layout.StackPane; import javafx.scene.layout.VBox; import javafx.util.Callback; -import monero.common.MoneroUtils; -import monero.wallet.model.MoneroTxConfig; import monero.wallet.model.MoneroWalletListener; import net.glxn.qrgen.QRCode; import net.glxn.qrgen.image.ImageType; @@ -111,6 +111,7 @@ public class DepositView extends ActivatableView { @FXML TableColumn addressColumn, balanceColumn, confirmationsColumn, usageColumn; private ImageView qrCodeImageView; + private StackPane qrCodePane; private AddressTextField addressTextField; private Button generateNewAddressButton; private TitledGroupBg titledGroupBg; @@ -144,6 +145,7 @@ public class DepositView extends ActivatableView { @Override public void initialize() { + GUIUtil.applyTableStyle(tableView); paymentLabelString = Res.get("funds.deposit.fundHavenoWallet"); addressColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.address"))); @@ -154,6 +156,7 @@ public class DepositView extends ActivatableView { // set loading placeholder Label placeholderLabel = new Label("Loading..."); tableView.setPlaceholder(placeholderLabel); + tableView.getStyleClass().add("non-interactive-table"); ThreadUtils.execute(() -> { @@ -190,19 +193,19 @@ public class DepositView extends ActivatableView { titledGroupBg = addTitledGroupBg(gridPane, gridRow, 4, Res.get("funds.deposit.fundWallet")); titledGroupBg.getStyleClass().add("last"); - qrCodeImageView = new ImageView(); - qrCodeImageView.setFitHeight(150); - qrCodeImageView.setFitWidth(150); - qrCodeImageView.getStyleClass().add("qr-code"); - Tooltip.install(qrCodeImageView, new Tooltip(Res.get("shared.openLargeQRWindow"))); - qrCodeImageView.setOnMouseClicked(e -> UserThread.runAfter( + Tuple2 qrCodeTuple = GUIUtil.getSmallXmrQrCodePane(); + qrCodePane = qrCodeTuple.first; + qrCodeImageView = qrCodeTuple.second; + + Tooltip.install(qrCodePane, new Tooltip(Res.get("shared.openLargeQRWindow"))); + qrCodePane.setOnMouseClicked(e -> UserThread.runAfter( () -> new QRCodeWindow(getPaymentUri()).show(), 200, TimeUnit.MILLISECONDS)); - GridPane.setRowIndex(qrCodeImageView, gridRow); - GridPane.setRowSpan(qrCodeImageView, 4); - GridPane.setColumnIndex(qrCodeImageView, 1); - GridPane.setMargin(qrCodeImageView, new Insets(Layout.FIRST_ROW_DISTANCE, 0, 0, 10)); - gridPane.getChildren().add(qrCodeImageView); + GridPane.setRowIndex(qrCodePane, gridRow); + GridPane.setRowSpan(qrCodePane, 4); + GridPane.setColumnIndex(qrCodePane, 1); + GridPane.setMargin(qrCodePane, new Insets(Layout.FIRST_ROW_DISTANCE, 0, 0, 10)); + gridPane.getChildren().add(qrCodePane); addressTextField = addAddressTextField(gridPane, ++gridRow, Res.get("shared.address"), Layout.FIRST_ROW_DISTANCE); addressTextField.setPaymentLabel(paymentLabelString); @@ -213,8 +216,8 @@ public class DepositView extends ActivatableView { titledGroupBg.setVisible(false); titledGroupBg.setManaged(false); - qrCodeImageView.setVisible(false); - qrCodeImageView.setManaged(false); + qrCodePane.setVisible(false); + qrCodePane.setManaged(false); addressTextField.setVisible(false); addressTextField.setManaged(false); amountTextField.setManaged(false); @@ -310,8 +313,8 @@ public class DepositView extends ActivatableView { private void fillForm(String address) { titledGroupBg.setVisible(true); titledGroupBg.setManaged(true); - qrCodeImageView.setVisible(true); - qrCodeImageView.setManaged(true); + qrCodePane.setVisible(true); + qrCodePane.setManaged(true); addressTextField.setVisible(true); addressTextField.setManaged(true); amountTextField.setManaged(true); @@ -366,10 +369,7 @@ public class DepositView extends ActivatableView { @NotNull private String getPaymentUri() { - return MoneroUtils.getPaymentUri(new MoneroTxConfig() - .setAddress(addressTextField.getAddress()) - .setAmount(HavenoUtils.coinToAtomicUnits(getAmount())) - .setNote(paymentLabelString)); + return GUIUtil.getMoneroURI(addressTextField.getAddress(), HavenoUtils.coinToAtomicUnits(getAmount()), paymentLabelString); } /////////////////////////////////////////////////////////////////////////////////////////// @@ -377,7 +377,6 @@ public class DepositView extends ActivatableView { /////////////////////////////////////////////////////////////////////////////////////////// private void setUsageColumnCellFactory() { - usageColumn.getStyleClass().add("last-column"); usageColumn.setCellValueFactory((addressListItem) -> new ReadOnlyObjectWrapper<>(addressListItem.getValue())); usageColumn.setCellFactory(new Callback<>() { @@ -390,7 +389,9 @@ public class DepositView extends ActivatableView { public void updateItem(final DepositListItem item, boolean empty) { super.updateItem(item, empty); if (item != null && !empty) { - setGraphic(new AutoTooltipLabel(item.getUsage())); + Label usageLabel = new AutoTooltipLabel(item.getUsage()); + usageLabel.getStyleClass().add("highlight-text"); + setGraphic(usageLabel); } else { setGraphic(null); } @@ -401,7 +402,6 @@ public class DepositView extends ActivatableView { } private void setAddressColumnCellFactory() { - addressColumn.getStyleClass().add("first-column"); addressColumn.setCellValueFactory((addressListItem) -> new ReadOnlyObjectWrapper<>(addressListItem.getValue())); addressColumn.setCellFactory( @@ -434,6 +434,7 @@ public class DepositView extends ActivatableView { private void setBalanceColumnCellFactory() { balanceColumn.setCellValueFactory((addressListItem) -> new ReadOnlyObjectWrapper<>(addressListItem.getValue())); + balanceColumn.getStyleClass().add("highlight-text"); balanceColumn.setCellFactory(new Callback<>() { @Override diff --git a/desktop/src/main/java/haveno/desktop/main/funds/locked/LockedView.java b/desktop/src/main/java/haveno/desktop/main/funds/locked/LockedView.java index 8cf5700cc6..5570ca1bef 100644 --- a/desktop/src/main/java/haveno/desktop/main/funds/locked/LockedView.java +++ b/desktop/src/main/java/haveno/desktop/main/funds/locked/LockedView.java @@ -122,6 +122,7 @@ public class LockedView extends ActivatableView { addressColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.address"))); balanceColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.balanceWithCur", Res.getBaseCurrencyCode()))); + GUIUtil.applyTableStyle(tableView); tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY); tableView.setPlaceholder(new AutoTooltipLabel(Res.get("funds.locked.noFunds"))); @@ -250,7 +251,6 @@ public class LockedView extends ActivatableView { /////////////////////////////////////////////////////////////////////////////////////////// private void setDateColumnCellFactory() { - dateColumn.getStyleClass().add("first-column"); dateColumn.setCellValueFactory((addressListItem) -> new ReadOnlyObjectWrapper<>(addressListItem.getValue())); dateColumn.setCellFactory(new Callback<>() { @@ -342,7 +342,6 @@ public class LockedView extends ActivatableView { } private void setBalanceColumnCellFactory() { - balanceColumn.getStyleClass().add("last-column"); balanceColumn.setCellValueFactory((addressListItem) -> new ReadOnlyObjectWrapper<>(addressListItem.getValue())); balanceColumn.setCellFactory( new Callback<>() { diff --git a/desktop/src/main/java/haveno/desktop/main/funds/reserved/ReservedView.java b/desktop/src/main/java/haveno/desktop/main/funds/reserved/ReservedView.java index bcef7e6488..3ed2dfea85 100644 --- a/desktop/src/main/java/haveno/desktop/main/funds/reserved/ReservedView.java +++ b/desktop/src/main/java/haveno/desktop/main/funds/reserved/ReservedView.java @@ -122,6 +122,7 @@ public class ReservedView extends ActivatableView { addressColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.address"))); balanceColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.balanceWithCur", Res.getBaseCurrencyCode()))); + GUIUtil.applyTableStyle(tableView); tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY); tableView.setPlaceholder(new AutoTooltipLabel(Res.get("funds.reserved.noFunds"))); @@ -249,7 +250,6 @@ public class ReservedView extends ActivatableView { /////////////////////////////////////////////////////////////////////////////////////////// private void setDateColumnCellFactory() { - dateColumn.getStyleClass().add("first-column"); dateColumn.setCellValueFactory((addressListItem) -> new ReadOnlyObjectWrapper<>(addressListItem.getValue())); dateColumn.setCellFactory(new Callback<>() { @@ -313,7 +313,6 @@ public class ReservedView extends ActivatableView { } private void setAddressColumnCellFactory() { - addressColumn.getStyleClass().add("last-column"); addressColumn.setCellValueFactory((addressListItem) -> new ReadOnlyObjectWrapper<>(addressListItem.getValue())); addressColumn.setCellFactory( @@ -341,7 +340,6 @@ public class ReservedView extends ActivatableView { } private void setBalanceColumnCellFactory() { - balanceColumn.getStyleClass().add("last-column"); balanceColumn.setCellValueFactory((addressListItem) -> new ReadOnlyObjectWrapper<>(addressListItem.getValue())); balanceColumn.setCellFactory( new Callback<>() { diff --git a/desktop/src/main/java/haveno/desktop/main/funds/transactions/TransactionsView.fxml b/desktop/src/main/java/haveno/desktop/main/funds/transactions/TransactionsView.fxml index 24d8f121d7..3f8aa752fd 100644 --- a/desktop/src/main/java/haveno/desktop/main/funds/transactions/TransactionsView.fxml +++ b/desktop/src/main/java/haveno/desktop/main/funds/transactions/TransactionsView.fxml @@ -32,14 +32,14 @@ - - - + + + - - + + diff --git a/desktop/src/main/java/haveno/desktop/main/funds/transactions/TransactionsView.java b/desktop/src/main/java/haveno/desktop/main/funds/transactions/TransactionsView.java index b882782212..66434a8663 100644 --- a/desktop/src/main/java/haveno/desktop/main/funds/transactions/TransactionsView.java +++ b/desktop/src/main/java/haveno/desktop/main/funds/transactions/TransactionsView.java @@ -127,6 +127,8 @@ public class TransactionsView extends ActivatableView { @Override public void initialize() { + GUIUtil.applyTableStyle(tableView); + dateColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.dateTime"))); detailsColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.details"))); addressColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.address"))); @@ -139,6 +141,7 @@ public class TransactionsView extends ActivatableView { tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY_FLEX_LAST_COLUMN); tableView.setPlaceholder(new AutoTooltipLabel(Res.get("funds.tx.noTxAvailable"))); + tableView.getStyleClass().add("non-interactive-table"); setDateColumnCellFactory(); setDetailsColumnCellFactory(); @@ -169,11 +172,6 @@ public class TransactionsView extends ActivatableView { keyEventEventHandler = event -> { // Not intended to be public to users as the feature is not well tested if (Utilities.isAltOrCtrlPressed(KeyCode.R, event)) { - if (revertTxColumn.isVisible()) { - confidenceColumn.getStyleClass().remove("last-column"); - } else { - confidenceColumn.getStyleClass().add("last-column"); - } revertTxColumn.setVisible(!revertTxColumn.isVisible()); } }; @@ -265,7 +263,6 @@ public class TransactionsView extends ActivatableView { /////////////////////////////////////////////////////////////////////////////////////////// private void setDateColumnCellFactory() { - dateColumn.getStyleClass().add("first-column"); dateColumn.setCellValueFactory((addressListItem) -> new ReadOnlyObjectWrapper<>(addressListItem.getValue())); dateColumn.setMaxWidth(200); @@ -400,6 +397,7 @@ public class TransactionsView extends ActivatableView { private void setAmountColumnCellFactory() { amountColumn.setCellValueFactory((addressListItem) -> new ReadOnlyObjectWrapper<>(addressListItem.getValue())); + amountColumn.getStyleClass().add("highlight-text"); amountColumn.setCellFactory( new Callback<>() { @@ -427,6 +425,7 @@ public class TransactionsView extends ActivatableView { private void setTxFeeColumnCellFactory() { txFeeColumn.setCellValueFactory((addressListItem) -> new ReadOnlyObjectWrapper<>(addressListItem.getValue())); + txFeeColumn.getStyleClass().add("highlight-text"); txFeeColumn.setCellFactory( new Callback<>() { @@ -453,6 +452,7 @@ public class TransactionsView extends ActivatableView { private void setMemoColumnCellFactory() { memoColumn.setCellValueFactory((addressListItem) -> new ReadOnlyObjectWrapper<>(addressListItem.getValue())); + memoColumn.getStyleClass().add("highlight-text"); memoColumn.setCellFactory( new Callback<>() { @@ -477,7 +477,6 @@ public class TransactionsView extends ActivatableView { } private void setConfidenceColumnCellFactory() { - confidenceColumn.getStyleClass().add("last-column"); confidenceColumn.setCellValueFactory((addressListItem) -> new ReadOnlyObjectWrapper<>(addressListItem.getValue())); confidenceColumn.setCellFactory( @@ -504,7 +503,6 @@ public class TransactionsView extends ActivatableView { } private void setRevertTxColumnCellFactory() { - revertTxColumn.getStyleClass().add("last-column"); revertTxColumn.setCellValueFactory((addressListItem) -> new ReadOnlyObjectWrapper<>(addressListItem.getValue())); revertTxColumn.setCellFactory( diff --git a/desktop/src/main/java/haveno/desktop/main/funds/withdrawal/WithdrawalView.java b/desktop/src/main/java/haveno/desktop/main/funds/withdrawal/WithdrawalView.java index 730b47adf6..6217bb1e04 100644 --- a/desktop/src/main/java/haveno/desktop/main/funds/withdrawal/WithdrawalView.java +++ b/desktop/src/main/java/haveno/desktop/main/funds/withdrawal/WithdrawalView.java @@ -144,7 +144,7 @@ public class WithdrawalView extends ActivatableView { amountLabel = feeTuple3.first; amountTextField = feeTuple3.second; - amountTextField.setMinWidth(180); + amountTextField.setMinWidth(200); HyperlinkWithIcon sendMaxLink = feeTuple3.third; withdrawMemoTextField = addTopLabelInputTextField(gridPane, ++rowIndex, diff --git a/desktop/src/main/java/haveno/desktop/main/market/MarketView.java b/desktop/src/main/java/haveno/desktop/main/market/MarketView.java index 98f5e75490..fc72de153a 100644 --- a/desktop/src/main/java/haveno/desktop/main/market/MarketView.java +++ b/desktop/src/main/java/haveno/desktop/main/market/MarketView.java @@ -88,10 +88,10 @@ public class MarketView extends ActivatableView { @Override public void initialize() { - offerBookTab.setText(Res.get("market.tabs.offerBook").toUpperCase()); - spreadTab.setText(Res.get("market.tabs.spreadCurrency").toUpperCase()); - spreadTabPaymentMethod.setText(Res.get("market.tabs.spreadPayment").toUpperCase()); - tradesTab.setText(Res.get("market.tabs.trades").toUpperCase()); + offerBookTab.setText(Res.get("market.tabs.offerBook")); + spreadTab.setText(Res.get("market.tabs.spreadCurrency")); + spreadTabPaymentMethod.setText(Res.get("market.tabs.spreadPayment")); + tradesTab.setText(Res.get("market.tabs.trades")); navigationListener = (viewPath, data) -> { if (viewPath.size() == 3 && viewPath.indexOf(MarketView.class) == 1) diff --git a/desktop/src/main/java/haveno/desktop/main/market/offerbook/OfferBookChartView.java b/desktop/src/main/java/haveno/desktop/main/market/offerbook/OfferBookChartView.java index 3d2ec6d884..e89c78dac9 100644 --- a/desktop/src/main/java/haveno/desktop/main/market/offerbook/OfferBookChartView.java +++ b/desktop/src/main/java/haveno/desktop/main/market/offerbook/OfferBookChartView.java @@ -26,6 +26,7 @@ import haveno.common.util.Tuple3; import haveno.common.util.Tuple4; import haveno.core.locale.CurrencyUtil; import haveno.core.locale.Res; +import haveno.core.monetary.Volume; import haveno.core.offer.Offer; import haveno.core.offer.OfferDirection; import haveno.core.util.FormattingUtils; @@ -65,13 +66,13 @@ import javafx.scene.chart.NumberAxis; import javafx.scene.chart.XYChart; import javafx.scene.control.Button; import javafx.scene.control.ComboBox; +import javafx.scene.control.ContentDisplay; import javafx.scene.control.Label; import javafx.scene.control.SingleSelectionModel; import javafx.scene.control.Tab; import javafx.scene.control.TableCell; import javafx.scene.control.TableColumn; import javafx.scene.control.TableView; -import javafx.scene.image.ImageView; import javafx.scene.layout.AnchorPane; import javafx.scene.layout.HBox; import javafx.scene.layout.Priority; @@ -95,7 +96,10 @@ public class OfferBookChartView extends ActivatableViewAndModel currencyComboBox; private Subscription tradeCurrencySubscriber; - private final StringProperty volumeColumnLabel = new SimpleStringProperty(); + private final StringProperty volumeSellColumnLabel = new SimpleStringProperty(); + private final StringProperty volumeBuyColumnLabel = new SimpleStringProperty(); + private final StringProperty amountSellColumnLabel = new SimpleStringProperty(); + private final StringProperty amountBuyColumnLabel = new SimpleStringProperty(); private final StringProperty priceColumnLabel = new SimpleStringProperty(); private AutoTooltipButton sellButton; private AutoTooltipButton buyButton; @@ -106,10 +110,11 @@ public class OfferBookChartView extends ActivatableViewAndModel changeListener; private ListChangeListener currencyListItemsListener; private final double dataLimitFactor = 3; - private final double initialOfferTableViewHeight = 121; + private final double initialOfferTableViewHeight = 78; // decrease as MainView's content-pane's top anchor increases + private final double offerTableExtraMarginBottom = 0; private final Function offerTableViewHeight = (screenSize) -> { // initial visible row count=5, header height=30 - double pixelsPerOfferTableRow = (initialOfferTableViewHeight - 30) / 5.0; + double pixelsPerOfferTableRow = (initialOfferTableViewHeight - offerTableExtraMarginBottom) / 5.0; int extraRows = screenSize <= INITIAL_WINDOW_HEIGHT ? 0 : (int) ((screenSize - INITIAL_WINDOW_HEIGHT) / pixelsPerOfferTableRow); return extraRows == 0 ? initialOfferTableViewHeight : Math.ceil(initialOfferTableViewHeight + ((extraRows + 1) * pixelsPerOfferTableRow)); }; @@ -136,6 +141,7 @@ public class OfferBookChartView extends ActivatableViewAndModel { String code = tradeCurrency.getCode(); - volumeColumnLabel.set(Res.get("offerbook.volume", code)); xAxis.setTickLabelFormatter(new StringConverter<>() { final int cryptoPrecision = 3; final DecimalFormat df = new DecimalFormat(",###"); @@ -230,15 +235,21 @@ public class OfferBookChartView extends ActivatableViewAndModel model.goToOfferView(model.isCrypto() ? OfferDirection.SELL : OfferDirection.BUY)); + sellButton.setId("sell-button-big"); buyHeaderLabel.setText(Res.get("market.offerBook.buyOffersHeaderLabel", viewBaseCurrencyCode)); - buyButton.updateText(Res.get("shared.buyCurrency", viewBaseCurrencyCode, viewPriceCurrencyCode)); + buyButton.updateText(Res.get( "shared.buyCurrency", viewBaseCurrencyCode)); + buyButton.setGraphic(GUIUtil.getCurrencyIconWithBorder(viewBaseCurrencyCode)); + buyButton.setOnAction(e -> model.goToOfferView(model.isCrypto() ? OfferDirection.BUY : OfferDirection.SELL)); + buyButton.setId("buy-button-big"); priceColumnLabel.set(Res.get("shared.priceWithCur", viewPriceCurrencyCode)); @@ -288,8 +299,8 @@ public class OfferBookChartView extends ActivatableViewAndModel model.goToOfferView(OfferDirection.BUY); sellTableRowSelectionListener = (observable, oldValue, newValue) -> model.goToOfferView(OfferDirection.SELL); + buyTableRowSelectionListener = (observable, oldValue, newValue) -> model.goToOfferView(OfferDirection.BUY); havenoWindowVerticalSizeListener = (observable, oldValue, newValue) -> layout(); } @@ -345,12 +356,27 @@ public class OfferBookChartView extends ActivatableViewAndModel, VBox, Button, Label> getOfferTable(OfferDirection direction) { TableView tableView = new TableView<>(); + GUIUtil.applyTableStyle(tableView, false); tableView.setMinHeight(initialOfferTableViewHeight); tableView.setPrefHeight(initialOfferTableViewHeight); tableView.setMinWidth(480); - tableView.getStyleClass().add("offer-table"); + tableView.getStyleClass().addAll("offer-table", "non-interactive-table"); // price TableColumn priceColumn = new TableColumn<>(); @@ -484,12 +511,14 @@ public class OfferBookChartView extends ActivatableViewAndModel volumeColumn = new TableColumn<>(); volumeColumn.setMinWidth(115); volumeColumn.setSortable(false); - volumeColumn.textProperty().bind(volumeColumnLabel); - volumeColumn.getStyleClass().addAll("number-column", "first-column"); + volumeColumn.textProperty().bind(isSellTable ? volumeSellColumnLabel : volumeBuyColumnLabel); + volumeColumn.getStyleClass().addAll("number-column"); volumeColumn.setCellValueFactory((offer) -> new ReadOnlyObjectWrapper<>(offer.getValue())); volumeColumn.setCellFactory( new Callback<>() { @@ -546,7 +575,8 @@ public class OfferBookChartView extends ActivatableViewAndModel amountColumn = new AutoTooltipTableColumn<>(Res.get("shared.XMRMinMax")); + TableColumn amountColumn = new TableColumn<>(); + amountColumn.textProperty().bind(isSellTable ? amountSellColumnLabel : amountBuyColumnLabel); amountColumn.setMinWidth(115); amountColumn.setSortable(false); amountColumn.getStyleClass().add("number-column"); @@ -570,10 +600,8 @@ public class OfferBookChartView extends ActivatableViewAndModel avatarColumn = new AutoTooltipTableColumn<>(isSellOffer ? + TableColumn avatarColumn = new AutoTooltipTableColumn<>(isSellTable ? Res.get("shared.sellerUpperCase") : Res.get("shared.buyerUpperCase")) { { setMinWidth(80); @@ -582,7 +610,7 @@ public class OfferBookChartView extends ActivatableViewAndModel new ReadOnlyObjectWrapper<>(offer.getValue())); avatarColumn.setCellFactory( new Callback<>() { @@ -629,20 +657,16 @@ public class OfferBookChartView extends ActivatableViewAndModel model.goToOfferView(direction)); Region spacer = new Region(); @@ -653,9 +677,9 @@ public class OfferBookChartView extends ActivatableViewAndModel(tableView, vBox, button, titleLabel); diff --git a/desktop/src/main/java/haveno/desktop/main/market/offerbook/OfferBookChartViewModel.java b/desktop/src/main/java/haveno/desktop/main/market/offerbook/OfferBookChartViewModel.java index c7d0c91277..3e8b52184a 100644 --- a/desktop/src/main/java/haveno/desktop/main/market/offerbook/OfferBookChartViewModel.java +++ b/desktop/src/main/java/haveno/desktop/main/market/offerbook/OfferBookChartViewModel.java @@ -26,6 +26,7 @@ import haveno.core.locale.CurrencyUtil; import haveno.core.locale.GlobalSettings; import haveno.core.locale.TradeCurrency; import haveno.core.monetary.Price; +import haveno.core.monetary.Volume; import haveno.core.offer.Offer; import haveno.core.offer.OfferDirection; import haveno.core.offer.OpenOfferManager; @@ -58,6 +59,7 @@ import javafx.collections.ListChangeListener; import javafx.collections.ObservableList; import javafx.scene.chart.XYChart; +import java.math.BigInteger; import java.util.ArrayList; import java.util.Comparator; import java.util.List; @@ -212,10 +214,42 @@ class OfferBookChartViewModel extends ActivatableViewModel { } public boolean isSellOffer(OfferDirection direction) { - // for cryptocurrency, buy direction is to buy XMR, so we need sell offers - // for traditional currency, buy direction is to sell XMR, so we need buy offers - boolean isCryptoCurrency = CurrencyUtil.isCryptoCurrency(getCurrencyCode()); - return isCryptoCurrency ? direction == OfferDirection.BUY : direction == OfferDirection.SELL; + return direction == OfferDirection.SELL; + } + + public double getTotalAmount(OfferDirection direction) { + synchronized (offerBookListItems) { + List offerList = offerBookListItems.stream() + .map(OfferBookListItem::getOffer) + .filter(e -> e.getCurrencyCode().equals(selectedTradeCurrencyProperty.get().getCode()) + && e.getDirection().equals(direction)) + .collect(Collectors.toList()); + BigInteger sum = BigInteger.ZERO; + for (Offer offer : offerList) sum = sum.add(offer.getAmount()); + return HavenoUtils.atomicUnitsToXmr(sum); + } + } + + public Volume getTotalVolume(OfferDirection direction) { + synchronized (offerBookListItems) { + List volumes = offerBookListItems.stream() + .map(OfferBookListItem::getOffer) + .filter(e -> e.getCurrencyCode().equals(selectedTradeCurrencyProperty.get().getCode()) + && e.getDirection().equals(direction)) + .map(Offer::getVolume) + .collect(Collectors.toList()); + try { + return VolumeUtil.sum(volumes); + } catch (Exception e) { + // log.error("Cannot compute total volume because prices are unavailable, currency={}, direction={}", + // selectedTradeCurrencyProperty.get().getCode(), direction); + return null; // expected before prices are available + } + } + } + + public boolean isCrypto() { + return CurrencyUtil.isCryptoCurrency(getCurrencyCode()); } public boolean isMyOffer(Offer offer) { diff --git a/desktop/src/main/java/haveno/desktop/main/market/spread/SpreadView.java b/desktop/src/main/java/haveno/desktop/main/market/spread/SpreadView.java index 9adf595ad4..150b7c888a 100644 --- a/desktop/src/main/java/haveno/desktop/main/market/spread/SpreadView.java +++ b/desktop/src/main/java/haveno/desktop/main/market/spread/SpreadView.java @@ -65,6 +65,8 @@ public class SpreadView extends ActivatableViewAndModel(); + GUIUtil.applyTableStyle(tableView); + tableView.getStyleClass().add("non-interactive-table"); int gridRow = 0; GridPane.setRowIndex(tableView, gridRow); @@ -144,7 +146,7 @@ public class SpreadView extends ActivatableViewAndModel new ReadOnlyObjectWrapper<>(item.getValue())); column.setCellFactory( new Callback<>() { @@ -259,7 +261,7 @@ public class SpreadView extends ActivatableViewAndModel new ReadOnlyObjectWrapper<>(item.getValue())); column.setCellFactory( new Callback<>() { @@ -289,7 +291,7 @@ public class SpreadView extends ActivatableViewAndModel new ReadOnlyObjectWrapper<>(item.getValue())); column.setCellFactory( new Callback<>() { diff --git a/desktop/src/main/java/haveno/desktop/main/market/trades/TradesChartsView.java b/desktop/src/main/java/haveno/desktop/main/market/trades/TradesChartsView.java index 97fcce06d1..7470cf1c4b 100644 --- a/desktop/src/main/java/haveno/desktop/main/market/trades/TradesChartsView.java +++ b/desktop/src/main/java/haveno/desktop/main/market/trades/TradesChartsView.java @@ -686,6 +686,7 @@ public class TradesChartsView extends ActivatableViewAndModel(); + GUIUtil.applyTableStyle(tableView); VBox.setVgrow(tableView, Priority.ALWAYS); + tableView.getStyleClass().add("non-interactive-table"); // date TableColumn dateColumn = new AutoTooltipTableColumn<>(Res.get("shared.dateTime")) { @@ -739,7 +742,7 @@ public class TradesChartsView extends ActivatableViewAndModel new ReadOnlyObjectWrapper<>(tradeStatistics.getValue())); dateColumn.setCellFactory( new Callback<>() { @@ -865,7 +868,7 @@ public class TradesChartsView extends ActivatableViewAndModel paymentMethodColumn = new AutoTooltipTableColumn<>(Res.get("shared.paymentMethod")); - paymentMethodColumn.getStyleClass().add("number-column"); + paymentMethodColumn.getStyleClass().addAll("number-column"); paymentMethodColumn.setCellValueFactory((tradeStatistics) -> new ReadOnlyObjectWrapper<>(tradeStatistics.getValue())); paymentMethodColumn.setCellFactory( new Callback<>() { 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 e28ecff72b..a580d24802 100644 --- a/desktop/src/main/java/haveno/desktop/main/offer/MutableOfferView.java +++ b/desktop/src/main/java/haveno/desktop/main/offer/MutableOfferView.java @@ -85,6 +85,7 @@ import javafx.scene.layout.AnchorPane; import javafx.scene.layout.GridPane; import javafx.scene.layout.HBox; import javafx.scene.layout.Priority; +import javafx.scene.layout.StackPane; import javafx.scene.layout.VBox; import javafx.scene.text.Text; import javafx.util.StringConverter; @@ -149,7 +150,8 @@ public abstract class MutableOfferView> exten private ComboBox paymentAccountsComboBox; private ComboBox currencyComboBox; private ImageView qrCodeImageView; - private VBox currencySelection, fixedPriceBox, percentagePriceBox, currencyTextFieldBox, triggerPriceVBox; + private StackPane qrCodePane; + private VBox paymentAccountsSelection, currencySelection, fixedPriceBox, percentagePriceBox, currencyTextFieldBox, triggerPriceVBox; private HBox fundingHBox, firstRowHBox, secondRowHBox, placeOfferBox, amountValueCurrencyBox, priceAsPercentageValueCurrencyBox, volumeValueCurrencyBox, priceValueCurrencyBox, minAmountValueCurrencyBox, securityDepositAndFeeBox, triggerPriceHBox; @@ -308,7 +310,7 @@ public abstract class MutableOfferView> exten if (!result) { new Popup().headLine(Res.get("popup.warning.noTradingAccountSetup.headline")) .instruction(Res.get("popup.warning.noTradingAccountSetup.msg")) - .actionButtonTextWithGoTo("navigation.account") + .actionButtonTextWithGoTo("mainView.menu.account") .onAction(() -> { navigation.setReturnPath(navigation.getCurrentPath()); navigation.navigateTo(MainView.class, AccountView.class, TraditionalAccountsView.class); @@ -425,7 +427,8 @@ public abstract class MutableOfferView> exten totalToPayTextField.setContentForInfoPopOver(createInfoPopover()); }); - paymentAccountsComboBox.setDisable(true); + paymentAccountsSelection.setDisable(true); + currencySelection.setDisable(true); editOfferElements.forEach(node -> { node.setMouseTransparent(true); @@ -449,8 +452,8 @@ public abstract class MutableOfferView> exten private void updateOfferElementsStyle() { GridPane.setColumnSpan(firstRowHBox, 2); - String activeInputStyle = "input-with-border"; - String readOnlyInputStyle = "input-with-border-readonly"; + String activeInputStyle = "offer-input"; + String readOnlyInputStyle = "offer-input-readonly"; amountValueCurrencyBox.getStyleClass().remove(activeInputStyle); amountValueCurrencyBox.getStyleClass().add(readOnlyInputStyle); priceAsPercentageValueCurrencyBox.getStyleClass().remove(activeInputStyle); @@ -709,7 +712,11 @@ public abstract class MutableOfferView> exten }; extraInfoFocusedListener = (observable, oldValue, newValue) -> { model.onFocusOutExtraInfoTextArea(oldValue, newValue); - extraInfoTextArea.setText(model.extraInfo.get()); + + // avoid setting text area to empty text because blinking caret does not appear + if (model.extraInfo.get() != null && !model.extraInfo.get().isEmpty()) { + extraInfoTextArea.setText(model.extraInfo.get()); + } }; errorMessageListener = (o, oldValue, newValue) -> { @@ -749,7 +756,7 @@ public abstract class MutableOfferView> exten UserThread.runAfter(() -> new Popup().headLine(Res.get("createOffer.success.headline")) .feedback(Res.get("createOffer.success.info")) .dontShowAgainId(key) - .actionButtonTextWithGoTo("navigation.portfolio.myOpenOffers") + .actionButtonTextWithGoTo("portfolio.tab.openOffers") .onAction(this::closeAndGoToOpenOffers) .onClose(this::closeAndGoToOpenOffers) .show(), @@ -973,7 +980,7 @@ public abstract class MutableOfferView> exten private void addGridPane() { gridPane = new GridPane(); gridPane.getStyleClass().add("content-pane"); - gridPane.setPadding(new Insets(25, 25, -1, 25)); + gridPane.setPadding(new Insets(25, 25, 25, 25)); gridPane.setHgap(5); gridPane.setVgap(5); GUIUtil.setDefaultTwoColumnConstraintsForGridPane(gridPane); @@ -995,8 +1002,9 @@ public abstract class MutableOfferView> exten final Tuple3> currencyBoxTuple = addTopLabelComboBox( Res.get("shared.currency"), Res.get("list.currency.select")); + paymentAccountsSelection = tradingAccountBoxTuple.first; currencySelection = currencyBoxTuple.first; - paymentGroupBox.getChildren().addAll(tradingAccountBoxTuple.first, currencySelection); + paymentGroupBox.getChildren().addAll(paymentAccountsSelection, currencySelection); GridPane.setRowIndex(paymentGroupBox, gridRow); GridPane.setColumnSpan(paymentGroupBox, 2); @@ -1007,11 +1015,13 @@ public abstract class MutableOfferView> exten paymentAccountsComboBox = tradingAccountBoxTuple.third; paymentAccountsComboBox.setMinWidth(tradingAccountBoxTuple.first.getMinWidth()); paymentAccountsComboBox.setPrefWidth(tradingAccountBoxTuple.first.getMinWidth()); - editOfferElements.add(tradingAccountBoxTuple.first); + paymentAccountsComboBox.getStyleClass().add("input-with-border"); + editOfferElements.add(paymentAccountsSelection); // we display either currencyComboBox (multi currency account) or currencyTextField (single) currencyComboBox = currencyBoxTuple.third; currencyComboBox.setMaxWidth(tradingAccountBoxTuple.first.getMinWidth() / 2); + currencyComboBox.getStyleClass().add("input-with-border"); editOfferElements.add(currencySelection); currencyComboBox.setConverter(new StringConverter<>() { @Override @@ -1094,6 +1104,7 @@ public abstract class MutableOfferView> exten GridPane.setColumnSpan(extraInfoTitledGroupBg, 3); extraInfoTextArea = new InputTextArea(); + extraInfoTextArea.setText(""); extraInfoTextArea.setPromptText(Res.get("payment.shared.extraInfo.prompt.offer")); extraInfoTextArea.getStyleClass().add("text-area"); extraInfoTextArea.setWrapText(true); @@ -1179,8 +1190,8 @@ public abstract class MutableOfferView> exten totalToPayTextField.setManaged(false); addressTextField.setVisible(false); addressTextField.setManaged(false); - qrCodeImageView.setVisible(false); - qrCodeImageView.setManaged(false); + qrCodePane.setVisible(false); + qrCodePane.setManaged(false); balanceTextField.setVisible(false); balanceTextField.setManaged(false); cancelButton2.setVisible(false); @@ -1196,8 +1207,8 @@ public abstract class MutableOfferView> exten totalToPayTextField.setManaged(true); addressTextField.setVisible(true); addressTextField.setManaged(true); - qrCodeImageView.setVisible(true); - qrCodeImageView.setManaged(true); + qrCodePane.setVisible(true); + qrCodePane.setManaged(true); balanceTextField.setVisible(true); balanceTextField.setManaged(true); cancelButton2.setVisible(true); @@ -1239,21 +1250,23 @@ public abstract class MutableOfferView> exten totalToPayTextField.setVisible(false); GridPane.setMargin(totalToPayTextField, new Insets(60 + heightAdjustment, 10, 0, 0)); - qrCodeImageView = new ImageView(); - qrCodeImageView.setVisible(false); - qrCodeImageView.setFitHeight(150); - qrCodeImageView.setFitWidth(150); - qrCodeImageView.getStyleClass().add("qr-code"); - Tooltip.install(qrCodeImageView, new Tooltip(Res.get("shared.openLargeQRWindow"))); - qrCodeImageView.setOnMouseClicked(e -> UserThread.runAfter( + Tuple2 qrCodeTuple = GUIUtil.getSmallXmrQrCodePane(); + qrCodePane = qrCodeTuple.first; + qrCodeImageView = qrCodeTuple.second; + + Tooltip.install(qrCodePane, new Tooltip(Res.get("shared.openLargeQRWindow"))); + qrCodePane.setOnMouseClicked(e -> UserThread.runAfter( () -> new QRCodeWindow(getMoneroURI()).show(), 200, TimeUnit.MILLISECONDS)); - GridPane.setRowIndex(qrCodeImageView, gridRow); - GridPane.setColumnIndex(qrCodeImageView, 1); - GridPane.setRowSpan(qrCodeImageView, 3); - GridPane.setValignment(qrCodeImageView, VPos.BOTTOM); - GridPane.setMargin(qrCodeImageView, new Insets(Layout.FIRST_ROW_DISTANCE - 9, 0, 0, 10)); - gridPane.getChildren().add(qrCodeImageView); + GridPane.setRowIndex(qrCodePane, gridRow); + GridPane.setColumnIndex(qrCodePane, 1); + GridPane.setRowSpan(qrCodePane, 3); + GridPane.setValignment(qrCodePane, VPos.BOTTOM); + GridPane.setMargin(qrCodePane, new Insets(Layout.FIRST_ROW_DISTANCE - 9, 0, 0, 10)); + gridPane.getChildren().add(qrCodePane); + + qrCodePane.setVisible(false); + qrCodePane.setManaged(false); addressTextField = addAddressTextField(gridPane, ++gridRow, Res.get("shared.tradeWalletAddress")); diff --git a/desktop/src/main/java/haveno/desktop/main/offer/MutableOfferViewModel.java b/desktop/src/main/java/haveno/desktop/main/offer/MutableOfferViewModel.java index 821f9e8a5c..c29781c3bf 100644 --- a/desktop/src/main/java/haveno/desktop/main/offer/MutableOfferViewModel.java +++ b/desktop/src/main/java/haveno/desktop/main/offer/MutableOfferViewModel.java @@ -504,7 +504,7 @@ public abstract class MutableOfferViewModel ext extraInfoStringListener = (ov, oldValue, newValue) -> { if (newValue != null) { - extraInfo.set(newValue); + extraInfo.set(newValue.trim()); UserThread.execute(() -> onExtraInfoTextAreaChanged()); } }; @@ -727,7 +727,7 @@ public abstract class MutableOfferViewModel ext new Popup().warning(Res.get("shared.notEnoughFunds", HavenoUtils.formatXmr(dataModel.totalToPayAsProperty().get(), true), HavenoUtils.formatXmr(dataModel.getTotalBalance(), true))) - .actionButtonTextWithGoTo("navigation.funds.depositFunds") + .actionButtonTextWithGoTo("funds.tab.deposit") .onAction(() -> navigation.navigateTo(MainView.class, FundsView.class, DepositView.class)) .show(); } @@ -1056,7 +1056,7 @@ public abstract class MutableOfferViewModel ext FormattingUtils.formatToPercentWithSymbol(preferences.getMaxPriceDistanceInPercent()))) .actionButtonText(Res.get("createOffer.changePrice")) .onAction(popup::hide) - .closeButtonTextWithGoTo("navigation.settings.preferences") + .closeButtonTextWithGoTo("settings.tab.preferences") .onClose(() -> navigation.navigateTo(MainView.class, SettingsView.class, PreferencesView.class)) .show(); } diff --git a/desktop/src/main/java/haveno/desktop/main/offer/OfferView.java b/desktop/src/main/java/haveno/desktop/main/offer/OfferView.java index 6f6aba7cbc..dd037593fa 100644 --- a/desktop/src/main/java/haveno/desktop/main/offer/OfferView.java +++ b/desktop/src/main/java/haveno/desktop/main/offer/OfferView.java @@ -220,14 +220,14 @@ public abstract class OfferView extends ActivatableView { labelTab.setClosable(false); Label offerLabel = new Label(getOfferLabel()); // use overlay for label for custom formatting offerLabel.getStyleClass().add("titled-group-bg-label"); - offerLabel.setStyle("-fx-font-size: 1.4em;"); + offerLabel.setStyle("-fx-font-size: 1.3em;"); labelTab.setGraphic(offerLabel); - fiatOfferBookTab = new Tab(Res.get("shared.fiat").toUpperCase()); + fiatOfferBookTab = new Tab(Res.get("shared.fiat")); fiatOfferBookTab.setClosable(false); - cryptoOfferBookTab = new Tab(Res.get("shared.crypto").toUpperCase()); + cryptoOfferBookTab = new Tab(Res.get("shared.crypto")); cryptoOfferBookTab.setClosable(false); - otherOfferBookTab = new Tab(Res.get("shared.other").toUpperCase()); + otherOfferBookTab = new Tab(Res.get("shared.other")); otherOfferBookTab.setClosable(false); tabPane.getTabs().addAll(labelTab, fiatOfferBookTab, cryptoOfferBookTab, otherOfferBookTab); } diff --git a/desktop/src/main/java/haveno/desktop/main/offer/offerbook/OfferBookView.java b/desktop/src/main/java/haveno/desktop/main/offer/offerbook/OfferBookView.java index 0248b9c522..e3ed68ccf1 100644 --- a/desktop/src/main/java/haveno/desktop/main/offer/offerbook/OfferBookView.java +++ b/desktop/src/main/java/haveno/desktop/main/offer/offerbook/OfferBookView.java @@ -91,7 +91,6 @@ import javafx.scene.layout.GridPane; import javafx.scene.layout.HBox; import javafx.scene.layout.Priority; import javafx.scene.layout.Region; -import javafx.scene.layout.StackPane; import javafx.scene.layout.VBox; import javafx.scene.text.TextAlignment; import javafx.util.Callback; @@ -179,29 +178,29 @@ abstract public class OfferBookView> paymentBoxTuple = FormBuilder.addTopLabelAutocompleteComboBox( Res.get("offerbook.filterByPaymentMethod")); paymentMethodComboBox = paymentBoxTuple.third; paymentMethodComboBox.setCellFactory(GUIUtil.getPaymentMethodCellFactory()); paymentMethodComboBox.setPrefWidth(250); - - matchingOffersToggleButton = AwesomeDude.createIconToggleButton(AwesomeIcon.USER, null, "1.5em", null); - matchingOffersToggleButton.getStyleClass().add("toggle-button-no-slider"); - matchingOffersToggleButton.setPrefHeight(27); - Tooltip matchingOffersTooltip = new Tooltip(Res.get("offerbook.matchingOffers")); - Tooltip.install(matchingOffersToggleButton, matchingOffersTooltip); + paymentMethodComboBox.getStyleClass().add("input-with-border"); noDepositOffersToggleButton = new ToggleButton(Res.get("offerbook.filterNoDeposit")); noDepositOffersToggleButton.getStyleClass().add("toggle-button-no-slider"); - noDepositOffersToggleButton.setPrefHeight(27); Tooltip noDepositOffersTooltip = new Tooltip(Res.get("offerbook.noDepositOffers")); Tooltip.install(noDepositOffersToggleButton, noDepositOffersTooltip); + matchingOffersToggleButton = AwesomeDude.createIconToggleButton(AwesomeIcon.USER, null, "1.5em", null); + matchingOffersToggleButton.getStyleClass().add("toggle-button-no-slider"); + Tooltip matchingOffersTooltip = new Tooltip(Res.get("offerbook.matchingOffers")); + Tooltip.install(matchingOffersToggleButton, matchingOffersTooltip); + createOfferButton = new AutoTooltipButton(""); createOfferButton.setMinHeight(40); createOfferButton.setGraphicTextGap(10); - createOfferButton.setStyle("-fx-padding: 0 15 0 15;"); + createOfferButton.setStyle("-fx-padding: 7 25 7 25;"); disabledCreateOfferButtonTooltip = new Label(""); disabledCreateOfferButtonTooltip.setMinSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE); disabledCreateOfferButtonTooltip.setMaxSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE); @@ -211,15 +210,17 @@ abstract public class OfferBookView autoToolTipTextField = addTopLabelAutoToolTipTextField(""); VBox filterBox = autoToolTipTextField.first; filterInputField = autoToolTipTextField.third; - filterInputField.setPromptText(Res.get("market.offerBook.filterPrompt")); + filterInputField.setPromptText(Res.get("shared.filter")); + filterInputField.getStyleClass().add("input-with-border"); offerToolsBox.getChildren().addAll(currencyBoxTuple.first, paymentBoxTuple.first, - filterBox, matchingOffersToggleButton, noDepositOffersToggleButton, getSpacer(), createOfferButtonStack); + filterBox, noDepositOffersToggleButton, matchingOffersToggleButton, getSpacer(), createOfferVBox); GridPane.setHgrow(offerToolsBox, Priority.ALWAYS); GridPane.setRowIndex(offerToolsBox, gridRow); @@ -228,6 +229,7 @@ abstract public class OfferBookView(); + GUIUtil.applyTableStyle(tableView); GridPane.setRowIndex(tableView, ++gridRow); GridPane.setColumnIndex(tableView, 0); @@ -405,14 +407,12 @@ abstract public class OfferBookView { log.debug(Res.get("offerbook.removeOffer.success")); if (DontShowAgainLookup.showAgain(key)) - new Popup().instruction(Res.get("offerbook.withdrawFundsHint", Res.get("navigation.funds.availableForWithdrawal"))) - .actionButtonTextWithGoTo("navigation.funds.availableForWithdrawal") + new Popup().instruction(Res.get("offerbook.withdrawFundsHint", Res.get("funds.tab.withdrawal"))) + .actionButtonTextWithGoTo("funds.tab.withdrawal") .onAction(() -> navigation.navigateTo(MainView.class, FundsView.class, WithdrawalView.class)) .dontShowAgainId(key) .show(); @@ -769,7 +768,7 @@ abstract public class OfferBookView { navigation.setReturnPath(navigation.getCurrentPath()); navigation.navigateTo(MainView.class, AccountView.class, accountViewClass); @@ -812,7 +811,7 @@ abstract public class OfferBookView new ReadOnlyObjectWrapper<>(offer.getValue())); column.setCellFactory( new Callback<>() { @@ -1065,7 +1064,6 @@ abstract public class OfferBookView new ReadOnlyObjectWrapper<>(offer.getValue())); column.setCellFactory( new Callback<>() { @@ -1116,7 +1114,12 @@ abstract public class OfferBookView onTakeOffer(offer)); button2.setManaged(false); @@ -1179,8 +1186,8 @@ abstract public class OfferBookView new ReadOnlyObjectWrapper<>(offer.getValue())); column.setCellFactory( new Callback<>() { @@ -1280,8 +1287,8 @@ abstract public class OfferBookView paymentAccountsComboBox; private TextArea extraInfoTextArea; private Label amountDescriptionLabel, @@ -142,7 +143,7 @@ public class TakeOfferView extends ActivatableViewAndModel extraInfoTuple = addCompactTopLabelTextArea(gridPane, ++gridRowNoFundingRequired, Res.get("payment.shared.extraInfo.noDeposit"), ""); + extraInfoLabel = extraInfoTuple.first; + extraInfoLabel.setVisible(false); + extraInfoLabel.setManaged(false); + extraInfoTextArea = extraInfoTuple.second; + extraInfoTextArea.setVisible(false); + extraInfoTextArea.setManaged(false); + extraInfoTextArea.setText(offer.getCombinedExtraInfo().trim()); + extraInfoTextArea.getStyleClass().addAll("text-area", "flat-text-area-with-border"); extraInfoTextArea.setWrapText(true); - extraInfoTextArea.setPrefHeight(75); - extraInfoTextArea.setMinHeight(75); - extraInfoTextArea.setMaxHeight(150); + extraInfoTextArea.setMaxHeight(300); extraInfoTextArea.setEditable(false); - GridPane.setRowIndex(extraInfoTextArea, lastGridRowNoFundingRequired); + GUIUtil.adjustHeightAutomatically(extraInfoTextArea); + GridPane.setRowIndex(extraInfoTextArea, gridRowNoFundingRequired); GridPane.setColumnSpan(extraInfoTextArea, GridPane.REMAINING); GridPane.setColumnIndex(extraInfoTextArea, 0); // move up take offer buttons - GridPane.setRowIndex(takeOfferBox, lastGridRowNoFundingRequired + 1); + GridPane.setRowIndex(takeOfferBox, gridRowNoFundingRequired + 1); GridPane.setMargin(takeOfferBox, new Insets(15, 0, 0, 0)); } } @@ -490,19 +500,33 @@ public class TakeOfferView extends ActivatableViewAndModel new Popup().warning(newValue + "\n\n" + Res.get("takeOffer.alreadyPaidInFunds")) - .actionButtonTextWithGoTo("navigation.funds.availableForWithdrawal") + .actionButtonTextWithGoTo("funds.tab.withdrawal") .onAction(() -> { errorPopupDisplayed.set(true); model.resetOfferWarning(); @@ -663,6 +687,7 @@ public class TakeOfferView extends ActivatableViewAndModel new Popup().headLine(Res.get("takeOffer.success.headline")) .feedback(Res.get("takeOffer.success.info")) - .actionButtonTextWithGoTo("navigation.portfolio.pending") + .actionButtonTextWithGoTo("portfolio.tab.pendingTrades") .dontShowAgainId(key) .onAction(() -> { UserThread.runAfter( @@ -755,7 +780,7 @@ public class TakeOfferView extends ActivatableViewAndModel tuple = add2ButtonsWithBox(gridPane, ++gridRow, Res.get("shared.nextStep"), Res.get("shared.cancel"), 15, true); - buttonBox = tuple.third; + nextButtonBox = tuple.third; nextButton = tuple.first; nextButton.setMaxWidth(200); @@ -870,7 +895,7 @@ public class TakeOfferView extends ActivatableViewAndModel UserThread.runAfter( + Tuple2 qrCodeTuple = GUIUtil.getSmallXmrQrCodePane(); + qrCodePane = qrCodeTuple.first; + qrCodeImageView = qrCodeTuple.second; + + Tooltip.install(qrCodePane, new Tooltip(Res.get("shared.openLargeQRWindow"))); + qrCodePane.setOnMouseClicked(e -> UserThread.runAfter( () -> new QRCodeWindow(getMoneroURI()).show(), 200, TimeUnit.MILLISECONDS)); - GridPane.setRowIndex(qrCodeImageView, gridRow); - GridPane.setColumnIndex(qrCodeImageView, 1); - GridPane.setRowSpan(qrCodeImageView, 3); - GridPane.setValignment(qrCodeImageView, VPos.BOTTOM); - GridPane.setMargin(qrCodeImageView, new Insets(Layout.FIRST_ROW_DISTANCE - 9, 0, 0, 10)); - gridPane.getChildren().add(qrCodeImageView); + GridPane.setRowIndex(qrCodePane, gridRow); + GridPane.setColumnIndex(qrCodePane, 1); + GridPane.setRowSpan(qrCodePane, 3); + GridPane.setValignment(qrCodePane, VPos.BOTTOM); + GridPane.setMargin(qrCodePane, new Insets(Layout.FIRST_ROW_DISTANCE - 9, 0, 0, 10)); + gridPane.getChildren().add(qrCodePane); + + qrCodePane.setVisible(false); + qrCodePane.setManaged(false); addressTextField = addAddressTextField(gridPane, ++gridRow, Res.get("shared.tradeWalletAddress")); addressTextField.setVisible(false); + addressTextField.setManaged(false); balanceTextField = addBalanceTextField(gridPane, ++gridRow, Res.get("shared.tradeWalletBalance")); balanceTextField.setVisible(false); + balanceTextField.setManaged(false); fundingHBox = new HBox(); fundingHBox.setVisible(false); @@ -1000,6 +1033,7 @@ public class TakeOfferView extends ActivatableViewAndModel { new GenericMessageWindow() .preamble(Res.get("payment.tradingRestrictions")) - .instruction(offer.getCombinedExtraInfo()) + .instruction(offer.getCombinedExtraInfo().trim()) .actionButtonText(Res.get("shared.iConfirm")) .closeButtonText(Res.get("shared.close")) .width(Layout.INITIAL_WINDOW_WIDTH) diff --git a/desktop/src/main/java/haveno/desktop/main/offer/takeoffer/TakeOfferViewModel.java b/desktop/src/main/java/haveno/desktop/main/offer/takeoffer/TakeOfferViewModel.java index c73ce988c6..1ad0f9547a 100644 --- a/desktop/src/main/java/haveno/desktop/main/offer/takeoffer/TakeOfferViewModel.java +++ b/desktop/src/main/java/haveno/desktop/main/offer/takeoffer/TakeOfferViewModel.java @@ -256,7 +256,7 @@ class TakeOfferViewModel extends ActivatableWithDataModel im new Popup().warning(Res.get("shared.notEnoughFunds", HavenoUtils.formatXmr(dataModel.getTotalToPay().get(), true), HavenoUtils.formatXmr(dataModel.getTotalAvailableBalance(), true))) - .actionButtonTextWithGoTo("navigation.funds.depositFunds") + .actionButtonTextWithGoTo("funds.tab.deposit") .onAction(() -> navigation.navigateTo(MainView.class, FundsView.class, DepositView.class)) .show(); return false; diff --git a/desktop/src/main/java/haveno/desktop/main/overlays/Overlay.java b/desktop/src/main/java/haveno/desktop/main/overlays/Overlay.java index e216b14ed9..ea29d4b7ec 100644 --- a/desktop/src/main/java/haveno/desktop/main/overlays/Overlay.java +++ b/desktop/src/main/java/haveno/desktop/main/overlays/Overlay.java @@ -19,6 +19,8 @@ package haveno.desktop.main.overlays; import com.google.common.reflect.TypeToken; import de.jensd.fx.fontawesome.AwesomeIcon; +import de.jensd.fx.glyphs.materialdesignicons.MaterialDesignIcon; +import de.jensd.fx.glyphs.materialdesignicons.MaterialDesignIconView; import haveno.common.Timer; import haveno.common.UserThread; import haveno.common.config.Config; @@ -42,8 +44,6 @@ import javafx.animation.Interpolator; import javafx.animation.KeyFrame; import javafx.animation.KeyValue; import javafx.animation.Timeline; -import javafx.application.Platform; -import javafx.beans.binding.Bindings; import javafx.beans.property.BooleanProperty; import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.value.ChangeListener; @@ -52,6 +52,7 @@ import javafx.geometry.HPos; import javafx.geometry.Insets; import javafx.geometry.NodeOrientation; import javafx.geometry.Pos; +import javafx.scene.Cursor; import javafx.scene.Node; import javafx.scene.PerspectiveCamera; import javafx.scene.Scene; @@ -122,7 +123,7 @@ public abstract class Overlay> { Notification(AnimationType.SlideFromRightTop, ChangeBackgroundType.BlurLight), BackgroundInfo(AnimationType.SlideDownFromCenterTop, ChangeBackgroundType.BlurUltraLight), - Feedback(AnimationType.SlideDownFromCenterTop, ChangeBackgroundType.Darken), + Feedback(AnimationType.SlideDownFromCenterTop, ChangeBackgroundType.BlurLight), Information(AnimationType.FadeInAtCenter, ChangeBackgroundType.BlurLight), Instruction(AnimationType.ScaleFromCenter, ChangeBackgroundType.BlurLight), @@ -141,6 +142,9 @@ public abstract class Overlay> { } } + private static int numCenterOverlays = 0; + private static int numBlurEffects = 0; + protected final static double DEFAULT_WIDTH = 668; protected Stage stage; protected GridPane gridPane; @@ -168,7 +172,7 @@ public abstract class Overlay> { protected boolean showScrollPane = false; protected TextArea messageTextArea; - protected Label headlineIcon, copyIcon, headLineLabel; + protected Label headlineIcon, copyLabel, headLineLabel; protected String headLine, message, closeButtonText, actionButtonText, secondaryActionButtonText, dontShowAgainId, dontShowAgainText, truncatedMessage; @@ -249,6 +253,7 @@ public abstract class Overlay> { protected void animateHide() { animateHide(() -> { + if (isCentered()) numCenterOverlays--; removeEffectFromBackground(); if (stage != null) @@ -541,6 +546,14 @@ public abstract class Overlay> { layout(); + // add dropshadow if light mode or multiple centered overlays + if (isCentered()) { + numCenterOverlays++; + } + if (!CssTheme.isDarkTheme() || numCenterOverlays > 1) { + getRootContainer().getStyleClass().add("popup-dropshadow"); + } + addEffectToBackground(); // On Linux the owner stage does not move the child stage as it does on Mac @@ -739,6 +752,8 @@ public abstract class Overlay> { } protected void addEffectToBackground() { + numBlurEffects++; + if (numBlurEffects > 1) return; if (type.changeBackgroundType == ChangeBackgroundType.BlurUltraLight) MainView.blurUltraLight(); else if (type.changeBackgroundType == ChangeBackgroundType.BlurLight) @@ -758,12 +773,14 @@ public abstract class Overlay> { if (headLineLabel != null) { - if (copyIcon != null) { - copyIcon.getStyleClass().add("popup-icon-information"); - copyIcon.setManaged(true); - copyIcon.setVisible(true); - FormBuilder.getIconForLabel(AwesomeIcon.COPY, copyIcon, "1.1em"); - copyIcon.addEventHandler(MOUSE_CLICKED, mouseEvent -> { + if (copyLabel != null) { + copyLabel.getStyleClass().add("popup-icon-information"); + copyLabel.setManaged(true); + copyLabel.setVisible(true); + MaterialDesignIconView copyIcon = new MaterialDesignIconView(MaterialDesignIcon.CONTENT_COPY, "1.2em"); + copyLabel.setGraphic(copyIcon); + copyLabel.setCursor(Cursor.HAND); + copyLabel.addEventHandler(MOUSE_CLICKED, mouseEvent -> { if (message != null) { Utilities.copyToClipboard(getClipboardText()); Tooltip tp = new Tooltip(Res.get("shared.copiedToClipboard")); @@ -808,6 +825,8 @@ public abstract class Overlay> { } protected void removeEffectFromBackground() { + numBlurEffects--; + if (numBlurEffects > 0) return; MainView.removeEffect(); } @@ -828,15 +847,15 @@ public abstract class Overlay> { headLineLabel.setStyle(headlineStyle); if (message != null) { - copyIcon = new Label(); - copyIcon.setManaged(false); - copyIcon.setVisible(false); - copyIcon.setPadding(new Insets(3)); - copyIcon.setTooltip(new Tooltip(Res.get("shared.copyToClipboard"))); + copyLabel = new Label(); + copyLabel.setManaged(false); + copyLabel.setVisible(false); + copyLabel.setPadding(new Insets(3)); + copyLabel.setTooltip(new Tooltip(Res.get("shared.copyToClipboard"))); final Pane spacer = new Pane(); HBox.setHgrow(spacer, Priority.ALWAYS); spacer.setMinSize(Layout.PADDING, 1); - hBox.getChildren().addAll(headlineIcon, headLineLabel, spacer, copyIcon); + hBox.getChildren().addAll(headlineIcon, headLineLabel, spacer, copyLabel); } else { hBox.getChildren().addAll(headlineIcon, headLineLabel); } @@ -852,23 +871,8 @@ public abstract class Overlay> { if (message != null) { messageTextArea = new TextArea(truncatedMessage); messageTextArea.setEditable(false); - messageTextArea.getStyleClass().add("text-area-no-border"); - messageTextArea.sceneProperty().addListener((o, oldScene, newScene) -> { - if (newScene != null) { - // avoid javafx css warning - CssTheme.loadSceneStyles(newScene, CssTheme.CSS_THEME_LIGHT, false); - messageTextArea.applyCss(); - var text = messageTextArea.lookup(".text"); - - messageTextArea.prefHeightProperty().bind(Bindings.createDoubleBinding(() -> { - return messageTextArea.getFont().getSize() + text.getBoundsInLocal().getHeight(); - }, text.boundsInLocalProperty())); - - text.boundsInLocalProperty().addListener((observableBoundsAfter, boundsBefore, boundsAfter) -> { - Platform.runLater(() -> messageTextArea.requestLayout()); - }); - } - }); + messageTextArea.getStyleClass().add("text-area-popup"); + GUIUtil.adjustHeightAutomatically(messageTextArea); messageTextArea.setWrapText(true); Region messageRegion; @@ -1093,4 +1097,10 @@ public abstract class Overlay> { ", message='" + message + '\'' + '}'; } + + private boolean isCentered() { + if (type.animationType == AnimationType.SlideDownFromCenterTop) return false; + if (type.animationType == AnimationType.SlideFromRightTop) return false; + return true; + } } diff --git a/desktop/src/main/java/haveno/desktop/main/overlays/editor/PasswordPopup.java b/desktop/src/main/java/haveno/desktop/main/overlays/editor/PasswordPopup.java index bd169b3a4b..0af066c9aa 100644 --- a/desktop/src/main/java/haveno/desktop/main/overlays/editor/PasswordPopup.java +++ b/desktop/src/main/java/haveno/desktop/main/overlays/editor/PasswordPopup.java @@ -71,7 +71,7 @@ public class PasswordPopup extends Overlay { @Override public void show() { - actionButtonText("CONFIRM"); + actionButtonText("Confirm"); createGridPane(); addHeadLine(); addContent(); diff --git a/desktop/src/main/java/haveno/desktop/main/overlays/notifications/Notification.java b/desktop/src/main/java/haveno/desktop/main/overlays/notifications/Notification.java index 57a9550e3a..ebfcb4dcd4 100644 --- a/desktop/src/main/java/haveno/desktop/main/overlays/notifications/Notification.java +++ b/desktop/src/main/java/haveno/desktop/main/overlays/notifications/Notification.java @@ -42,6 +42,7 @@ public class Notification extends Overlay { private boolean hasBeenDisplayed; private boolean autoClose; private Timer autoCloseTimer; + private static final int BORDER_PADDING = 10; public Notification() { width = 413; // 320 visible bg because of insets @@ -205,8 +206,8 @@ public class Notification extends Overlay { Window window = owner.getScene().getWindow(); double titleBarHeight = window.getHeight() - owner.getScene().getHeight(); double shadowInset = 44; - stage.setX(Math.round(window.getX() + window.getWidth() + shadowInset - stage.getWidth())); - stage.setY(Math.round(window.getY() + titleBarHeight - shadowInset)); + stage.setX(Math.round(window.getX() + window.getWidth() + shadowInset - stage.getWidth() - BORDER_PADDING)); + stage.setY(Math.round(window.getY() + titleBarHeight - shadowInset + BORDER_PADDING)); } @Override diff --git a/desktop/src/main/java/haveno/desktop/main/overlays/notifications/NotificationCenter.java b/desktop/src/main/java/haveno/desktop/main/overlays/notifications/NotificationCenter.java index 829a0640d5..f9211cd17f 100644 --- a/desktop/src/main/java/haveno/desktop/main/overlays/notifications/NotificationCenter.java +++ b/desktop/src/main/java/haveno/desktop/main/overlays/notifications/NotificationCenter.java @@ -216,7 +216,7 @@ public class NotificationCenter { if (DontShowAgainLookup.showAgain(key)) { Notification notification = new Notification().tradeHeadLine(trade.getShortId()).message(message); if (navigation.getCurrentPath() != null && !navigation.getCurrentPath().contains(PendingTradesView.class)) { - notification.actionButtonTextWithGoTo("navigation.portfolio.pending") + notification.actionButtonTextWithGoTo("portfolio.tab.pendingTrades") .onAction(() -> { DontShowAgainLookup.dontShowAgain(key, true); navigation.navigateTo(MainView.class, PortfolioView.class, PendingTradesView.class); @@ -318,7 +318,7 @@ public class NotificationCenter { private void goToSupport(Trade trade, String message, Class viewClass) { Notification notification = new Notification().disputeHeadLine(trade.getShortId()).message(message); if (navigation.getCurrentPath() != null && !navigation.getCurrentPath().contains(viewClass)) { - notification.actionButtonTextWithGoTo("navigation.support") + notification.actionButtonTextWithGoTo("mainView.menu.support") .onAction(() -> navigation.navigateTo(MainView.class, SupportView.class, viewClass)) .show(); } else { diff --git a/desktop/src/main/java/haveno/desktop/main/overlays/windows/ContractWindow.java b/desktop/src/main/java/haveno/desktop/main/overlays/windows/ContractWindow.java index 6a238e56ee..7f72fb2e10 100644 --- a/desktop/src/main/java/haveno/desktop/main/overlays/windows/ContractWindow.java +++ b/desktop/src/main/java/haveno/desktop/main/overlays/windows/ContractWindow.java @@ -46,6 +46,7 @@ import static haveno.desktop.util.FormBuilder.addConfirmationLabelTextField; import static haveno.desktop.util.FormBuilder.addConfirmationLabelTextFieldWithCopyIcon; import static haveno.desktop.util.FormBuilder.addLabelExplorerAddressTextField; import static haveno.desktop.util.FormBuilder.addLabelTxIdTextField; +import static haveno.desktop.util.FormBuilder.addSeparator; import static haveno.desktop.util.FormBuilder.addTitledGroupBg; import haveno.desktop.util.Layout; import haveno.network.p2p.NodeAddress; @@ -137,15 +138,20 @@ public class ContractWindow extends Overlay { addTitledGroupBg(gridPane, ++rowIndex, rows, Res.get("contractWindow.title")); addConfirmationLabelTextField(gridPane, rowIndex, Res.get("shared.offerId"), offer.getId(), Layout.TWICE_FIRST_ROW_DISTANCE); + addSeparator(gridPane, ++rowIndex); addConfirmationLabelTextField(gridPane, ++rowIndex, Res.get("contractWindow.dates"), DisplayUtils.formatDateTime(offer.getDate()) + " / " + DisplayUtils.formatDateTime(dispute.getTradeDate())); String currencyCode = offer.getCurrencyCode(); + addSeparator(gridPane, ++rowIndex); addConfirmationLabelTextField(gridPane, ++rowIndex, Res.get("shared.offerType"), DisplayUtils.getDirectionBothSides(offer.getDirection(), offer.isPrivateOffer())); + addSeparator(gridPane, ++rowIndex); addConfirmationLabelTextField(gridPane, ++rowIndex, Res.get("shared.tradePrice"), FormattingUtils.formatPrice(contract.getPrice())); + addSeparator(gridPane, ++rowIndex); addConfirmationLabelTextField(gridPane, ++rowIndex, Res.get("shared.tradeAmount"), HavenoUtils.formatXmr(contract.getTradeAmount(), true)); + addSeparator(gridPane, ++rowIndex); addConfirmationLabelTextField(gridPane, ++rowIndex, VolumeUtil.formatVolumeLabel(currencyCode, ":"), @@ -157,16 +163,20 @@ public class ContractWindow extends Overlay { Res.getWithColAndCap("shared.seller") + " " + HavenoUtils.formatXmr(offer.getOfferPayload().getSellerSecurityDepositForTradeAmount(contract.getTradeAmount()), true); + addSeparator(gridPane, ++rowIndex); addConfirmationLabelTextField(gridPane, ++rowIndex, Res.get("shared.securityDeposit"), securityDeposit); + addSeparator(gridPane, ++rowIndex); addConfirmationLabelTextField(gridPane, ++rowIndex, Res.get("contractWindow.xmrAddresses"), contract.getBuyerPayoutAddressString() + " / " + contract.getSellerPayoutAddressString()); + addSeparator(gridPane, ++rowIndex); addConfirmationLabelTextField(gridPane, ++rowIndex, Res.get("contractWindow.onions"), contract.getBuyerNodeAddress().getFullAddress() + " / " + contract.getSellerNodeAddress().getFullAddress()); + addSeparator(gridPane, ++rowIndex); addConfirmationLabelTextField(gridPane, ++rowIndex, Res.get("contractWindow.accountAge"), @@ -176,16 +186,19 @@ public class ContractWindow extends Overlay { DisputeManager> disputeManager = getDisputeManager(dispute); String nrOfDisputesAsBuyer = disputeManager != null ? disputeManager.getNrOfDisputes(true, contract) : ""; String nrOfDisputesAsSeller = disputeManager != null ? disputeManager.getNrOfDisputes(false, contract) : ""; + addSeparator(gridPane, ++rowIndex); addConfirmationLabelTextField(gridPane, ++rowIndex, Res.get("contractWindow.numDisputes"), nrOfDisputesAsBuyer + " / " + nrOfDisputesAsSeller); + addSeparator(gridPane, ++rowIndex); addConfirmationLabelTextField(gridPane, ++rowIndex, Res.get("shared.paymentDetails", Res.get("shared.buyer")), dispute.getBuyerPaymentAccountPayload() != null ? dispute.getBuyerPaymentAccountPayload().getPaymentDetails() : "NA"); + addSeparator(gridPane, ++rowIndex); addConfirmationLabelTextField(gridPane, ++rowIndex, Res.get("shared.paymentDetails", Res.get("shared.seller")), @@ -219,6 +232,7 @@ public class ContractWindow extends Overlay { NodeAddress agentNodeAddress = disputeManager.getAgentNodeAddress(dispute); if (agentNodeAddress != null) { String value = agentMatrixUserName + " (" + agentNodeAddress.getFullAddress() + ")"; + addSeparator(gridPane, ++rowIndex); addConfirmationLabelTextField(gridPane, ++rowIndex, title, value); } } @@ -232,40 +246,53 @@ public class ContractWindow extends Overlay { countries = CountryUtil.getCodesString(acceptedCountryCodes); tooltip = new Tooltip(CountryUtil.getNamesByCodesString(acceptedCountryCodes)); } + addSeparator(gridPane, ++rowIndex); addConfirmationLabelTextField(gridPane, ++rowIndex, Res.get("shared.acceptedTakerCountries"), countries) .second.setTooltip(tooltip); } if (showAcceptedBanks) { if (offer.getPaymentMethod().equals(PaymentMethod.SAME_BANK)) { + addSeparator(gridPane, ++rowIndex); addConfirmationLabelTextField(gridPane, ++rowIndex, Res.get("shared.bankName"), acceptedBanks.get(0)); } else if (offer.getPaymentMethod().equals(PaymentMethod.SPECIFIC_BANKS)) { String value = Joiner.on(", ").join(acceptedBanks); Tooltip tooltip = new Tooltip(Res.get("shared.acceptedBanks") + value); + addSeparator(gridPane, ++rowIndex); addConfirmationLabelTextField(gridPane, ++rowIndex, Res.get("shared.acceptedBanks"), value) .second.setTooltip(tooltip); } } + addSeparator(gridPane, ++rowIndex); addLabelTxIdTextField(gridPane, ++rowIndex, Res.get("shared.makerDepositTransactionId"), contract.getMakerDepositTxHash()); - if (contract.getTakerDepositTxHash() != null) + if (contract.getTakerDepositTxHash() != null) { addLabelTxIdTextField(gridPane, ++rowIndex, Res.get("shared.takerDepositTransactionId"), contract.getTakerDepositTxHash()); + } - if (dispute.getDelayedPayoutTxId() != null) + if (dispute.getDelayedPayoutTxId() != null) { + addSeparator(gridPane, ++rowIndex); addLabelTxIdTextField(gridPane, ++rowIndex, Res.get("shared.delayedPayoutTxId"), dispute.getDelayedPayoutTxId()); + } if (dispute.getDonationAddressOfDelayedPayoutTx() != null) { + addSeparator(gridPane, ++rowIndex); addLabelExplorerAddressTextField(gridPane, ++rowIndex, Res.get("shared.delayedPayoutTxReceiverAddress"), dispute.getDonationAddressOfDelayedPayoutTx()); } - if (dispute.getPayoutTxSerialized() != null) + if (dispute.getPayoutTxSerialized() != null) { + addSeparator(gridPane, ++rowIndex); addLabelTxIdTextField(gridPane, ++rowIndex, Res.get("shared.payoutTxId"), dispute.getPayoutTxId()); + } - if (dispute.getContractHash() != null) + if (dispute.getContractHash() != null) { + addSeparator(gridPane, ++rowIndex); addConfirmationLabelTextFieldWithCopyIcon(gridPane, ++rowIndex, Res.get("contractWindow.contractHash"), Utils.HEX.encode(dispute.getContractHash())).second.setMouseTransparent(false); + } + addSeparator(gridPane, ++rowIndex); Button viewContractButton = addConfirmationLabelButton(gridPane, ++rowIndex, Res.get("shared.contractAsJson"), Res.get("shared.viewContractAsJson"), 0).second; viewContractButton.setDefaultButton(false); diff --git a/desktop/src/main/java/haveno/desktop/main/overlays/windows/DisputeSummaryWindow.java b/desktop/src/main/java/haveno/desktop/main/overlays/windows/DisputeSummaryWindow.java index 5004d240d8..49931e202b 100644 --- a/desktop/src/main/java/haveno/desktop/main/overlays/windows/DisputeSummaryWindow.java +++ b/desktop/src/main/java/haveno/desktop/main/overlays/windows/DisputeSummaryWindow.java @@ -188,7 +188,7 @@ public class DisputeSummaryWindow extends Overlay { protected void createGridPane() { super.createGridPane(); gridPane.setPadding(new Insets(35, 40, 30, 40)); - gridPane.getStyleClass().add("grid-pane"); + gridPane.getStyleClass().addAll("grid-pane", "popup-with-input"); gridPane.getColumnConstraints().get(0).setHalignment(HPos.LEFT); gridPane.setPrefWidth(width); } @@ -413,11 +413,13 @@ public class DisputeSummaryWindow extends Overlay { private void addPayoutAmountTextFields() { buyerPayoutAmountInputTextField = new InputTextField(); buyerPayoutAmountInputTextField.setLabelFloat(true); + buyerPayoutAmountInputTextField.getStyleClass().add("label-float"); buyerPayoutAmountInputTextField.setEditable(false); buyerPayoutAmountInputTextField.setPromptText(Res.get("disputeSummaryWindow.payoutAmount.buyer")); sellerPayoutAmountInputTextField = new InputTextField(); sellerPayoutAmountInputTextField.setLabelFloat(true); + sellerPayoutAmountInputTextField.getStyleClass().add("label-float"); sellerPayoutAmountInputTextField.setPromptText(Res.get("disputeSummaryWindow.payoutAmount.seller")); sellerPayoutAmountInputTextField.setEditable(false); diff --git a/desktop/src/main/java/haveno/desktop/main/overlays/windows/GenericMessageWindow.java b/desktop/src/main/java/haveno/desktop/main/overlays/windows/GenericMessageWindow.java index 01d119a479..9ee42ae27c 100644 --- a/desktop/src/main/java/haveno/desktop/main/overlays/windows/GenericMessageWindow.java +++ b/desktop/src/main/java/haveno/desktop/main/overlays/windows/GenericMessageWindow.java @@ -18,6 +18,7 @@ package haveno.desktop.main.overlays.windows; import haveno.desktop.main.overlays.Overlay; +import haveno.desktop.util.GUIUtil; import haveno.desktop.util.Layout; import javafx.scene.control.Label; import javafx.scene.control.TextArea; @@ -28,6 +29,7 @@ import static haveno.desktop.util.FormBuilder.addTextArea; public class GenericMessageWindow extends Overlay { private String preamble; + private static final double MAX_TEXT_AREA_HEIGHT = 250; public GenericMessageWindow() { super(); @@ -54,20 +56,11 @@ public class GenericMessageWindow extends Overlay { } checkNotNull(message, "message must not be null"); TextArea textArea = addTextArea(gridPane, ++rowIndex, "", 10); + textArea.getStyleClass().add("flat-text-area-with-border"); textArea.setText(message); textArea.setEditable(false); textArea.setWrapText(true); - // sizes the textArea to fit within its parent container - double verticalSizePercentage = ensureRange(countLines(message) / 20.0, 0.2, 0.7); - textArea.setPrefSize(Layout.INITIAL_WINDOW_WIDTH, Layout.INITIAL_WINDOW_HEIGHT * verticalSizePercentage); - } - - private static int countLines(String str) { - String[] lines = str.split("\r\n|\r|\n"); - return lines.length; - } - - private static double ensureRange(double value, double min, double max) { - return Math.min(Math.max(value, min), max); + textArea.setPrefWidth(Layout.INITIAL_WINDOW_WIDTH); + GUIUtil.adjustHeightAutomatically(textArea, MAX_TEXT_AREA_HEIGHT); } } diff --git a/desktop/src/main/java/haveno/desktop/main/overlays/windows/OfferDetailsWindow.java b/desktop/src/main/java/haveno/desktop/main/overlays/windows/OfferDetailsWindow.java index dd645246f7..b0c00ca60f 100644 --- a/desktop/src/main/java/haveno/desktop/main/overlays/windows/OfferDetailsWindow.java +++ b/desktop/src/main/java/haveno/desktop/main/overlays/windows/OfferDetailsWindow.java @@ -20,10 +20,16 @@ package haveno.desktop.main.overlays.windows; import com.google.common.base.Joiner; import com.google.inject.Inject; import com.google.inject.name.Named; +import com.jfoenix.controls.JFXButton; +import com.jfoenix.controls.JFXTextField; + +import de.jensd.fx.glyphs.materialdesignicons.MaterialDesignIcon; +import de.jensd.fx.glyphs.materialdesignicons.MaterialDesignIconView; import haveno.common.UserThread; import haveno.common.crypto.KeyRing; import haveno.common.util.Tuple2; import haveno.common.util.Tuple4; +import haveno.common.util.Utilities; import haveno.core.locale.CountryUtil; import haveno.core.locale.Res; import haveno.core.monetary.Price; @@ -45,30 +51,37 @@ import haveno.desktop.components.BusyAnimation; import haveno.desktop.main.overlays.Overlay; import haveno.desktop.main.overlays.editor.PasswordPopup; import haveno.desktop.main.overlays.popups.Popup; -import haveno.desktop.util.CssTheme; import haveno.desktop.util.DisplayUtils; import static haveno.desktop.util.FormBuilder.addButtonAfterGroup; import static haveno.desktop.util.FormBuilder.addButtonBusyAnimationLabelAfterGroup; import static haveno.desktop.util.FormBuilder.addConfirmationLabelLabel; import static haveno.desktop.util.FormBuilder.addConfirmationLabelTextArea; import static haveno.desktop.util.FormBuilder.addConfirmationLabelTextFieldWithCopyIcon; +import static haveno.desktop.util.FormBuilder.addLabel; +import static haveno.desktop.util.FormBuilder.addSeparator; import static haveno.desktop.util.FormBuilder.addTitledGroupBg; import haveno.desktop.util.GUIUtil; import haveno.desktop.util.Layout; import java.math.BigInteger; import java.util.List; import java.util.Optional; -import javafx.application.Platform; -import javafx.beans.binding.Bindings; import javafx.geometry.HPos; import javafx.geometry.Insets; +import javafx.geometry.Pos; +import javafx.geometry.VPos; +import javafx.scene.Node; import javafx.scene.control.Button; +import javafx.scene.control.ContentDisplay; import javafx.scene.control.Label; import javafx.scene.control.TextArea; import javafx.scene.control.Tooltip; import javafx.scene.image.ImageView; import javafx.scene.layout.GridPane; import javafx.scene.layout.HBox; +import javafx.scene.layout.Priority; +import javafx.scene.layout.VBox; +import javafx.scene.paint.Color; + import org.fxmisc.easybind.EasyBind; import org.fxmisc.easybind.Subscription; import org.slf4j.Logger; @@ -115,7 +128,7 @@ public class OfferDetailsWindow extends Overlay { this.tradePrice = tradePrice; rowIndex = -1; - width = 1118; + width = Layout.DETAILS_WINDOW_WIDTH; createGridPane(); addContent(); display(); @@ -124,7 +137,7 @@ public class OfferDetailsWindow extends Overlay { public void show(Offer offer) { this.offer = offer; rowIndex = -1; - width = 1118; + width = Layout.DETAILS_WINDOW_WIDTH; createGridPane(); addContent(); display(); @@ -194,7 +207,7 @@ public class OfferDetailsWindow extends Overlay { rows++; } - addTitledGroupBg(gridPane, ++rowIndex, rows, Res.get(offer.isPrivateOffer() ? "shared.Offer" : "shared.Offer")); + addTitledGroupBg(gridPane, ++rowIndex, rows, Res.get("shared.Offer")); String counterCurrencyDirectionInfo = ""; String xmrDirectionInfo = ""; @@ -218,17 +231,22 @@ public class OfferDetailsWindow extends Overlay { addConfirmationLabelLabel(gridPane, rowIndex, offerTypeLabel, DisplayUtils.getDirectionBothSides(direction, offer.isPrivateOffer()), firstRowDistance); } + String amount = Res.get("shared.xmrAmount"); + addSeparator(gridPane, ++rowIndex); if (takeOfferHandlerOptional.isPresent()) { addConfirmationLabelLabel(gridPane, ++rowIndex, amount + xmrDirectionInfo, HavenoUtils.formatXmr(tradeAmount, true)); + addSeparator(gridPane, ++rowIndex); addConfirmationLabelLabel(gridPane, ++rowIndex, VolumeUtil.formatVolumeLabel(currencyCode) + counterCurrencyDirectionInfo, VolumeUtil.formatVolumeWithCode(offer.getVolumeByAmount(tradeAmount))); } else { addConfirmationLabelLabel(gridPane, ++rowIndex, amount + xmrDirectionInfo, HavenoUtils.formatXmr(offer.getAmount(), true)); + addSeparator(gridPane, ++rowIndex); addConfirmationLabelLabel(gridPane, ++rowIndex, Res.get("offerDetailsWindow.minXmrAmount"), HavenoUtils.formatXmr(offer.getMinAmount(), true)); + addSeparator(gridPane, ++rowIndex); String volume = VolumeUtil.formatVolumeWithCode(offer.getVolume()); String minVolume = ""; if (offer.getVolume() != null && offer.getMinVolume() != null && @@ -239,6 +257,7 @@ public class OfferDetailsWindow extends Overlay { } String priceLabel = Res.get("shared.price"); + addSeparator(gridPane, ++rowIndex); if (takeOfferHandlerOptional.isPresent()) { addConfirmationLabelLabel(gridPane, ++rowIndex, priceLabel, FormattingUtils.formatPrice(tradePrice)); } else { @@ -264,6 +283,7 @@ public class OfferDetailsWindow extends Overlay { final PaymentAccount myPaymentAccount = user.getPaymentAccount(makerPaymentAccountId); String countryCode = offer.getCountryCode(); boolean isMyOffer = offer.isMyOffer(keyRing); + addSeparator(gridPane, ++rowIndex); if (isMyOffer && makerPaymentAccountId != null && myPaymentAccount != null) { addConfirmationLabelLabel(gridPane, ++rowIndex, Res.get("offerDetailsWindow.myTradingAccount"), myPaymentAccount.getAccountName()); } else { @@ -272,6 +292,7 @@ public class OfferDetailsWindow extends Overlay { } if (showXmrAutoConf) { + addSeparator(gridPane, ++rowIndex); String isAutoConf = offer.isXmrAutoConf() ? Res.get("shared.yes") : Res.get("shared.no"); @@ -280,8 +301,10 @@ public class OfferDetailsWindow extends Overlay { if (showAcceptedBanks) { if (paymentMethod.equals(PaymentMethod.SAME_BANK)) { + addSeparator(gridPane, ++rowIndex); addConfirmationLabelLabel(gridPane, ++rowIndex, Res.get("offerDetailsWindow.bankId"), acceptedBanks.get(0)); } else if (isSpecificBanks) { + addSeparator(gridPane, ++rowIndex); String value = Joiner.on(", ").join(acceptedBanks); String acceptedBanksLabel = Res.get("shared.acceptedBanks"); Tooltip tooltip = new Tooltip(acceptedBanksLabel + " " + value); @@ -291,6 +314,7 @@ public class OfferDetailsWindow extends Overlay { } } if (showAcceptedCountryCodes) { + addSeparator(gridPane, ++rowIndex); String countries; Tooltip tooltip = null; if (CountryUtil.containsAllSepaEuroCountries(acceptedCountryCodes)) { @@ -313,29 +337,16 @@ public class OfferDetailsWindow extends Overlay { } if (isF2F) { + addSeparator(gridPane, ++rowIndex); addConfirmationLabelLabel(gridPane, ++rowIndex, Res.get("payment.f2f.city"), offer.getF2FCity()); } if (showOfferExtraInfo) { + addSeparator(gridPane, ++rowIndex); TextArea textArea = addConfirmationLabelTextArea(gridPane, ++rowIndex, Res.get("payment.shared.extraInfo"), "", 0).second; - textArea.setText(offer.getCombinedExtraInfo()); - textArea.setMaxHeight(200); - textArea.sceneProperty().addListener((o, oldScene, newScene) -> { - if (newScene != null) { - // avoid javafx css warning - CssTheme.loadSceneStyles(newScene, CssTheme.CSS_THEME_LIGHT, false); - textArea.applyCss(); - var text = textArea.lookup(".text"); - - textArea.prefHeightProperty().bind(Bindings.createDoubleBinding(() -> { - return textArea.getFont().getSize() + text.getBoundsInLocal().getHeight(); - }, text.boundsInLocalProperty())); - - text.boundsInLocalProperty().addListener((observableBoundsAfter, boundsBefore, boundsAfter) -> { - Platform.runLater(() -> textArea.requestLayout()); - }); - } - }); + textArea.setText(offer.getCombinedExtraInfo().trim()); + textArea.setMaxHeight(Layout.DETAILS_WINDOW_EXTRA_INFO_MAX_HEIGHT); textArea.setEditable(false); + GUIUtil.adjustHeightAutomatically(textArea, Layout.DETAILS_WINDOW_EXTRA_INFO_MAX_HEIGHT); } // get amount reserved for the offer @@ -355,13 +366,16 @@ public class OfferDetailsWindow extends Overlay { if (offerChallenge != null) rows++; - addTitledGroupBg(gridPane, ++rowIndex, rows, Res.get("shared.details"), Layout.GROUP_DISTANCE); + addTitledGroupBg(gridPane, ++rowIndex, rows, Res.get("shared.details"), Layout.COMPACT_GROUP_DISTANCE); addConfirmationLabelTextFieldWithCopyIcon(gridPane, rowIndex, Res.get("shared.offerId"), offer.getId(), - Layout.TWICE_FIRST_ROW_AND_GROUP_DISTANCE); + Layout.TWICE_FIRST_ROW_AND_COMPACT_GROUP_DISTANCE); + addSeparator(gridPane, ++rowIndex); addConfirmationLabelTextFieldWithCopyIcon(gridPane, ++rowIndex, Res.get("offerDetailsWindow.makersOnion"), offer.getMakerNodeAddress().getFullAddress()); + addSeparator(gridPane, ++rowIndex); addConfirmationLabelLabel(gridPane, ++rowIndex, Res.get("offerDetailsWindow.creationDate"), DisplayUtils.formatDateTime(offer.getDate())); + addSeparator(gridPane, ++rowIndex); String value = Res.getWithColAndCap("shared.buyer") + " " + HavenoUtils.formatXmr(takeOfferHandlerOptional.isPresent() ? offer.getOfferPayload().getBuyerSecurityDepositForTradeAmount(tradeAmount) : offer.getOfferPayload().getMaxBuyerSecurityDeposit(), true) + @@ -372,27 +386,75 @@ public class OfferDetailsWindow extends Overlay { addConfirmationLabelLabel(gridPane, ++rowIndex, Res.get("shared.securityDeposit"), value); if (reservedAmount != null) { + addSeparator(gridPane, ++rowIndex); addConfirmationLabelLabel(gridPane, ++rowIndex, Res.get("shared.reservedAmount"), HavenoUtils.formatXmr(reservedAmount, true)); } - if (countryCode != null && !isF2F) + if (countryCode != null && !isF2F) { + addSeparator(gridPane, ++rowIndex); addConfirmationLabelLabel(gridPane, ++rowIndex, Res.get("offerDetailsWindow.countryBank"), CountryUtil.getNameAndCode(countryCode)); + } - if (offerChallenge != null) - addConfirmationLabelTextFieldWithCopyIcon(gridPane, ++rowIndex, Res.get("offerDetailsWindow.challenge"), offerChallenge); + if (offerChallenge != null) { + addSeparator(gridPane, ++rowIndex); + + // add label + Label label = addLabel(gridPane, ++rowIndex, Res.get("offerDetailsWindow.challenge"), 0); + label.getStyleClass().addAll("confirmation-label", "regular-text-color"); + GridPane.setHalignment(label, HPos.LEFT); + GridPane.setValignment(label, VPos.TOP); + + // add vbox with passphrase and copy button + VBox vbox = new VBox(13); + vbox.setAlignment(Pos.TOP_CENTER); + VBox.setVgrow(vbox, Priority.ALWAYS); + vbox.getStyleClass().addAll("passphrase-copy-box"); + + // add passphrase + JFXTextField centerLabel = new JFXTextField(offerChallenge); + centerLabel.getStyleClass().add("confirmation-value"); + centerLabel.setAlignment(Pos.CENTER); + centerLabel.setFocusTraversable(false); + + // add copy button + Label copyLabel = new Label(); + copyLabel.getStyleClass().addAll("icon"); + copyLabel.setTooltip(new Tooltip(Res.get("shared.copyToClipboard"))); + MaterialDesignIconView copyIcon = new MaterialDesignIconView(MaterialDesignIcon.CONTENT_COPY, "1.2em"); + copyIcon.setFill(Color.WHITE); + copyLabel.setGraphic(copyIcon); + JFXButton copyButton = new JFXButton(Res.get("offerDetailsWindow.challenge.copy"), copyLabel); + copyButton.setContentDisplay(ContentDisplay.LEFT); + copyButton.setGraphicTextGap(8); + copyButton.setOnMouseClicked(e -> { + Utilities.copyToClipboard(offerChallenge); + Tooltip tp = new Tooltip(Res.get("shared.copiedToClipboard")); + Node node = (Node) e.getSource(); + UserThread.runAfter(() -> tp.hide(), 1); + tp.show(node, e.getScreenX() + Layout.PADDING, e.getScreenY() + Layout.PADDING); + }); + copyButton.setId("buy-button"); + copyButton.setFocusTraversable(false); + vbox.getChildren().addAll(centerLabel, copyButton); + + // add vbox to grid pane in next column + GridPane.setRowIndex(vbox, rowIndex); + GridPane.setColumnIndex(vbox, 1); + gridPane.getChildren().add(vbox); + } if (placeOfferHandlerOptional.isPresent()) { - addTitledGroupBg(gridPane, ++rowIndex, 1, Res.get("offerDetailsWindow.commitment"), Layout.GROUP_DISTANCE); + addTitledGroupBg(gridPane, ++rowIndex, 1, Res.get("offerDetailsWindow.commitment"), Layout.COMPACT_GROUP_DISTANCE); final Tuple2 labelLabelTuple2 = addConfirmationLabelLabel(gridPane, rowIndex, Res.get("offerDetailsWindow.agree"), Res.get("createOffer.tac"), - Layout.TWICE_FIRST_ROW_AND_GROUP_DISTANCE); + Layout.TWICE_FIRST_ROW_AND_COMPACT_GROUP_DISTANCE); labelLabelTuple2.second.setWrapText(true); addConfirmAndCancelButtons(true); } else if (takeOfferHandlerOptional.isPresent()) { - addTitledGroupBg(gridPane, ++rowIndex, 1, Res.get("shared.contract"), Layout.GROUP_DISTANCE); + addTitledGroupBg(gridPane, ++rowIndex, 1, Res.get("shared.contract"), Layout.COMPACT_GROUP_DISTANCE); final Tuple2 labelLabelTuple2 = addConfirmationLabelLabel(gridPane, rowIndex, Res.get("offerDetailsWindow.tac"), Res.get("takeOffer.tac"), - Layout.TWICE_FIRST_ROW_AND_GROUP_DISTANCE); + Layout.TWICE_FIRST_ROW_AND_COMPACT_GROUP_DISTANCE); labelLabelTuple2.second.setWrapText(true); addConfirmAndCancelButtons(false); @@ -418,9 +480,6 @@ public class OfferDetailsWindow extends Overlay { Res.get("offerDetailsWindow.confirm.taker", Res.get("shared.buy")) : Res.get("offerDetailsWindow.confirm.taker", Res.get("shared.sell")); - ImageView iconView = new ImageView(); - iconView.setId(isBuyerRole ? "image-buy-white" : "image-sell-white"); - Tuple4 placeOfferTuple = addButtonBusyAnimationLabelAfterGroup(gridPane, ++rowIndex, 1, isPlaceOffer ? placeOfferButtonText : takeOfferButtonText); @@ -428,11 +487,18 @@ public class OfferDetailsWindow extends Overlay { AutoTooltipButton confirmButton = (AutoTooltipButton) placeOfferTuple.first; confirmButton.setMinHeight(40); confirmButton.setPadding(new Insets(0, 20, 0, 20)); - confirmButton.setGraphic(iconView); confirmButton.setGraphicTextGap(10); confirmButton.setId(isBuyerRole ? "buy-button-big" : "sell-button-big"); confirmButton.updateText(isPlaceOffer ? placeOfferButtonText : takeOfferButtonText); + if (offer.hasBuyerAsTakerWithoutDeposit()) { + confirmButton.setGraphic(GUIUtil.getLockLabel()); + } else { + ImageView iconView = new ImageView(); + iconView.setId(isBuyerRole ? "image-buy-white" : "image-sell-white"); + confirmButton.setGraphic(iconView); + } + busyAnimation = placeOfferTuple.second; Label spinnerInfoLabel = placeOfferTuple.third; diff --git a/desktop/src/main/java/haveno/desktop/main/overlays/windows/QRCodeWindow.java b/desktop/src/main/java/haveno/desktop/main/overlays/windows/QRCodeWindow.java index 8bca62d143..21809a7501 100644 --- a/desktop/src/main/java/haveno/desktop/main/overlays/windows/QRCodeWindow.java +++ b/desktop/src/main/java/haveno/desktop/main/overlays/windows/QRCodeWindow.java @@ -17,9 +17,12 @@ package haveno.desktop.main.overlays.windows; +import haveno.common.util.Tuple2; +import haveno.common.util.Utilities; import haveno.core.locale.Res; import haveno.desktop.components.AutoTooltipLabel; import haveno.desktop.main.overlays.Overlay; +import haveno.desktop.util.GUIUtil; import javafx.geometry.HPos; import javafx.geometry.Insets; import javafx.scene.control.Label; @@ -27,31 +30,35 @@ import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.layout.GridPane; import javafx.scene.layout.Priority; +import javafx.scene.layout.StackPane; import net.glxn.qrgen.QRCode; import net.glxn.qrgen.image.ImageType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.ByteArrayInputStream; +import java.net.URI; public class QRCodeWindow extends Overlay { private static final Logger log = LoggerFactory.getLogger(QRCodeWindow.class); - private final ImageView qrCodeImageView; + private final StackPane qrCodePane; private final String moneroUri; - public QRCodeWindow(String bitcoinURI) { - this.moneroUri = bitcoinURI; + public QRCodeWindow(String moneroUri) { + this.moneroUri = moneroUri; + + Tuple2 qrCodeTuple = GUIUtil.getBigXmrQrCodePane(); + qrCodePane = qrCodeTuple.first; + ImageView qrCodeImageView = qrCodeTuple.second; + final byte[] imageBytes = QRCode - .from(bitcoinURI) + .from(moneroUri) .withSize(300, 300) .to(ImageType.PNG) .stream() .toByteArray(); Image qrImage = new Image(new ByteArrayInputStream(imageBytes)); - qrCodeImageView = new ImageView(qrImage); - qrCodeImageView.setFitHeight(250); - qrCodeImageView.setFitWidth(250); - qrCodeImageView.getStyleClass().add("qr-code"); + qrCodeImageView.setImage(qrImage); type = Type.Information; width = 468; @@ -65,10 +72,11 @@ public class QRCodeWindow extends Overlay { addHeadLine(); addMessage(); - GridPane.setRowIndex(qrCodeImageView, ++rowIndex); - GridPane.setColumnSpan(qrCodeImageView, 2); - GridPane.setHalignment(qrCodeImageView, HPos.CENTER); - gridPane.getChildren().add(qrCodeImageView); + qrCodePane.setOnMouseClicked(event -> openWallet()); + GridPane.setRowIndex(qrCodePane, ++rowIndex); + GridPane.setColumnSpan(qrCodePane, 2); + GridPane.setHalignment(qrCodePane, HPos.CENTER); + gridPane.getChildren().add(qrCodePane); String request = moneroUri.replace("%20", " ").replace("?", "\n?").replace("&", "\n&"); Label infoLabel = new AutoTooltipLabel(Res.get("qRCodeWindow.request", request)); @@ -91,4 +99,12 @@ public class QRCodeWindow extends Overlay { public String getClipboardText() { return moneroUri; } + + private void openWallet() { + try { + Utilities.openURI(URI.create(moneroUri)); + } catch (Exception e) { + log.warn(e.getMessage()); + } + } } diff --git a/desktop/src/main/java/haveno/desktop/main/overlays/windows/SignPaymentAccountsWindow.java b/desktop/src/main/java/haveno/desktop/main/overlays/windows/SignPaymentAccountsWindow.java index fe8b8c9009..3bf7be6b14 100644 --- a/desktop/src/main/java/haveno/desktop/main/overlays/windows/SignPaymentAccountsWindow.java +++ b/desktop/src/main/java/haveno/desktop/main/overlays/windows/SignPaymentAccountsWindow.java @@ -113,7 +113,7 @@ public class SignPaymentAccountsWindow extends Overlay { this.trade = trade; rowIndex = -1; - width = 918; + width = Layout.DETAILS_WINDOW_WIDTH; createGridPane(); addContent(); display(); @@ -127,7 +126,6 @@ public class TradeDetailsWindow extends Overlay { @Override protected void createGridPane() { super.createGridPane(); - gridPane.setPadding(new Insets(35, 40, 30, 40)); gridPane.getStyleClass().add("grid-pane"); } @@ -135,7 +133,7 @@ public class TradeDetailsWindow extends Overlay { Offer offer = trade.getOffer(); Contract contract = trade.getContract(); - int rows = 5; + int rows = 9; addTitledGroupBg(gridPane, ++rowIndex, rows, Res.get("tradeDetailsWindow.headline")); boolean myOffer = tradeManager.isMyOffer(offer); @@ -156,18 +154,22 @@ public class TradeDetailsWindow extends Overlay { xmrDirectionInfo = toSpend; } + addSeparator(gridPane, ++rowIndex); addConfirmationLabelTextField(gridPane, ++rowIndex, Res.get("shared.xmrAmount") + xmrDirectionInfo, HavenoUtils.formatXmr(trade.getAmount(), true)); + addSeparator(gridPane, ++rowIndex); addConfirmationLabelTextField(gridPane, ++rowIndex, VolumeUtil.formatVolumeLabel(offer.getCurrencyCode()) + counterCurrencyDirectionInfo, VolumeUtil.formatVolumeWithCode(trade.getVolume())); + addSeparator(gridPane, ++rowIndex); addConfirmationLabelTextField(gridPane, ++rowIndex, Res.get("shared.tradePrice"), FormattingUtils.formatPrice(trade.getPrice())); String paymentMethodText = Res.get(offer.getPaymentMethod().getId()); + addSeparator(gridPane, ++rowIndex); addConfirmationLabelTextField(gridPane, ++rowIndex, Res.get("shared.paymentMethod"), paymentMethodText); // second group - rows = 7; + rows = 5; if (offer.getCombinedExtraInfo() != null && !offer.getCombinedExtraInfo().isEmpty()) rows++; @@ -200,9 +202,10 @@ public class TradeDetailsWindow extends Overlay { if (trade.getTradePeerNodeAddress() != null) rows++; - addTitledGroupBg(gridPane, ++rowIndex, rows, Res.get("shared.details"), Layout.GROUP_DISTANCE); + addTitledGroupBg(gridPane, ++rowIndex, rows, Res.get("shared.details"), Layout.COMPACT_GROUP_DISTANCE); addConfirmationLabelTextField(gridPane, rowIndex, Res.get("shared.tradeId"), - trade.getId(), Layout.TWICE_FIRST_ROW_AND_GROUP_DISTANCE); + trade.getId(), Layout.TWICE_FIRST_ROW_AND_COMPACT_GROUP_DISTANCE); + addSeparator(gridPane, ++rowIndex); addConfirmationLabelTextField(gridPane, ++rowIndex, Res.get("tradeDetailsWindow.tradeDate"), DisplayUtils.formatDateTime(trade.getDate())); String securityDeposit = Res.getWithColAndCap("shared.buyer") + @@ -212,40 +215,30 @@ public class TradeDetailsWindow extends Overlay { Res.getWithColAndCap("shared.seller") + " " + HavenoUtils.formatXmr(trade.getSellerSecurityDepositBeforeMiningFee(), true); + addSeparator(gridPane, ++rowIndex); addConfirmationLabelTextField(gridPane, ++rowIndex, Res.get("shared.securityDeposit"), securityDeposit); NodeAddress arbitratorNodeAddress = trade.getArbitratorNodeAddress(); if (arbitratorNodeAddress != null) { + addSeparator(gridPane, ++rowIndex); addConfirmationLabelTextField(gridPane, ++rowIndex, Res.get("tradeDetailsWindow.agentAddresses"), arbitratorNodeAddress.getFullAddress()); } - if (trade.getTradePeerNodeAddress() != null) + if (trade.getTradePeerNodeAddress() != null) { + addSeparator(gridPane, ++rowIndex); addConfirmationLabelTextField(gridPane, ++rowIndex, Res.get("tradeDetailsWindow.tradePeersOnion"), trade.getTradePeerNodeAddress().getFullAddress()); + } if (offer.getCombinedExtraInfo() != null && !offer.getCombinedExtraInfo().isEmpty()) { + addSeparator(gridPane, ++rowIndex); TextArea textArea = addConfirmationLabelTextArea(gridPane, ++rowIndex, Res.get("payment.shared.extraInfo.offer"), "", 0).second; - textArea.setText(offer.getCombinedExtraInfo()); - textArea.setMaxHeight(200); - textArea.sceneProperty().addListener((o, oldScene, newScene) -> { - if (newScene != null) { - // avoid javafx css warning - CssTheme.loadSceneStyles(newScene, CssTheme.CSS_THEME_LIGHT, false); - textArea.applyCss(); - var text = textArea.lookup(".text"); - - textArea.prefHeightProperty().bind(Bindings.createDoubleBinding(() -> { - return textArea.getFont().getSize() + text.getBoundsInLocal().getHeight(); - }, text.boundsInLocalProperty())); - - text.boundsInLocalProperty().addListener((observableBoundsAfter, boundsBefore, boundsAfter) -> { - Platform.runLater(() -> textArea.requestLayout()); - }); - } - }); + textArea.setText(offer.getCombinedExtraInfo().trim()); + textArea.setMaxHeight(Layout.DETAILS_WINDOW_EXTRA_INFO_MAX_HEIGHT); textArea.setEditable(false); + GUIUtil.adjustHeightAutomatically(textArea, Layout.DETAILS_WINDOW_EXTRA_INFO_MAX_HEIGHT); } if (contract != null) { @@ -254,6 +247,7 @@ public class TradeDetailsWindow extends Overlay { if (buyerPaymentAccountPayload != null) { String paymentDetails = buyerPaymentAccountPayload.getPaymentDetails(); String postFix = " / " + buyersAccountAge; + addSeparator(gridPane, ++rowIndex); addConfirmationLabelTextField(gridPane, ++rowIndex, Res.get("shared.paymentDetails", Res.get("shared.buyer")), paymentDetails + postFix).second.setTooltip(new Tooltip(paymentDetails + postFix)); @@ -261,21 +255,27 @@ public class TradeDetailsWindow extends Overlay { if (sellerPaymentAccountPayload != null) { String paymentDetails = sellerPaymentAccountPayload.getPaymentDetails(); String postFix = " / " + sellersAccountAge; + addSeparator(gridPane, ++rowIndex); addConfirmationLabelTextField(gridPane, ++rowIndex, Res.get("shared.paymentDetails", Res.get("shared.seller")), paymentDetails + postFix).second.setTooltip(new Tooltip(paymentDetails + postFix)); } - if (buyerPaymentAccountPayload == null && sellerPaymentAccountPayload == null) + if (buyerPaymentAccountPayload == null && sellerPaymentAccountPayload == null) { + addSeparator(gridPane, ++rowIndex); addConfirmationLabelTextField(gridPane, ++rowIndex, Res.get("shared.paymentMethod"), Res.get(contract.getPaymentMethodId())); + } } - if (trade.getMaker().getDepositTxHash() != null) + if (trade.getMaker().getDepositTxHash() != null) { + addSeparator(gridPane, ++rowIndex); addLabelTxIdTextField(gridPane, ++rowIndex, Res.get("shared.makerDepositTransactionId"), trade.getMaker().getDepositTxHash()); - if (trade.getTaker().getDepositTxHash() != null) + } + if (trade.getTaker().getDepositTxHash() != null) { addLabelTxIdTextField(gridPane, ++rowIndex, Res.get("shared.takerDepositTransactionId"), trade.getTaker().getDepositTxHash()); + } if (showDisputedTx) { @@ -287,6 +287,7 @@ public class TradeDetailsWindow extends Overlay { } if (trade.hasFailed()) { + addSeparator(gridPane, ++rowIndex); textArea = addConfirmationLabelTextArea(gridPane, ++rowIndex, Res.get("shared.errorMessage"), "", 0).second; textArea.setText(trade.getErrorMessage()); textArea.setEditable(false); @@ -302,6 +303,7 @@ public class TradeDetailsWindow extends Overlay { textArea.scrollTopProperty().addListener(changeListener); textArea.setScrollTop(30); + addSeparator(gridPane, ++rowIndex); addConfirmationLabelTextField(gridPane, ++rowIndex, Res.get("tradeDetailsWindow.tradePhase"), trade.getPhase().name()); } diff --git a/desktop/src/main/java/haveno/desktop/main/overlays/windows/TradeFeedbackWindow.java b/desktop/src/main/java/haveno/desktop/main/overlays/windows/TradeFeedbackWindow.java index 76d414b380..a9202f9b8a 100644 --- a/desktop/src/main/java/haveno/desktop/main/overlays/windows/TradeFeedbackWindow.java +++ b/desktop/src/main/java/haveno/desktop/main/overlays/windows/TradeFeedbackWindow.java @@ -40,7 +40,7 @@ public class TradeFeedbackWindow extends Overlay { @Override public void show() { headLine(Res.get("tradeFeedbackWindow.title")); - message(Res.get("tradeFeedbackWindow.msg.part1")); + //message(Res.get("tradeFeedbackWindow.msg.part1")); // TODO: this message part has padding which remaining message does not have hideCloseButton(); actionButtonText(Res.get("shared.close")); @@ -51,6 +51,17 @@ public class TradeFeedbackWindow extends Overlay { protected void addMessage() { super.addMessage(); + AutoTooltipLabel messageLabel1 = new AutoTooltipLabel(Res.get("tradeFeedbackWindow.msg.part1")); + messageLabel1.setMouseTransparent(true); + messageLabel1.setWrapText(true); + GridPane.setHalignment(messageLabel1, HPos.LEFT); + GridPane.setHgrow(messageLabel1, Priority.ALWAYS); + GridPane.setRowIndex(messageLabel1, ++rowIndex); + GridPane.setColumnIndex(messageLabel1, 0); + GridPane.setColumnSpan(messageLabel1, 2); + gridPane.getChildren().add(messageLabel1); + GridPane.setMargin(messageLabel1, new Insets(10, 0, 10, 0)); + AutoTooltipLabel messageLabel2 = new AutoTooltipLabel(Res.get("tradeFeedbackWindow.msg.part2")); messageLabel2.setMouseTransparent(true); messageLabel2.setWrapText(true); diff --git a/desktop/src/main/java/haveno/desktop/main/overlays/windows/TxDetailsWindow.java b/desktop/src/main/java/haveno/desktop/main/overlays/windows/TxDetailsWindow.java index 1af5d97fbf..417e5c0043 100644 --- a/desktop/src/main/java/haveno/desktop/main/overlays/windows/TxDetailsWindow.java +++ b/desktop/src/main/java/haveno/desktop/main/overlays/windows/TxDetailsWindow.java @@ -30,7 +30,6 @@ import static haveno.desktop.util.FormBuilder.addConfirmationLabelLabel; import static haveno.desktop.util.FormBuilder.addConfirmationLabelTextFieldWithCopyIcon; import static haveno.desktop.util.FormBuilder.addLabelTxIdTextField; import static haveno.desktop.util.FormBuilder.addMultilineLabel; -import static haveno.desktop.util.FormBuilder.addTitledGroupBg; import java.math.BigInteger; @@ -50,21 +49,20 @@ public class TxDetailsWindow extends Overlay { this.item = item; rowIndex = -1; width = 918; + if (headLine == null) + headLine = Res.get("txDetailsWindow.headline"); createGridPane(); gridPane.setHgap(15); addHeadLine(); addContent(); addButtons(); - addDontShowAgainCheckBox(); applyStyles(); display(); } protected void addContent() { - int rows = 10; MoneroTxWallet tx = item.getTx(); String memo = tx.getNote(); - if (memo != null && !"".equals(memo)) rows++; String txKey = null; boolean isOutgoing = tx.getOutgoingTransfer() != null; if (isOutgoing) { @@ -74,18 +72,11 @@ public class TxDetailsWindow extends Overlay { // TODO (monero-java): wallet.getTxKey() should return null if key does not exist instead of throwing exception } } - if (txKey != null && !"".equals(txKey)) rows++; - - // add title - addTitledGroupBg(gridPane, ++rowIndex, rows, Res.get("txDetailsWindow.headline")); - Region spacer = new Region(); - spacer.setMinHeight(15); - gridPane.add(spacer, 0, ++rowIndex); // add sent or received note String resKey = isOutgoing ? "txDetailsWindow.xmr.noteSent" : "txDetailsWindow.xmr.noteReceived"; GridPane.setColumnSpan(addMultilineLabel(gridPane, ++rowIndex, Res.get(resKey), 0), 2); - spacer = new Region(); + Region spacer = new Region(); spacer.setMinHeight(15); gridPane.add(spacer, 0, ++rowIndex); diff --git a/desktop/src/main/java/haveno/desktop/main/overlays/windows/VerifyDisputeResultSignatureWindow.java b/desktop/src/main/java/haveno/desktop/main/overlays/windows/VerifyDisputeResultSignatureWindow.java index fd60d07193..a8bd993219 100644 --- a/desktop/src/main/java/haveno/desktop/main/overlays/windows/VerifyDisputeResultSignatureWindow.java +++ b/desktop/src/main/java/haveno/desktop/main/overlays/windows/VerifyDisputeResultSignatureWindow.java @@ -77,6 +77,7 @@ public class VerifyDisputeResultSignatureWindow extends Overlay { root.setTabClosingPolicy(TabPane.TabClosingPolicy.ALL_TABS); failedTradesTab.setClosable(false); - openOffersTab.setText(Res.get("portfolio.tab.openOffers").toUpperCase()); - pendingTradesTab.setText(Res.get("portfolio.tab.pendingTrades").toUpperCase()); - closedTradesTab.setText(Res.get("portfolio.tab.history").toUpperCase()); + openOffersTab.setText(Res.get("portfolio.tab.openOffers")); + pendingTradesTab.setText(Res.get("portfolio.tab.pendingTrades")); + closedTradesTab.setText(Res.get("portfolio.tab.history")); navigationListener = (viewPath, data) -> { if (viewPath.size() == 3 && viewPath.indexOf(PortfolioView.class) == 1) diff --git a/desktop/src/main/java/haveno/desktop/main/portfolio/closedtrades/ClosedTradesView.java b/desktop/src/main/java/haveno/desktop/main/portfolio/closedtrades/ClosedTradesView.java index ab92f845db..aa7c6bb1c5 100644 --- a/desktop/src/main/java/haveno/desktop/main/portfolio/closedtrades/ClosedTradesView.java +++ b/desktop/src/main/java/haveno/desktop/main/portfolio/closedtrades/ClosedTradesView.java @@ -156,6 +156,8 @@ public class ClosedTradesView extends ActivatableViewAndModel onWidthChange((double) newValue); tradeFeeColumn.setGraphic(new AutoTooltipLabel(ColumnNames.TRADE_FEE.toString().replace(" BTC", ""))); buyerSecurityDepositColumn.setGraphic(new AutoTooltipLabel(ColumnNames.BUYER_SEC.toString())); @@ -252,6 +254,7 @@ public class ClosedTradesView extends ActivatableViewAndModel new ReadOnlyObjectWrapper<>(offerListItem.getValue())); tradeIdColumn.setCellFactory( new Callback<>() { @@ -463,7 +465,7 @@ public class ClosedTradesView extends ActivatableViewAndModel setAvatarColumnCellFactory() { - avatarColumn.getStyleClass().addAll("last-column", "avatar-column"); + avatarColumn.getStyleClass().add("avatar-column"); avatarColumn.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue())); avatarColumn.setCellFactory( new Callback<>() { @@ -696,7 +698,7 @@ public class ClosedTradesView extends ActivatableViewAndModel onRevertTrade(trade)); diff --git a/desktop/src/main/java/haveno/desktop/main/portfolio/failedtrades/FailedTradesView.java b/desktop/src/main/java/haveno/desktop/main/portfolio/failedtrades/FailedTradesView.java index 35fe31dc22..f92dc4c0b8 100644 --- a/desktop/src/main/java/haveno/desktop/main/portfolio/failedtrades/FailedTradesView.java +++ b/desktop/src/main/java/haveno/desktop/main/portfolio/failedtrades/FailedTradesView.java @@ -115,6 +115,8 @@ public class FailedTradesView extends ActivatableViewAndModel new ReadOnlyObjectWrapper<>(offerListItem.getValue())); tradeIdColumn.setCellFactory( new Callback<>() { @@ -455,7 +456,6 @@ public class FailedTradesView extends ActivatableViewAndModel new ReadOnlyObjectWrapper<>(trade.getValue())); stateColumn.setCellFactory( new Callback<>() { diff --git a/desktop/src/main/java/haveno/desktop/main/portfolio/openoffer/OpenOffersView.fxml b/desktop/src/main/java/haveno/desktop/main/portfolio/openoffer/OpenOffersView.fxml index 035ec5fbdc..44b26f1bd3 100644 --- a/desktop/src/main/java/haveno/desktop/main/portfolio/openoffer/OpenOffersView.fxml +++ b/desktop/src/main/java/haveno/desktop/main/portfolio/openoffer/OpenOffersView.fxml @@ -35,7 +35,6 @@ - diff --git a/desktop/src/main/java/haveno/desktop/main/portfolio/openoffer/OpenOffersView.java b/desktop/src/main/java/haveno/desktop/main/portfolio/openoffer/OpenOffersView.java index 3282ab078a..ec19daccdd 100644 --- a/desktop/src/main/java/haveno/desktop/main/portfolio/openoffer/OpenOffersView.java +++ b/desktop/src/main/java/haveno/desktop/main/portfolio/openoffer/OpenOffersView.java @@ -116,8 +116,6 @@ public class OpenOffersView extends ActivatableViewAndModel onWidthChange((double) newValue); groupIdColumn.setGraphic(new AutoTooltipLabel(ColumnNames.GROUP_ID.toString())); paymentMethodColumn.setGraphic(new AutoTooltipLabel(ColumnNames.PAYMENT_METHOD.toString())); @@ -231,8 +231,7 @@ public class OpenOffersView extends ActivatableViewAndModel applyFilteredListPredicate(filterTextField.getText()); searchBox.setSpacing(5); HBox.setHgrow(searchBoxSpacer, Priority.ALWAYS); @@ -470,8 +469,8 @@ public class OpenOffersView extends ActivatableViewAndModel navigation.navigateTo(MainView.class, FundsView.class, WithdrawalView.class)) .dontShowAgainId(key) .show(); @@ -527,7 +526,7 @@ public class OpenOffersView extends ActivatableViewAndModel new ReadOnlyObjectWrapper<>(openOfferListItem.getValue())); - offerIdColumn.getStyleClass().addAll("number-column", "first-column"); + offerIdColumn.getStyleClass().addAll("number-column"); offerIdColumn.setCellFactory( new Callback<>() { @@ -903,7 +902,7 @@ public class OpenOffersView extends ActivatableViewAndModel new ReadOnlyObjectWrapper<>(offerListItem.getValue())); removeItemColumn.setCellFactory( new Callback<>() { diff --git a/desktop/src/main/java/haveno/desktop/main/portfolio/pendingtrades/PendingTradesView.java b/desktop/src/main/java/haveno/desktop/main/portfolio/pendingtrades/PendingTradesView.java index 645fbb5014..70b588bb4e 100644 --- a/desktop/src/main/java/haveno/desktop/main/portfolio/pendingtrades/PendingTradesView.java +++ b/desktop/src/main/java/haveno/desktop/main/portfolio/pendingtrades/PendingTradesView.java @@ -55,6 +55,7 @@ import haveno.desktop.main.shared.ChatView; import haveno.desktop.util.CssTheme; import haveno.desktop.util.DisplayUtils; import haveno.desktop.util.FormBuilder; +import haveno.desktop.util.GUIUtil; import haveno.network.p2p.NodeAddress; import java.util.Comparator; import java.util.HashMap; @@ -171,6 +172,8 @@ public class PendingTradesView extends ActivatableViewAndModel new ReadOnlyObjectWrapper<>(pendingTradesListItem.getValue())); tradeIdColumn.setCellFactory( new Callback<>() { @@ -821,7 +824,7 @@ public class PendingTradesView extends ActivatableViewAndModel setAvatarColumnCellFactory() { avatarColumn.setCellValueFactory((trade) -> new ReadOnlyObjectWrapper<>(trade.getValue())); - avatarColumn.getStyleClass().addAll("last-column", "avatar-column"); + avatarColumn.getStyleClass().add("avatar-column"); avatarColumn.setCellFactory( new Callback<>() { @@ -860,7 +863,7 @@ public class PendingTradesView extends ActivatableViewAndModel setChatColumnCellFactory() { chatColumn.setCellValueFactory((trade) -> new ReadOnlyObjectWrapper<>(trade.getValue())); - chatColumn.getStyleClass().addAll("last-column", "avatar-column"); + chatColumn.getStyleClass().addAll("avatar-column"); chatColumn.setSortable(false); chatColumn.setCellFactory( new Callback<>() { diff --git a/desktop/src/main/java/haveno/desktop/main/portfolio/pendingtrades/steps/TradeStepView.java b/desktop/src/main/java/haveno/desktop/main/portfolio/pendingtrades/steps/TradeStepView.java index 3af019e6d0..b7ac1b1886 100644 --- a/desktop/src/main/java/haveno/desktop/main/portfolio/pendingtrades/steps/TradeStepView.java +++ b/desktop/src/main/java/haveno/desktop/main/portfolio/pendingtrades/steps/TradeStepView.java @@ -125,6 +125,7 @@ public abstract class TradeStepView extends AnchorPane { gridPane.setHgap(Layout.GRID_GAP); gridPane.setVgap(Layout.GRID_GAP); + gridPane.setPadding(new Insets(0, 0, 25, 0)); ColumnConstraints columnConstraints1 = new ColumnConstraints(); columnConstraints1.setHgrow(Priority.ALWAYS); diff --git a/desktop/src/main/java/haveno/desktop/main/portfolio/pendingtrades/steps/buyer/BuyerStep4View.java b/desktop/src/main/java/haveno/desktop/main/portfolio/pendingtrades/steps/buyer/BuyerStep4View.java index e589443301..9a328695df 100644 --- a/desktop/src/main/java/haveno/desktop/main/portfolio/pendingtrades/steps/buyer/BuyerStep4View.java +++ b/desktop/src/main/java/haveno/desktop/main/portfolio/pendingtrades/steps/buyer/BuyerStep4View.java @@ -142,7 +142,7 @@ public class BuyerStep4View extends TradeStepView { if (!DevEnv.isDevMode()) { UserThread.runAfter(() -> new Popup().headLine(Res.get("portfolio.pending.step5_buyer.tradeCompleted.headline")) .feedback(Res.get("portfolio.pending.step5_buyer.tradeCompleted.msg")) - .actionButtonTextWithGoTo("navigation.portfolio.closedTrades") + .actionButtonTextWithGoTo("portfolio.tab.history") .onAction(() -> model.dataModel.navigation.navigateTo(MainView.class, PortfolioView.class, ClosedTradesView.class)) .dontShowAgainId("tradeCompleteWithdrawCompletedInfo") .show(), 500, TimeUnit.MILLISECONDS); diff --git a/desktop/src/main/java/haveno/desktop/main/settings/SettingsView.java b/desktop/src/main/java/haveno/desktop/main/settings/SettingsView.java index ee147ad376..e2a67428d9 100644 --- a/desktop/src/main/java/haveno/desktop/main/settings/SettingsView.java +++ b/desktop/src/main/java/haveno/desktop/main/settings/SettingsView.java @@ -56,9 +56,9 @@ public class SettingsView extends ActivatableView { @Override public void initialize() { - preferencesTab.setText(Res.get("settings.tab.preferences").toUpperCase()); - networkTab.setText(Res.get("settings.tab.network").toUpperCase()); - aboutTab.setText(Res.get("settings.tab.about").toUpperCase()); + preferencesTab.setText(Res.get("settings.tab.preferences")); + networkTab.setText(Res.get("settings.tab.network")); + aboutTab.setText(Res.get("settings.tab.about")); navigationListener = (viewPath, data) -> { if (viewPath.size() == 3 && viewPath.indexOf(SettingsView.class) == 1) diff --git a/desktop/src/main/java/haveno/desktop/main/settings/about/AboutView.java b/desktop/src/main/java/haveno/desktop/main/settings/about/AboutView.java index e543837f5d..73e51a47da 100644 --- a/desktop/src/main/java/haveno/desktop/main/settings/about/AboutView.java +++ b/desktop/src/main/java/haveno/desktop/main/settings/about/AboutView.java @@ -19,6 +19,7 @@ package haveno.desktop.main.settings.about; import com.google.inject.Inject; import haveno.common.app.Version; +import haveno.core.filter.FilterManager; import haveno.core.locale.Res; import haveno.desktop.common.view.ActivatableView; import haveno.desktop.common.view.FxmlView; @@ -35,16 +36,18 @@ import javafx.scene.layout.GridPane; @FxmlView public class AboutView extends ActivatableView { +private final FilterManager filterManager; private int gridRow = 0; @Inject - public AboutView() { + public AboutView(FilterManager filterManager) { super(); + this.filterManager = filterManager; } @Override public void initialize() { - addTitledGroupBg(root, gridRow, 4, Res.get("setting.about.aboutHaveno")); + addTitledGroupBg(root, gridRow, 5, Res.get("setting.about.aboutHaveno")); Label label = addLabel(root, gridRow, Res.get("setting.about.about"), Layout.TWICE_FIRST_ROW_DISTANCE); label.setWrapText(true); @@ -77,8 +80,11 @@ public class AboutView extends ActivatableView { if (isXmr) addCompactTopLabelTextField(root, ++gridRow, Res.get("setting.about.feeEstimation.label"), "Monero node"); - addTitledGroupBg(root, ++gridRow, 2, Res.get("setting.about.versionDetails"), Layout.GROUP_DISTANCE); + String minVersion = filterManager.getDisableTradeBelowVersion() == null ? Res.get("shared.none") : filterManager.getDisableTradeBelowVersion(); + + addTitledGroupBg(root, ++gridRow, 3, Res.get("setting.about.versionDetails"), Layout.GROUP_DISTANCE); addCompactTopLabelTextField(root, gridRow, Res.get("setting.about.version"), Version.VERSION, Layout.TWICE_FIRST_ROW_AND_GROUP_DISTANCE); + addCompactTopLabelTextField(root, ++gridRow, Res.get("filterWindow.disableTradeBelowVersion"), minVersion); addCompactTopLabelTextField(root, ++gridRow, Res.get("setting.about.subsystems.label"), Res.get("setting.about.subsystems.val", diff --git a/desktop/src/main/java/haveno/desktop/main/settings/network/NetworkSettingsView.fxml b/desktop/src/main/java/haveno/desktop/main/settings/network/NetworkSettingsView.fxml index bbfb4c6e05..d4dcf14085 100644 --- a/desktop/src/main/java/haveno/desktop/main/settings/network/NetworkSettingsView.fxml +++ b/desktop/src/main/java/haveno/desktop/main/settings/network/NetworkSettingsView.fxml @@ -53,7 +53,7 @@ - + @@ -108,6 +108,9 @@ + + + @@ -159,10 +162,7 @@ - - - + diff --git a/desktop/src/main/java/haveno/desktop/main/settings/network/NetworkSettingsView.java b/desktop/src/main/java/haveno/desktop/main/settings/network/NetworkSettingsView.java index a767d032bc..4d1a6cde01 100644 --- a/desktop/src/main/java/haveno/desktop/main/settings/network/NetworkSettingsView.java +++ b/desktop/src/main/java/haveno/desktop/main/settings/network/NetworkSettingsView.java @@ -75,7 +75,7 @@ public class NetworkSettingsView extends ActivatableView { @FXML InputTextField xmrNodesInputTextField; @FXML - TextField onionAddress, sentDataTextField, receivedDataTextField, chainHeightTextField, minVersionForTrading; + TextField onionAddress, sentDataTextField, receivedDataTextField, chainHeightTextField; @FXML Label p2PPeersLabel, moneroConnectionsLabel; @FXML @@ -149,6 +149,14 @@ public class NetworkSettingsView extends ActivatableView { @Override public void initialize() { + GUIUtil.applyTableStyle(p2pPeersTableView); + GUIUtil.applyTableStyle(moneroConnectionsTableView); + + onionAddress.getStyleClass().add("label-float"); + sentDataTextField.getStyleClass().add("label-float"); + receivedDataTextField.getStyleClass().add("label-float"); + chainHeightTextField.getStyleClass().add("label-float"); + btcHeader.setText(Res.get("settings.net.xmrHeader")); p2pHeader.setText(Res.get("settings.net.p2pHeader")); onionAddress.setPromptText(Res.get("settings.net.onionAddressLabel")); @@ -160,7 +168,6 @@ public class NetworkSettingsView extends ActivatableView { useTorForXmrOnRadio.setText(Res.get("settings.net.useTorForXmrOnRadio")); moneroNodesLabel.setText(Res.get("settings.net.moneroNodesLabel")); moneroConnectionAddressColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.address"))); - moneroConnectionAddressColumn.getStyleClass().add("first-column"); moneroConnectionConnectedColumn.setGraphic(new AutoTooltipLabel(Res.get("settings.net.connection"))); localhostXmrNodeInfoLabel.setText(Res.get("settings.net.localhostXmrNodeInfo")); useProvidedNodesRadio.setText(Res.get("settings.net.useProvidedNodesRadio")); @@ -170,18 +177,15 @@ public class NetworkSettingsView extends ActivatableView { rescanOutputsButton.updateText(Res.get("settings.net.rescanOutputsButton")); p2PPeersLabel.setText(Res.get("settings.net.p2PPeersLabel")); onionAddressColumn.setGraphic(new AutoTooltipLabel(Res.get("settings.net.onionAddressColumn"))); - onionAddressColumn.getStyleClass().add("first-column"); creationDateColumn.setGraphic(new AutoTooltipLabel(Res.get("settings.net.creationDateColumn"))); connectionTypeColumn.setGraphic(new AutoTooltipLabel(Res.get("settings.net.connectionTypeColumn"))); sentDataTextField.setPromptText(Res.get("settings.net.sentDataLabel")); receivedDataTextField.setPromptText(Res.get("settings.net.receivedDataLabel")); chainHeightTextField.setPromptText(Res.get("settings.net.chainHeightLabel")); - minVersionForTrading.setPromptText(Res.get("filterWindow.disableTradeBelowVersion")); roundTripTimeColumn.setGraphic(new AutoTooltipLabel(Res.get("settings.net.roundTripTimeColumn"))); sentBytesColumn.setGraphic(new AutoTooltipLabel(Res.get("settings.net.sentBytesColumn"))); receivedBytesColumn.setGraphic(new AutoTooltipLabel(Res.get("settings.net.receivedBytesColumn"))); peerTypeColumn.setGraphic(new AutoTooltipLabel(Res.get("settings.net.peerTypeColumn"))); - peerTypeColumn.getStyleClass().add("last-column"); openTorSettingsButton.updateText(Res.get("settings.net.openTorSettingsButton")); // TODO: hiding button to rescan outputs until supported @@ -504,10 +508,6 @@ public class NetworkSettingsView extends ActivatableView { selectMoneroPeersToggle(); onMoneroPeersToggleSelected(false); } - - // set min version for trading - String minVersion = filterManager.getDisableTradeBelowVersion(); - minVersionForTrading.textProperty().setValue(minVersion == null ? Res.get("shared.none") : minVersion); } private boolean isPublicNodesDisabled() { diff --git a/desktop/src/main/java/haveno/desktop/main/settings/preferences/PreferencesView.java b/desktop/src/main/java/haveno/desktop/main/settings/preferences/PreferencesView.java index da71490b9a..e12e4d4bc0 100644 --- a/desktop/src/main/java/haveno/desktop/main/settings/preferences/PreferencesView.java +++ b/desktop/src/main/java/haveno/desktop/main/settings/preferences/PreferencesView.java @@ -102,7 +102,7 @@ import org.apache.commons.lang3.StringUtils; @FxmlView public class PreferencesView extends ActivatableViewAndModel { private final User user; - private TextField btcExplorerTextField; + private TextField xmrExplorerTextField; private ComboBox userLanguageComboBox; private ComboBox userCountryComboBox; private ComboBox preferredTradeCurrencyComboBox; @@ -220,9 +220,9 @@ public class PreferencesView extends ActivatableViewAndModel btcExp = addTextFieldWithEditButton(root, ++gridRow, Res.get("setting.preferences.explorer")); - btcExplorerTextField = btcExp.first; - editCustomBtcExplorer = btcExp.second; + Tuple2 xmrExp = addTextFieldWithEditButton(root, ++gridRow, Res.get("setting.preferences.explorer")); + xmrExplorerTextField = xmrExp.first; + editCustomBtcExplorer = xmrExp.second; // deviation deviationInputTextField = addInputTextField(root, ++gridRow, @@ -688,7 +688,7 @@ public class PreferencesView extends ActivatableViewAndModel { preferences.setBlockChainExplorer(urlWindow.getEditedBlockChainExplorer()); - btcExplorerTextField.setText(preferences.getBlockChainExplorer().name); + xmrExplorerTextField.setText(preferences.getBlockChainExplorer().name); }) .closeButtonText(Res.get("shared.cancel")) .onClose(urlWindow::hide) diff --git a/desktop/src/main/java/haveno/desktop/main/shared/ChatView.java b/desktop/src/main/java/haveno/desktop/main/shared/ChatView.java index 236f8471e9..e2ea0bb1c9 100644 --- a/desktop/src/main/java/haveno/desktop/main/shared/ChatView.java +++ b/desktop/src/main/java/haveno/desktop/main/shared/ChatView.java @@ -26,7 +26,7 @@ import haveno.desktop.main.overlays.notifications.Notification; import haveno.desktop.main.overlays.popups.Popup; import haveno.desktop.util.DisplayUtils; import haveno.desktop.util.GUIUtil; - +import haveno.desktop.util.Layout; import haveno.core.locale.Res; import haveno.core.support.SupportManager; import haveno.core.support.SupportSession; @@ -43,7 +43,8 @@ import com.google.common.io.ByteStreams; import de.jensd.fx.fontawesome.AwesomeDude; import de.jensd.fx.fontawesome.AwesomeIcon; - +import de.jensd.fx.glyphs.materialdesignicons.MaterialDesignIcon; +import de.jensd.fx.glyphs.materialdesignicons.MaterialDesignIconView; import javafx.stage.FileChooser; import javafx.scene.Node; @@ -204,6 +205,7 @@ public class ChatView extends AnchorPane { inputTextArea = new HavenoTextArea(); inputTextArea.setPrefHeight(70); inputTextArea.setWrapText(true); + inputTextArea.getStyleClass().add("input-with-border"); if (!supportSession.isDisputeAgent()) { inputTextArea.setPromptText(Res.get("support.input.prompt")); @@ -271,7 +273,7 @@ public class ChatView extends AnchorPane { ImageView arrow = new ImageView(); Label headerLabel = new AutoTooltipLabel(); Label messageLabel = new AutoTooltipLabel(); - Label copyIcon = new Label(); + Label copyLabel = new Label(); HBox attachmentsBox = new HBox(); AnchorPane messageAnchorPane = new AnchorPane(); Label statusIcon = new Label(); @@ -292,10 +294,10 @@ public class ChatView extends AnchorPane { statusIcon.getStyleClass().add("small-text"); statusInfoLabel.getStyleClass().add("small-text"); statusInfoLabel.setPadding(new Insets(3, 0, 0, 0)); - copyIcon.setTooltip(new Tooltip(Res.get("shared.copyToClipboard"))); + copyLabel.setTooltip(new Tooltip(Res.get("shared.copyToClipboard"))); statusHBox.setSpacing(5); statusHBox.getChildren().addAll(statusIcon, statusInfoLabel); - messageAnchorPane.getChildren().addAll(bg, arrow, headerLabel, messageLabel, copyIcon, attachmentsBox, statusHBox); + messageAnchorPane.getChildren().addAll(bg, arrow, headerLabel, messageLabel, copyLabel, attachmentsBox, statusHBox); } @Override @@ -303,7 +305,13 @@ public class ChatView extends AnchorPane { UserThread.execute(() -> { super.updateItem(message, empty); if (message != null && !empty) { - copyIcon.setOnMouseClicked(e -> Utilities.copyToClipboard(messageLabel.getText())); + copyLabel.setOnMouseClicked(e -> { + Utilities.copyToClipboard(messageLabel.getText()); + Tooltip tp = new Tooltip(Res.get("shared.copiedToClipboard")); + Node node = (Node) e.getSource(); + UserThread.runAfter(() -> tp.hide(), 1); + tp.show(node, e.getScreenX() + Layout.PADDING, e.getScreenY() + Layout.PADDING); + }); messageLabel.setOnMouseClicked(event -> { if (2 > event.getClickCount()) { return; @@ -319,7 +327,7 @@ public class ChatView extends AnchorPane { AnchorPane.clearConstraints(headerLabel); AnchorPane.clearConstraints(arrow); AnchorPane.clearConstraints(messageLabel); - AnchorPane.clearConstraints(copyIcon); + AnchorPane.clearConstraints(copyLabel); AnchorPane.clearConstraints(statusHBox); AnchorPane.clearConstraints(attachmentsBox); @@ -328,7 +336,7 @@ public class ChatView extends AnchorPane { AnchorPane.setTopAnchor(headerLabel, 0d); AnchorPane.setBottomAnchor(arrow, bottomBorder + 5d); AnchorPane.setTopAnchor(messageLabel, 25d); - AnchorPane.setTopAnchor(copyIcon, 25d); + AnchorPane.setTopAnchor(copyLabel, 25d); AnchorPane.setBottomAnchor(attachmentsBox, bottomBorder + 10); boolean senderIsTrader = message.isSenderIsTrader(); @@ -341,20 +349,20 @@ public class ChatView extends AnchorPane { headerLabel.getStyleClass().removeAll("message-header", "my-message-header", "success-text", "highlight-static"); messageLabel.getStyleClass().removeAll("my-message", "message"); - copyIcon.getStyleClass().removeAll("my-message", "message"); + copyLabel.getStyleClass().removeAll("my-message", "message"); if (message.isSystemMessage()) { headerLabel.getStyleClass().addAll("message-header", "success-text"); bg.setId("message-bubble-green"); messageLabel.getStyleClass().add("my-message"); - copyIcon.getStyleClass().add("my-message"); + copyLabel.getStyleClass().add("my-message"); message.addWeakMessageStateListener(() -> UserThread.execute(() -> updateMsgState(message))); updateMsgState(message); } else if (isMyMsg) { headerLabel.getStyleClass().add("my-message-header"); bg.setId("message-bubble-blue"); messageLabel.getStyleClass().add("my-message"); - copyIcon.getStyleClass().add("my-message"); + copyLabel.getStyleClass().add("my-message"); if (supportSession.isClient()) arrow.setId("bubble_arrow_blue_left"); else @@ -375,7 +383,7 @@ public class ChatView extends AnchorPane { headerLabel.getStyleClass().add("message-header"); bg.setId("message-bubble-grey"); messageLabel.getStyleClass().add("message"); - copyIcon.getStyleClass().add("message"); + copyLabel.getStyleClass().add("message"); if (supportSession.isClient()) arrow.setId("bubble_arrow_grey_right"); else @@ -389,7 +397,7 @@ public class ChatView extends AnchorPane { AnchorPane.setRightAnchor(bg, border); AnchorPane.setLeftAnchor(messageLabel, padding); AnchorPane.setRightAnchor(messageLabel, msgLabelPaddingRight); - AnchorPane.setRightAnchor(copyIcon, padding); + AnchorPane.setRightAnchor(copyLabel, padding); AnchorPane.setLeftAnchor(attachmentsBox, padding); AnchorPane.setRightAnchor(attachmentsBox, padding); AnchorPane.setLeftAnchor(statusHBox, padding); @@ -400,7 +408,7 @@ public class ChatView extends AnchorPane { AnchorPane.setLeftAnchor(arrow, border); AnchorPane.setLeftAnchor(messageLabel, padding + arrowWidth); AnchorPane.setRightAnchor(messageLabel, msgLabelPaddingRight); - AnchorPane.setRightAnchor(copyIcon, padding); + AnchorPane.setRightAnchor(copyLabel, padding); AnchorPane.setLeftAnchor(attachmentsBox, padding + arrowWidth); AnchorPane.setRightAnchor(attachmentsBox, padding); AnchorPane.setRightAnchor(statusHBox, padding); @@ -411,7 +419,7 @@ public class ChatView extends AnchorPane { AnchorPane.setRightAnchor(arrow, border); AnchorPane.setLeftAnchor(messageLabel, padding); AnchorPane.setRightAnchor(messageLabel, msgLabelPaddingRight + arrowWidth); - AnchorPane.setRightAnchor(copyIcon, padding + arrowWidth); + AnchorPane.setRightAnchor(copyLabel, padding + arrowWidth); AnchorPane.setLeftAnchor(attachmentsBox, padding); AnchorPane.setRightAnchor(attachmentsBox, padding + arrowWidth); AnchorPane.setLeftAnchor(statusHBox, padding); @@ -454,8 +462,9 @@ public class ChatView extends AnchorPane { } // Need to set it here otherwise style is not correct - AwesomeDude.setIcon(copyIcon, AwesomeIcon.COPY, "16.0"); - copyIcon.getStyleClass().addAll("icon", "copy-icon-disputes"); + copyLabel.getStyleClass().addAll("icon", "copy-icon-disputes"); + MaterialDesignIconView copyIcon = new MaterialDesignIconView(MaterialDesignIcon.CONTENT_COPY, "16.0"); + copyLabel.setGraphic(copyIcon); // TODO There are still some cell rendering issues on updates setGraphic(messageAnchorPane); @@ -465,7 +474,7 @@ public class ChatView extends AnchorPane { messageAnchorPane.prefWidthProperty().unbind(); - copyIcon.setOnMouseClicked(null); + copyLabel.setOnMouseClicked(null); messageLabel.setOnMouseClicked(null); setGraphic(null); } diff --git a/desktop/src/main/java/haveno/desktop/main/support/SupportView.java b/desktop/src/main/java/haveno/desktop/main/support/SupportView.java index 42503f315f..12a000c00b 100644 --- a/desktop/src/main/java/haveno/desktop/main/support/SupportView.java +++ b/desktop/src/main/java/haveno/desktop/main/support/SupportView.java @@ -139,9 +139,9 @@ public class SupportView extends ActivatableView { // Has to be called before loadView updateAgentTabs(); - tradersMediationDisputesTab.setText(Res.get("support.tab.mediation.support").toUpperCase()); - tradersRefundDisputesTab.setText(Res.get("support.tab.refund.support").toUpperCase()); - tradersArbitrationDisputesTab.setText(Res.get("support.tab.arbitration.support").toUpperCase()); + tradersMediationDisputesTab.setText(Res.get("support.tab.mediation.support")); + tradersRefundDisputesTab.setText(Res.get("support.tab.refund.support")); + tradersArbitrationDisputesTab.setText(Res.get("support.tab.arbitration.support")); navigationListener = (viewPath, data) -> { if (viewPath.size() == 3 && viewPath.indexOf(SupportView.class) == 1) @@ -221,16 +221,16 @@ public class SupportView extends ActivatableView { // We might get that method called before we have the map is filled in the arbitratorManager if (arbitratorTab != null) { - arbitratorTab.setText(Res.get("support.tab.ArbitratorsSupportTickets", Res.get("shared.arbitrator")).toUpperCase()); + arbitratorTab.setText(Res.get("support.tab.ArbitratorsSupportTickets", Res.get("shared.arbitrator"))); } if (signedOfferTab != null) { - signedOfferTab.setText(Res.get("support.tab.SignedOffers").toUpperCase()); + signedOfferTab.setText(Res.get("support.tab.SignedOffers")); } if (mediatorTab != null) { - mediatorTab.setText(Res.get("support.tab.ArbitratorsSupportTickets", Res.get("shared.mediator")).toUpperCase()); + mediatorTab.setText(Res.get("support.tab.ArbitratorsSupportTickets", Res.get("shared.mediator"))); } if (refundAgentTab != null) { - refundAgentTab.setText(Res.get("support.tab.ArbitratorsSupportTickets", Res.get("shared.refundAgentForSupportStaff")).toUpperCase()); + refundAgentTab.setText(Res.get("support.tab.ArbitratorsSupportTickets", Res.get("shared.refundAgentForSupportStaff"))); } } diff --git a/desktop/src/main/java/haveno/desktop/main/support/dispute/DisputeView.java b/desktop/src/main/java/haveno/desktop/main/support/dispute/DisputeView.java index 5af027c16c..87c05c997c 100644 --- a/desktop/src/main/java/haveno/desktop/main/support/dispute/DisputeView.java +++ b/desktop/src/main/java/haveno/desktop/main/support/dispute/DisputeView.java @@ -223,12 +223,8 @@ public abstract class DisputeView extends ActivatableView implements @Override public void initialize() { - Label label = new AutoTooltipLabel(Res.get("support.filter")); - HBox.setMargin(label, new Insets(5, 0, 0, 0)); - HBox.setHgrow(label, Priority.NEVER); - filterTextField = new InputTextField(); - filterTextField.setPromptText(Res.get("support.filter.prompt")); + filterTextField.setPromptText(Res.get("shared.filter")); Tooltip tooltip = new Tooltip(); tooltip.setShowDelay(Duration.millis(100)); tooltip.setShowDuration(Duration.seconds(10)); @@ -298,8 +294,7 @@ public abstract class DisputeView extends ActivatableView implements HBox filterBox = new HBox(); filterBox.setSpacing(5); - filterBox.getChildren().addAll(label, - filterTextField, + filterBox.getChildren().addAll(filterTextField, alertIconLabel, spacer, reOpenButton, @@ -311,6 +306,7 @@ public abstract class DisputeView extends ActivatableView implements VBox.setVgrow(filterBox, Priority.NEVER); tableView = new TableView<>(); + GUIUtil.applyTableStyle(tableView); VBox.setVgrow(tableView, Priority.SOMETIMES); tableView.setMinHeight(150); @@ -957,7 +953,6 @@ public abstract class DisputeView extends ActivatableView implements { setMaxWidth(80); setMinWidth(65); - getStyleClass().addAll("first-column", "avatar-column"); setSortable(false); } }; @@ -1354,7 +1349,6 @@ public abstract class DisputeView extends ActivatableView implements setMinWidth(50); } }; - column.getStyleClass().add("last-column"); column.setCellValueFactory((dispute) -> new ReadOnlyObjectWrapper<>(dispute.getValue())); column.setCellFactory( new Callback<>() { diff --git a/desktop/src/main/java/haveno/desktop/main/support/dispute/agent/DisputeAgentView.java b/desktop/src/main/java/haveno/desktop/main/support/dispute/agent/DisputeAgentView.java index 10c657dce7..5901d48769 100644 --- a/desktop/src/main/java/haveno/desktop/main/support/dispute/agent/DisputeAgentView.java +++ b/desktop/src/main/java/haveno/desktop/main/support/dispute/agent/DisputeAgentView.java @@ -208,7 +208,6 @@ public abstract class DisputeAgentView extends DisputeView implements MultipleHo protected void setupTable() { super.setupTable(); - stateColumn.getStyleClass().remove("last-column"); tableView.getColumns().add(getAlertColumn()); } @@ -243,7 +242,6 @@ public abstract class DisputeAgentView extends DisputeView implements MultipleHo setMinWidth(50); } }; - column.getStyleClass().add("last-column"); column.setCellValueFactory((dispute) -> new ReadOnlyObjectWrapper<>(dispute.getValue())); column.setCellFactory( c -> new TableCell<>() { diff --git a/desktop/src/main/java/haveno/desktop/theme-dark.css b/desktop/src/main/java/haveno/desktop/theme-dark.css index d045e3b5b0..1e56814261 100644 --- a/desktop/src/main/java/haveno/desktop/theme-dark.css +++ b/desktop/src/main/java/haveno/desktop/theme-dark.css @@ -20,10 +20,10 @@ /* haveno main colors */ -bs-color-primary: #0b65da; -bs-color-primary-dark: #0c59bd; - -bs-text-color: #dadada; - -bs-background-color: #29292a; - -bs-background-gray: #2B2B2B; - -bs-content-background-gray: #1F1F1F; + -bs-text-color: white; + -bs-background-color: black; + -bs-background-gray: transparent; + -bs-content-background-gray: black; /* fifty shades of gray */ -bs-color-gray-13: #bbb; @@ -43,7 +43,19 @@ -bs-color-gray-bbb: #5a5a5a; -bs-color-gray-aaa: #29292a; -bs-color-gray-fafa: #0a0a0a; - -bs-color-gray-background: #1F1F1F; + -bs-color-gray-background: black; + -bs-color-background-popup: rgb(38, 38, 38); + -bs-color-background-popup-blur: rgb(9, 9, 9); + -bs-color-background-popup-input: rgb(9, 9, 9); + -bs-color-background-form-field: rgb(26, 26, 26); + -bs-color-background-form-field-readonly: rgb(18, 18, 18); + -bs-color-border-form-field: rgb(65, 65, 65); + -bs-color-background-pane: rgb(15, 15, 15); + -bs-color-background-row-even: rgb(19, 19, 19); + -bs-color-background-row-odd: rgb(9, 9, 9); + -bs-color-table-cell-dim: -bs-color-gray-ccc; + -bs-text-color-dim1: rgb(87, 87, 87); + -bs-text-color-dim2: rgb(130, 130, 130); /* lesser used colors */ -bs-color-blue-5: #0a4576; @@ -70,11 +82,15 @@ -bs-rd-nav-border: #535353; -bs-rd-nav-primary-border: rgba(0, 0, 0, 0); -bs-rd-nav-border-color: rgba(255, 255, 255, 0.1); - -bs-rd-nav-background: #141414; - -bs-rd-nav-primary-background: rgba(255, 255, 255, 0.015); - -bs-rd-nav-selected: #fff; - -bs-rd-nav-deselected: rgba(255, 255, 255, 0.45); - -bs-rd-nav-button-hover: rgba(255, 255, 255, 0.03); + -bs-rd-nav-background: rgb(15, 15, 15); + -bs-rd-nav-primary-background: rgb(15, 15, 15); + -bs-rd-nav-selected: black; + -bs-rd-nav-deselected: rgba(255, 255, 255, 1); + -bs-rd-nav-secondary-selected: -fx-accent; + -bs-rd-nav-secondary-deselected: -bs-rd-font-light; + -bs-rd-nav-button-hover: derive(-bs-rd-nav-background, 10%); + -bs-rd-nav-primary-button-hover: derive(-bs-rd-nav-primary-background, 10%); + -bs-rd-nav-hover-text: black; -bs-content-pane-bg-top: #212121; -bs-rd-tab-border: rgba(255, 255, 255, 0.00); @@ -90,7 +106,7 @@ -bs-footer-pane-text: #cfcecf; -bs-footer-pane-line: #29292a; - -bs-rd-font-balance: #bbbbbb; + -bs-rd-font-balance: white; -bs-rd-font-dark-gray: #d4d4d4; -bs-rd-font-dark: #cccccc; -bs-rd-font-light: #b4b4b4; @@ -99,28 +115,28 @@ -bs-rd-font-confirmation-label: #504f52; -bs-rd-font-balance-label: #999999; - -bs-text-color-transparent-dark: rgba(29, 29, 33, 0.54); + -bs-text-color-dropshadow: rgba(45, 45, 49, .75); + -bs-text-color-dropshadow-light-mode: transparent; -bs-text-color-transparent: rgba(29, 29, 33, 0.2); -bs-color-gray-line: #504f52; -bs-rd-separator: #1F1F1F; - -bs-rd-separator-dark: #1F1F1F; + -bs-rd-separator-dark: rgb(255, 255, 255, 0.1); -bs-rd-error-red: #d83431; -bs-rd-error-field: #521C1C; -bs-rd-message-bubble: #0086c6; -bs-rd-tooltip-truncated: #afaeb0; - -bs-toggle-selected: #25b135; + /*-bs-toggle-selected: rgb(12, 89, 189);*/ + -bs-toggle-selected: rgb(12, 89, 190); -bs-warning: #db6300; - -bs-buy: #006600; - -bs-buy-focus: black; - -bs-buy-hover: #237b2d; - -bs-buy-transparent: rgba(46, 163, 60, 0.3); - -bs-sell: #660000; - -bs-sell-focus: #090202; - -bs-sell-hover: #b42522; - -bs-sell-transparent: rgba(216, 52, 49, 0.3); - -bs-volume-transparent: rgba(37, 177, 54, 0.5); + -bs-buy: rgb(80, 180, 90); + -bs-buy-focus: derive(-bs-buy, -50%); + -bs-buy-hover: derive(-bs-buy, -10%); + -bs-sell: rgb(213, 63, 46); + -bs-sell-focus: derive(-bs-sell, -50%); + -bs-sell-hover: derive(-bs-sell, -10%); + -bs-volume-transparent: -bs-buy; -bs-candle-stick-average-line: rgba(21, 188, 29, 0.8); -bs-candle-stick-loss: #ee6563; -bs-candle-stick-won: #15bc1d; @@ -154,7 +170,7 @@ /* Monero orange color code */ -xmr-orange: #f26822; - -bs-support-chat-background: #cccccc; + -bs-support-chat-background: rgb(125, 125, 125); } /* table view */ @@ -164,7 +180,7 @@ } .table-view .column-header { - -fx-background-color: derive(-bs-background-color,-50%); + -fx-background-color: -bs-color-background-pane; -fx-border-width: 0; } @@ -173,21 +189,31 @@ -fx-border-width: 0; } +/** These must be set to override default styles */ .table-view .table-row-cell:even .table-cell { - -fx-background-color: derive(-bs-background-color, -5%); - -fx-border-color: derive(-bs-background-color, -5%); + -fx-background-color: -bs-color-background-row-even; + -fx-border-color: -bs-color-background-row-even; } - .table-view .table-row-cell:odd .table-cell { - -fx-background-color: derive(-bs-background-color,-30%); - -fx-border-color: derive(-bs-background-color,-30%); + -fx-background-color: -bs-color-background-row-odd; + -fx-border-color: -bs-color-background-row-odd; } - .table-view .table-row-cell:selected .table-cell { -fx-background: -fx-accent; -fx-background-color: -fx-selection-bar; -fx-border-color: -fx-selection-bar; } +.table-view .table-row-cell:selected .table-cell, +.table-view .table-row-cell:selected .table-cell .label, +.table-view .table-row-cell:selected .table-cell .text { + -fx-text-fill: -fx-dark-text-color; +} +.table-view .table-row-cell:selected .table-cell .hyperlink, +.table-view .table-row-cell:selected .table-cell .hyperlink .text, +.table-view .table-row-cell:selected .table-cell .hyperlink-with-icon, +.table-view .table-row-cell:selected .table-cell .hyperlink-with-icon .text { + -fx-fill: -fx-dark-text-color; +} .table-row-cell { -fx-border-color: -bs-background-color; @@ -208,35 +234,49 @@ -fx-background-color: -bs-tab-content-area; } -.jfx-tab-pane .viewport { - -fx-background-color: -bs-viewport-background; + +.jfx-tab-pane .headers-region .tab:selected .tab-container .tab-label { + -fx-text-fill: white; } -.jfx-tab-pane .tab-header-background { - -fx-background-color: derive(-bs-color-gray-background, -20%); +.nav-secondary-button:selected .text { + -fx-fill: white; +} + +.jfx-tab-pane .headers-region > .tab > .jfx-rippler { + -jfx-rippler-fill: none; +} + +.jfx-tab-pane .viewport { + -fx-background-color: -bs-viewport-background; } /* text field */ .jfx-text-field, .jfx-text-area, .jfx-combo-box, .jfx-combo-box > .list-cell { - -fx-background-color: derive(-bs-background-color, 15%); -fx-prompt-text-fill: -bs-color-gray-6; -fx-text-fill: -bs-color-gray-12; } -.jfx-text-area:readonly, .jfx-text-field:readonly, +.jfx-text-area:readonly, +.jfx-text-field:readonly, .hyperlink-with-icon { -fx-background: -bs-background-color; - -fx-background-color: -bs-background-color; + -fx-background-color: -bs-color-background-form-field-readonly; -fx-prompt-text-fill: -bs-color-gray-2; -fx-text-fill: -bs-color-gray-3; } + +.popover > .content .text-field { + -fx-background-color: -bs-color-background-form-field !important; +} + .jfx-combo-box > .text, .jfx-text-field-top-label, .jfx-text-area-top-label { -fx-text-fill: -bs-color-gray-11; } -.input-with-border { +.offer-input { -fx-border-color: -bs-color-gray-2; -fx-border-width: 0 0 10 0; } @@ -254,11 +294,6 @@ -fx-text-fill: -fx-dark-text-color; } -.chart-pane, .chart-plot-background, -#charts .chart-plot-background, -#charts-dao .chart-plot-background { - -fx-background-color: transparent; -} .axis:top, .axis:right, .axis:bottom, .axis:left { -fx-border-color: transparent transparent transparent transparent; } @@ -332,7 +367,7 @@ } .combo-box-popup > .list-view{ - -fx-background-color: -bs-background-color; + -fx-background-color: -bs-color-background-pane; } .jfx-combo-box > .arrow-button > .arrow { @@ -352,7 +387,6 @@ } .list-view .list-cell:odd, .list-view .list-cell:even { - -fx-background-color: -bs-background-color; -fx-border-width: 0; } @@ -371,18 +405,6 @@ -fx-border-width: 0; } -.jfx-text-field { - -fx-background-radius: 4; -} - -.jfx-text-field > .input-line { - -fx-translate-x: 0; -} - -.jfx-text-field > .input-focused-line { - -fx-translate-x: 0; -} - .jfx-text-field-top-label { -fx-text-fill: -bs-color-gray-dim; } @@ -394,14 +416,13 @@ -fx-background-color: derive(-bs-background-color, 15%); } .jfx-combo-box:error, -.jfx-text-field:error{ +.jfx-text-field:error { -fx-text-fill: -bs-rd-error-red; -fx-background-color: -bs-rd-error-field; } .jfx-combo-box:error:focused, .jfx-text-field:error:focused{ - -fx-text-fill: -bs-rd-error-red; -fx-background-color: derive(-bs-rd-error-field, -5%); } @@ -417,11 +438,7 @@ -jfx-disable-animation: true; } -.jfx-password-field { - -fx-background-color: derive(-bs-background-color, -15%); -} - -.input-with-border { +.offer-input { -fx-border-width: 0; -fx-border-color: -bs-background-color; } @@ -448,11 +465,6 @@ -jfx-disable-animation: true; } -.top-navigation { - -fx-border-width: 0 0 0 0; - -fx-padding: 0 7 0 0; -} - .nav-price-balance { -fx-effect: null; } @@ -462,37 +474,17 @@ } .nav-button:selected { - -fx-background-color: derive(-bs-color-primary-dark, -10%); + -fx-background-color: white; -fx-effect: null; } -.nav-button:hover { - -fx-background-color: -bs-rd-nav-button-hover; -} - .nav-primary .nav-button:selected { - -fx-background-color: derive(-bs-color-primary-dark, -5%); + -fx-background-color: derive(white, -5%); } .table-view { -fx-border-color: transparent; } -.table-view .table-cell { - -fx-padding: 6 0 4 0; - -fx-text-fill: -bs-text-color; -} -.table-view .table-cell.last-column { - -fx-padding: 6 10 4 0; -} - -.table-view .table-cell.last-column.avatar-column { - -fx-padding: 6 0 4 0; -} - -.table-view .table-cell.first-column { - -fx-padding: 6 0 4 10; -} - .jfx-tab-pane .headers-region .tab .tab-container .tab-label { -fx-cursor: hand; -jfx-disable-animation: true; @@ -559,12 +551,95 @@ } .toggle-button-no-slider { - -fx-focus-color: transparent; - -fx-faint-focus-color: transparent; - -fx-background-radius: 3; - -fx-background-insets: 0, 1; + -fx-background-color: -bs-color-background-form-field; } .toggle-button-no-slider:selected { + -fx-text-fill: white; + -fx-background-color: -bs-color-gray-ccc; + -fx-border-color: -bs-color-gray-ccc; + -fx-border-width: 1px; +} + +.toggle-button-no-slider:hover { + -fx-cursor: hand; + -fx-background-color: -bs-color-gray-ddd; + -fx-border-color: -bs-color-gray-ddd; +} + +.toggle-button-no-slider:selected:hover { + -fx-cursor: hand; + -fx-background-color: -bs-color-gray-3; + -fx-border-color: -bs-color-gray-3; +} + +.toggle-button-no-slider:pressed, .toggle-button-no-slider:selected:hover:pressed { -fx-background-color: -bs-color-gray-bbb; } + +#image-logo-splash { + -fx-image: url("../../images/logo_splash_dark.png"); +} + +#image-logo-splash-testnet { + -fx-image: url("../../images/logo_splash_testnet_dark.png"); +} + +#image-logo-landscape { + -fx-image: url("../../images/logo_landscape_dark.png"); +} + +.table-view .placeholder { + -fx-background-color: -bs-color-background-pane; +} + +#charts .default-color0.chart-series-area-fill { + -fx-fill: linear-gradient(to bottom, + rgba(80, 181, 90, 0.45) 0%, + rgba(80, 181, 90, 0.0) 100% + ); +} + +#charts .default-color1.chart-series-area-fill { + -fx-fill: linear-gradient(to bottom, + rgba(213, 63, 46, 0.45) 0%, + rgba(213, 63, 46, 0.0) 100% + ); +} + +.table-view .table-row-cell .label { + -fx-text-fill: -bs-text-color; +} + +.table-view.non-interactive-table .table-cell, +.table-view.non-interactive-table .table-cell .label, +.table-view.non-interactive-table .label, +.table-view.non-interactive-table .text, +.table-view.non-interactive-table .hyperlink, +.table-view.non-interactive-table .hyperlink-with-icon, +.table-view.non-interactive-table .table-row-cell .hyperlink .text { + -fx-text-fill: -bs-color-gray-dim; +} + +.table-view.non-interactive-table .hyperlink, +.table-view.non-interactive-table .hyperlink-with-icon, +.table-view.non-interactive-table .table-row-cell .hyperlink .text { + -fx-fill: -bs-color-gray-dim; +} + +.table-view.non-interactive-table .table-cell.highlight-text, +.table-view.non-interactive-table .table-cell.highlight-text .label, +.table-view.non-interactive-table .table-cell.highlight-text .text, +.table-view.non-interactive-table .table-cell.highlight-text .hyperlink, +.table-view.non-interactive-table .table-cell.highlight-text .hyperlink .text { + -fx-text-fill: -fx-dark-text-color; +} + +/* Match specificity to override. */ +.table-view.non-interactive-table .table-cell.highlight-text .zero-decimals { + -fx-text-fill: -bs-color-gray-3; +} + +.regular-text-color { + -fx-text-fill: -bs-text-color; +} diff --git a/desktop/src/main/java/haveno/desktop/theme-light.css b/desktop/src/main/java/haveno/desktop/theme-light.css index 7605eb1819..160a21e3c3 100644 --- a/desktop/src/main/java/haveno/desktop/theme-light.css +++ b/desktop/src/main/java/haveno/desktop/theme-light.css @@ -41,12 +41,17 @@ -bs-rd-green: #0b65da; -bs-rd-green-dark: #3EA34A; -bs-rd-nav-selected: #0b65da; - -bs-rd-nav-deselected: rgba(255, 255, 255, 0.75); - -bs-rd-nav-background: #0c59bd; + -bs-rd-nav-deselected: rgba(255, 255, 255, 1); + -bs-rd-nav-secondary-selected: -fx-accent; + -bs-rd-nav-secondary-deselected: -bs-rd-font-light; + -bs-rd-nav-background: #0b65da; -bs-rd-nav-primary-background: #0b65da; + -bs-rd-nav-button-hover: derive(-bs-rd-nav-background, 10%); + -bs-rd-nav-primary-button-hover: derive(-bs-rd-nav-primary-background, 10%); -bs-rd-nav-primary-border: #0B65DA; -bs-rd-nav-border: #535353; -bs-rd-nav-border-color: rgba(255, 255, 255, 0.31); + -bs-rd-nav-hover-text: white; -bs-rd-tab-border: #e2e0e0; -bs-tab-content-area: #ffffff; -bs-color-gray-background: #f2f2f2; @@ -58,32 +63,31 @@ -bs-footer-pane-background: #dddddd; -bs-footer-pane-text: #4b4b4b; -bs-footer-pane-line: #bbb; - -bs-rd-font-balance: #4f4f4f; + -bs-rd-font-balance: white; -bs-rd-font-dark-gray: #3c3c3c; -bs-rd-font-dark: #4b4b4b; -bs-rd-font-light: #8d8d8d; -bs-rd-font-lighter: #a7a7a7; -bs-rd-font-confirmation-label: #504f52; - -bs-rd-font-balance-label: #8e8e8e; - -bs-text-color-transparent-dark: rgba(0, 0, 0, 0.54); + -bs-rd-font-balance-label: #bbbbbb; + -bs-text-color-dropshadow: rgba(0, 0, 0, 0.54); + -bs-text-color-dropshadow-light-mode: rgba(0, 0, 0, 0.54); -bs-text-color-transparent: rgba(0, 0, 0, 0.2); -bs-color-gray-line: #979797; -bs-rd-separator: #dbdbdb; - -bs-rd-separator-dark: #d5e0d6; + -bs-rd-separator-dark: rgb(255, 255, 255, 0.1); -bs-rd-error-red: #dd0000; -bs-rd-message-bubble: #0086c6; -bs-toggle-selected: #7b7b7b; -bs-rd-tooltip-truncated: #0a0a0a; -bs-warning: #ff8a2b; - -bs-buy: #3ea34a; + -bs-buy: rgb(80, 180, 90); -bs-buy-focus: derive(-bs-buy, -50%); -bs-buy-hover: derive(-bs-buy, -10%); - -bs-buy-transparent: rgba(62, 163, 74, 0.3); - -bs-sell: #d73030; + -bs-sell: rgb(213, 63, 46); -bs-sell-focus: derive(-bs-sell, -50%); -bs-sell-hover: derive(-bs-sell, -10%); - -bs-sell-transparent: rgba(215, 48, 48, 0.3); - -bs-volume-transparent: rgba(37, 177, 53, 0.3); + -bs-volume-transparent: -bs-buy; -bs-candle-stick-average-line: -bs-rd-green; -bs-candle-stick-loss: #fe3001; -bs-candle-stick-won: #20b221; @@ -104,6 +108,19 @@ -bs-prompt-text: -fx-control-inner-background; -bs-soft-red: #aa4c3b; -bs-turquoise-light: #11eeee; + -bs-color-border-form-field: -bs-background-gray; + -bs-color-background-form-field-readonly: -bs-color-gray-1; + -bs-color-background-pane: -bs-background-color; + -bs-color-background-row-even: -bs-color-background-pane; + -bs-color-background-row-odd: derive(-bs-color-background-pane, -6%); + -bs-color-table-cell-dim: -bs-color-gray-ccc; + -bs-color-background-popup: white; + -bs-color-background-popup-blur: white; + -bs-color-background-popup-input: -bs-color-gray-background; + -bs-color-background-form-field: white; + -bs-text-color-dim1: black; + -bs-text-color-dim2: black; + /* Monero orange color code */ -xmr-orange: #f26822; @@ -126,7 +143,33 @@ -fx-background-color: -bs-color-gray-3; } -.toggle-button-no-slider { - -fx-focus-color: transparent; - -fx-faint-focus-color: transparent; +#image-logo-splash { + -fx-image: url("../../images/logo_splash_light.png"); +} + +#image-logo-splash-testnet { + -fx-image: url("../../images/logo_splash_testnet_light.png"); +} + +#image-logo-landscape { + -fx-image: url("../../images/logo_landscape_light.png"); +} + +#charts .default-color0.chart-series-area-fill { + -fx-fill: linear-gradient(to bottom, + rgba(62, 163, 74, 0.45) 0%, + rgba(62, 163, 74, 0.0) 100% + ); +} + +#charts .default-color1.chart-series-area-fill { + -fx-fill: linear-gradient(to bottom, + rgba(215, 48, 48, 0.45) 0%, + rgba(215, 48, 48, 0.0) 100% + ); +} + +/* All inputs have border in light mode. */ +.jfx-combo-box, .jfx-text-field, .jfx-text-area, .jfx-password-field { + -fx-border-color: -bs-color-border-form-field; } diff --git a/desktop/src/main/java/haveno/desktop/util/CssTheme.java b/desktop/src/main/java/haveno/desktop/util/CssTheme.java index 1e1c547607..4648d07eeb 100644 --- a/desktop/src/main/java/haveno/desktop/util/CssTheme.java +++ b/desktop/src/main/java/haveno/desktop/util/CssTheme.java @@ -58,6 +58,10 @@ public class CssTheme { scene.getStylesheets().add(cssThemeFolder + "theme-dev.css"); } + public static int getCurrentTheme() { + return currentCSSTheme; + } + public static boolean isDarkTheme() { return currentCSSTheme == CSS_THEME_DARK; } diff --git a/desktop/src/main/java/haveno/desktop/util/CurrencyList.java b/desktop/src/main/java/haveno/desktop/util/CurrencyList.java index 3e68ccf876..88bef5ab1c 100644 --- a/desktop/src/main/java/haveno/desktop/util/CurrencyList.java +++ b/desktop/src/main/java/haveno/desktop/util/CurrencyList.java @@ -18,6 +18,8 @@ package haveno.desktop.util; import com.google.common.collect.Lists; + +import haveno.core.locale.CurrencyUtil; import haveno.core.locale.TradeCurrency; import haveno.core.user.Preferences; import javafx.collections.FXCollections; @@ -92,14 +94,13 @@ public class CurrencyList { } private Comparator getComparator() { - Comparator result; if (preferences.isSortMarketCurrenciesNumerically()) { - Comparator byCount = Comparator.comparingInt(left -> left.numTrades); - result = byCount.reversed(); + return Comparator + .comparingInt((CurrencyListItem item) -> item.numTrades).reversed() + .thenComparing(item -> CurrencyUtil.isCryptoCurrency(item.tradeCurrency.getCode()) ? item.tradeCurrency.getName() : item.tradeCurrency.getCode()); } else { - result = Comparator.comparing(item -> item.tradeCurrency); + return Comparator.comparing(item -> CurrencyUtil.isCryptoCurrency(item.tradeCurrency.getCode()) ? item.tradeCurrency.getName() : item.tradeCurrency.getCode()); } - return result; } private Map countTrades(List currencies) { diff --git a/desktop/src/main/java/haveno/desktop/util/FormBuilder.java b/desktop/src/main/java/haveno/desktop/util/FormBuilder.java index ae3e7ed266..dbee100f06 100644 --- a/desktop/src/main/java/haveno/desktop/util/FormBuilder.java +++ b/desktop/src/main/java/haveno/desktop/util/FormBuilder.java @@ -135,6 +135,24 @@ public class FormBuilder { return titledGroupBg; } + /////////////////////////////////////////////////////////////////////////////////////////// + // Divider + /////////////////////////////////////////////////////////////////////////////////////////// + + public static Region addSeparator(GridPane gridPane, int rowIndex) { + Region separator = new Region(); + separator.getStyleClass().add("grid-pane-separator"); + separator.setPrefHeight(1); + separator.setMinHeight(1); + separator.setMaxHeight(1); + GridPane.setRowIndex(separator, rowIndex); + GridPane.setColumnIndex(separator, 0); + GridPane.setColumnSpan(separator, 2); + gridPane.getChildren().add(separator); + separator.setPrefHeight(1); + GridPane.setMargin(separator, new Insets(0, 0, 3, 0)); + return separator; + } /////////////////////////////////////////////////////////////////////////////////////////// // Label @@ -361,7 +379,7 @@ public class FormBuilder { textField.setPrefWidth(Layout.INITIAL_WINDOW_WIDTH); Button button = new AutoTooltipButton("..."); - button.setStyle("-fx-min-width: 26; -fx-pref-height: 26; -fx-padding: 0 0 10 0; -fx-background-color: -fx-background;"); + button.setStyle("-fx-min-width: 32; -fx-padding: 0 0 10 0; -fx-background-color: -fx-background;"); button.managedProperty().bind(button.visibleProperty()); HBox hbox = new HBox(textField, button); @@ -369,6 +387,7 @@ public class FormBuilder { hbox.setSpacing(8); VBox vbox = getTopLabelVBox(0); + vbox.setSpacing(2); vbox.getChildren().addAll(getTopLabel(title), hbox); gridPane.getChildren().add(vbox); @@ -490,6 +509,7 @@ public class FormBuilder { GridPane.setColumnIndex(textArea, 1); GridPane.setMargin(label, new Insets(top, 0, 0, 0)); GridPane.setHalignment(label, HPos.LEFT); + GridPane.setValignment(label, VPos.TOP); GridPane.setMargin(textArea, new Insets(top, 0, 0, 0)); return new Tuple2<>(label, textArea); @@ -617,6 +637,7 @@ public class FormBuilder { JFXTextArea textArea = new HavenoTextArea(); textArea.setPromptText(prompt); textArea.setLabelFloat(true); + textArea.getStyleClass().add("label-float"); textArea.setWrapText(true); GridPane.setRowIndex(textArea, rowIndex); @@ -805,9 +826,9 @@ public class FormBuilder { } public static InputTextField addInputTextField(GridPane gridPane, int rowIndex, String title, double top) { - InputTextField inputTextField = new InputTextField(); inputTextField.setLabelFloat(true); + inputTextField.getStyleClass().add("label-float"); inputTextField.setPromptText(title); GridPane.setRowIndex(inputTextField, rowIndex); GridPane.setColumnIndex(inputTextField, 0); @@ -884,6 +905,8 @@ public class FormBuilder { public static PasswordTextField addPasswordTextField(GridPane gridPane, int rowIndex, String title, double top) { PasswordTextField passwordField = new PasswordTextField(); + passwordField.getStyleClass().addAll("label-float"); + GUIUtil.applyFilledStyle(passwordField); passwordField.setPromptText(title); GridPane.setRowIndex(passwordField, rowIndex); GridPane.setColumnIndex(passwordField, 0); @@ -1006,8 +1029,10 @@ public class FormBuilder { InputTextField inputTextField1 = new InputTextField(); inputTextField1.setPromptText(title1); inputTextField1.setLabelFloat(true); + inputTextField1.getStyleClass().add("label-float"); InputTextField inputTextField2 = new InputTextField(); inputTextField2.setLabelFloat(true); + inputTextField2.getStyleClass().add("label-float"); inputTextField2.setPromptText(title2); HBox hBox = new HBox(); @@ -1228,6 +1253,7 @@ public class FormBuilder { public static ComboBox addComboBox(GridPane gridPane, int rowIndex, int top) { final JFXComboBox comboBox = new JFXComboBox<>(); + GUIUtil.applyFilledStyle(comboBox); GridPane.setRowIndex(comboBox, rowIndex); GridPane.setMargin(comboBox, new Insets(top, 0, 0, 0)); @@ -1264,7 +1290,9 @@ public class FormBuilder { VBox vBox = getTopLabelVBox(top); final JFXComboBox comboBox = new JFXComboBox<>(); + GUIUtil.applyFilledStyle(comboBox); comboBox.setPromptText(prompt); + comboBox.setPadding(new Insets(top, 0, 0, 12)); vBox.getChildren().addAll(label, comboBox); @@ -1389,7 +1417,9 @@ public class FormBuilder { public static ComboBox addComboBox(GridPane gridPane, int rowIndex, String title, double top) { JFXComboBox comboBox = new JFXComboBox<>(); + GUIUtil.applyFilledStyle(comboBox); comboBox.setLabelFloat(true); + comboBox.getStyleClass().add("label-float"); comboBox.setPromptText(title); comboBox.setMaxWidth(Double.MAX_VALUE); @@ -1399,6 +1429,7 @@ public class FormBuilder { GridPane.setRowIndex(comboBox, rowIndex); GridPane.setColumnIndex(comboBox, 0); + comboBox.setPadding(new Insets(0, 0, 0, 12)); GridPane.setMargin(comboBox, new Insets(top + Layout.FLOATING_LABEL_DISTANCE, 0, 0, 0)); gridPane.getChildren().add(comboBox); @@ -1407,7 +1438,9 @@ public class FormBuilder { public static AutocompleteComboBox addAutocompleteComboBox(GridPane gridPane, int rowIndex, String title, double top) { var comboBox = new AutocompleteComboBox(); + GUIUtil.applyFilledStyle(comboBox); comboBox.setLabelFloat(true); + comboBox.getStyleClass().add("label-float"); comboBox.setPromptText(title); comboBox.setMaxWidth(Double.MAX_VALUE); @@ -1469,6 +1502,7 @@ public class FormBuilder { AutocompleteComboBox comboBox = new AutocompleteComboBox<>(); comboBox.setPromptText(titleCombobox); comboBox.setLabelFloat(true); + comboBox.getStyleClass().add("label-float"); topLabelVBox2.getChildren().addAll(topLabel2, comboBox); hBox.getChildren().addAll(topLabelVBox1, topLabelVBox2); @@ -1498,7 +1532,9 @@ public class FormBuilder { hBox.setSpacing(10); ComboBox comboBox1 = new JFXComboBox<>(); + GUIUtil.applyFilledStyle(comboBox1); ComboBox comboBox2 = new JFXComboBox<>(); + GUIUtil.applyFilledStyle(comboBox2); hBox.getChildren().addAll(comboBox1, comboBox2); final Tuple2 topLabelWithVBox = addTopLabelWithVBox(gridPane, rowIndex, title, hBox, top); @@ -1526,8 +1562,10 @@ public class FormBuilder { hBox.setSpacing(10); JFXComboBox comboBox = new JFXComboBox<>(); + GUIUtil.applyFilledStyle(comboBox); comboBox.setPromptText(titleCombobox); comboBox.setLabelFloat(true); + comboBox.getStyleClass().add("label-float"); TextField textField = new HavenoTextField(); @@ -1570,6 +1608,7 @@ public class FormBuilder { button.setDefaultButton(true); ComboBox comboBox = new JFXComboBox<>(); + GUIUtil.applyFilledStyle(comboBox); hBox.getChildren().addAll(comboBox, button); @@ -1604,6 +1643,7 @@ public class FormBuilder { hBox.setSpacing(10); ComboBox comboBox = new JFXComboBox<>(); + GUIUtil.applyFilledStyle(comboBox); TextField textField = new TextField(textFieldText); textField.setEditable(false); textField.setMouseTransparent(true); @@ -1797,6 +1837,7 @@ public class FormBuilder { return new Tuple2<>(label, textFieldWithCopyIcon); } + /////////////////////////////////////////////////////////////////////////////////////////// // Label + AddressTextField /////////////////////////////////////////////////////////////////////////////////////////// @@ -2181,11 +2222,13 @@ public class FormBuilder { Label label = new AutoTooltipLabel(Res.getBaseCurrencyCode()); label.getStyleClass().add("input-label"); + HBox.setMargin(label, new Insets(0, 8, 0, 0)); HBox box = new HBox(); HBox.setHgrow(input, Priority.ALWAYS); input.setMaxWidth(Double.MAX_VALUE); - box.getStyleClass().add("input-with-border"); + box.setAlignment(Pos.CENTER_LEFT); + box.getStyleClass().add("offer-input"); box.getChildren().addAll(input, label); return new Tuple3<>(box, input, label); } @@ -2197,11 +2240,13 @@ public class FormBuilder { Label label = new AutoTooltipLabel(Res.getBaseCurrencyCode()); label.getStyleClass().add("input-label"); + HBox.setMargin(label, new Insets(0, 8, 0, 0)); HBox box = new HBox(); HBox.setHgrow(infoInputTextField, Priority.ALWAYS); infoInputTextField.setMaxWidth(Double.MAX_VALUE); - box.getStyleClass().add("input-with-border"); + box.setAlignment(Pos.CENTER_LEFT); + box.getStyleClass().add("offer-input"); box.getChildren().addAll(infoInputTextField, label); return new Tuple3<>(box, infoInputTextField, label); } @@ -2444,6 +2489,7 @@ public class FormBuilder { if (groupStyle != null) titledGroupBg.getStyleClass().add(groupStyle); TableView tableView = new TableView<>(); + GUIUtil.applyTableStyle(tableView); GridPane.setRowIndex(tableView, rowIndex); GridPane.setMargin(tableView, new Insets(top + 30, -10, 5, -10)); gridPane.getChildren().add(tableView); diff --git a/desktop/src/main/java/haveno/desktop/util/GUIUtil.java b/desktop/src/main/java/haveno/desktop/util/GUIUtil.java index e825e29e6e..3097780c1a 100644 --- a/desktop/src/main/java/haveno/desktop/util/GUIUtil.java +++ b/desktop/src/main/java/haveno/desktop/util/GUIUtil.java @@ -25,7 +25,10 @@ import com.googlecode.jcsv.CSVStrategy; import com.googlecode.jcsv.writer.CSVEntryConverter; import com.googlecode.jcsv.writer.CSVWriter; import com.googlecode.jcsv.writer.internal.CSVWriterBuilder; + +import de.jensd.fx.fontawesome.AwesomeIcon; import de.jensd.fx.glyphs.materialdesignicons.MaterialDesignIcon; +import de.jensd.fx.glyphs.materialdesignicons.MaterialDesignIconView; import haveno.common.UserThread; import haveno.common.config.Config; import haveno.common.file.CorruptedStorageFileHandler; @@ -64,9 +67,17 @@ import haveno.desktop.main.account.AccountView; import haveno.desktop.main.account.content.traditionalaccounts.TraditionalAccountsView; import haveno.desktop.main.overlays.popups.Popup; import haveno.network.p2p.P2PService; +import javafx.application.Platform; +import javafx.beans.binding.Bindings; +import javafx.beans.value.ChangeListener; import javafx.collections.FXCollections; +import javafx.collections.ListChangeListener; +import javafx.collections.ObservableList; import javafx.geometry.HPos; +import javafx.geometry.Insets; import javafx.geometry.Orientation; +import javafx.geometry.Pos; +import javafx.scene.Cursor; import javafx.scene.Node; import javafx.scene.Scene; import javafx.scene.control.ComboBox; @@ -76,14 +87,21 @@ import javafx.scene.control.ListCell; import javafx.scene.control.ListView; import javafx.scene.control.ScrollBar; import javafx.scene.control.ScrollPane; +import javafx.scene.control.TableCell; +import javafx.scene.control.TableColumn; import javafx.scene.control.TableView; import javafx.scene.control.TextArea; +import javafx.scene.control.TextField; import javafx.scene.control.Tooltip; +import javafx.scene.image.ImageView; import javafx.scene.layout.AnchorPane; import javafx.scene.layout.ColumnConstraints; import javafx.scene.layout.GridPane; import javafx.scene.layout.HBox; import javafx.scene.layout.Priority; +import javafx.scene.layout.Region; +import javafx.scene.layout.StackPane; +import javafx.scene.shape.Rectangle; import javafx.stage.DirectoryChooser; import javafx.stage.FileChooser; import javafx.stage.Modality; @@ -131,7 +149,9 @@ public class GUIUtil { public final static int NUM_DECIMALS_PRECISE = 7; public final static int AMOUNT_DECIMALS_WITH_ZEROS = 3; public final static int AMOUNT_DECIMALS = 4; + public static final double NUM_OFFERS_TRANSLATE_X = -13.0; + public static final boolean disablePaymentUriLabel = true; // universally disable payment uri labels, allowing bigger xmr logo overlays private static Preferences preferences; public static void setPreferences(Preferences preferences) { @@ -304,30 +324,42 @@ public class GUIUtil { HBox box = new HBox(); box.setSpacing(20); - Label currencyType = new AutoTooltipLabel( - CurrencyUtil.isTraditionalCurrency(code) ? Res.get("shared.traditional") : Res.get("shared.crypto")); + box.setAlignment(Pos.CENTER_LEFT); + Label label1 = new AutoTooltipLabel(getCurrencyType(code)); + label1.getStyleClass().add("currency-label-small"); + Label label2 = new AutoTooltipLabel(CurrencyUtil.isCryptoCurrency(code) ? item.tradeCurrency.getNameAndCode() : code); + label2.getStyleClass().add("currency-label"); + Label label3 = new AutoTooltipLabel(CurrencyUtil.isCryptoCurrency(code) ? "" : item.tradeCurrency.getName()); + if (!CurrencyUtil.isCryptoCurrency(code)) label3.getStyleClass().add("currency-label"); + Label label4 = new AutoTooltipLabel(); - currencyType.getStyleClass().add("currency-label-small"); - Label currency = new AutoTooltipLabel(code); - currency.getStyleClass().add("currency-label"); - Label offers = new AutoTooltipLabel(item.tradeCurrency.getName()); - offers.getStyleClass().add("currency-label"); - - box.getChildren().addAll(currencyType, currency, offers); + box.getChildren().addAll(label1, label2, label3); + if (!CurrencyUtil.isCryptoCurrency(code)) box.getChildren().add(label4); switch (code) { case GUIUtil.SHOW_ALL_FLAG: - currencyType.setText(Res.get("shared.all")); - currency.setText(Res.get("list.currency.showAll")); + label1.setText(Res.get("shared.all")); + label2.setText(Res.get("list.currency.showAll")); break; case GUIUtil.EDIT_FLAG: - currencyType.setText(Res.get("shared.edit")); - currency.setText(Res.get("list.currency.editList")); + label1.setText(Res.get("shared.edit")); + label2.setText(Res.get("list.currency.editList")); break; default: - if (preferences.isSortMarketCurrenciesNumerically()) { - offers.setText(offers.getText() + " (" + item.numTrades + " " + - (item.numTrades == 1 ? postFixSingle : postFixMulti) + ")"); + + // use icons for crypto + if (CurrencyUtil.isCryptoCurrency(code)) { + label1.setText(""); + StackPane iconWrapper = new StackPane(getCurrencyIcon(code)); // TODO: icon must be wrapped in StackPane for reliable rendering on linux + label1.setGraphic(iconWrapper); + } + + if (preferences.isSortMarketCurrenciesNumerically() && item.numTrades > 0) { + boolean isCrypto = CurrencyUtil.isCryptoCurrency(code); + Label offersTarget = isCrypto ? label3 : label4; + HBox.setMargin(offersTarget, new Insets(0, 0, 0, NUM_OFFERS_TRANSLATE_X)); + offersTarget.getStyleClass().add("offer-label"); + offersTarget.setText(item.numTrades + " " + (item.numTrades == 1 ? postFixSingle : postFixMulti)); } } @@ -430,33 +462,47 @@ public class GUIUtil { HBox box = new HBox(); box.setSpacing(20); - Label currencyType = new AutoTooltipLabel( - CurrencyUtil.isTraditionalCurrency(item.getCode()) ? Res.get("shared.traditional") : Res.get("shared.crypto")); + box.setAlignment(Pos.CENTER_LEFT); - currencyType.getStyleClass().add("currency-label-small"); - Label currency = new AutoTooltipLabel(item.getCode()); - currency.getStyleClass().add("currency-label"); - Label offers = new AutoTooltipLabel(item.getName()); - offers.getStyleClass().add("currency-label"); - - box.getChildren().addAll(currencyType, currency, offers); + Label label1 = new AutoTooltipLabel(getCurrencyType(item.getCode())); + label1.getStyleClass().add("currency-label-small"); + Label label2 = new AutoTooltipLabel(CurrencyUtil.isCryptoCurrency(code) ? item.getNameAndCode() : code); + label2.getStyleClass().add("currency-label"); + Label label3 = new AutoTooltipLabel(CurrencyUtil.isCryptoCurrency(code) ? "" : item.getName()); + if (!CurrencyUtil.isCryptoCurrency(code)) label3.getStyleClass().add("currency-label"); + Label label4 = new AutoTooltipLabel(); Optional offerCountOptional = Optional.ofNullable(offerCounts.get(code)); switch (code) { case GUIUtil.SHOW_ALL_FLAG: - currencyType.setText(Res.get("shared.all")); - currency.setText(Res.get("list.currency.showAll")); + label1.setText(Res.get("shared.all")); + label2.setText(Res.get("list.currency.showAll")); break; case GUIUtil.EDIT_FLAG: - currencyType.setText(Res.get("shared.edit")); - currency.setText(Res.get("list.currency.editList")); + label1.setText(Res.get("shared.edit")); + label2.setText(Res.get("list.currency.editList")); break; default: - offerCountOptional.ifPresent(numOffer -> offers.setText(offers.getText() + " (" + numOffer + " " + - (numOffer == 1 ? postFixSingle : postFixMulti) + ")")); + + // use icons for crypto + if (CurrencyUtil.isCryptoCurrency(item.getCode())) { + label1.setText(""); + label1.setGraphic(getCurrencyIcon(item.getCode())); + } + + boolean isCrypto = CurrencyUtil.isCryptoCurrency(code); + Label offersTarget = isCrypto ? label3 : label4; + offerCountOptional.ifPresent(numOffers -> { + HBox.setMargin(offersTarget, new Insets(0, 0, 0, NUM_OFFERS_TRANSLATE_X)); + offersTarget.getStyleClass().add("offer-label"); + offersTarget.setText(numOffers + " " + (numOffers == 1 ? postFixSingle : postFixMulti)); + }); } + box.getChildren().addAll(label1, label2, label3); + if (!CurrencyUtil.isCryptoCurrency(code)) box.getChildren().add(label4); + setGraphic(box); } else { @@ -466,6 +512,55 @@ public class GUIUtil { }; } + public static Callback, ListCell> getTradeCurrencyCellFactoryNameAndCode() { + return p -> new ListCell<>() { + @Override + protected void updateItem(TradeCurrency item, boolean empty) { + super.updateItem(item, empty); + + if (item != null && !empty) { + + HBox box = new HBox(); + box.setSpacing(10); + + Label label1 = new AutoTooltipLabel(getCurrencyType(item.getCode())); + label1.getStyleClass().add("currency-label-small"); + Label label2 = new AutoTooltipLabel(item.getNameAndCode()); + label2.getStyleClass().add("currency-label"); + + // use icons for crypto + if (CurrencyUtil.isCryptoCurrency(item.getCode())) { + label1.setText(""); + label1.setGraphic(getCurrencyIcon(item.getCode())); + } + + box.getChildren().addAll(label1, label2); + + setGraphic(box); + + } else { + setGraphic(null); + } + } + }; + } + + private static String getCurrencyType(String code) { + if (CurrencyUtil.isFiatCurrency(code)) { + return Res.get("shared.fiat"); + } else if (CurrencyUtil.isTraditionalCurrency(code)) { + return Res.get("shared.traditional"); + } else if (CurrencyUtil.isCryptoCurrency(code)) { + return Res.get("shared.crypto"); + } else { + return ""; + } + } + + private static String getCurrencyType(PaymentMethod method) { + return method.isTraditional() ? Res.get("shared.traditional") : Res.get("shared.crypto"); + } + public static ListCell getPaymentMethodButtonCell() { return new ListCell<>() { @@ -501,9 +596,7 @@ public class GUIUtil { HBox box = new HBox(); box.setSpacing(20); - Label paymentType = new AutoTooltipLabel( - method.isTraditional() ? Res.get("shared.traditional") : Res.get("shared.crypto")); - + Label paymentType = new AutoTooltipLabel(getCurrencyType(method)); paymentType.getStyleClass().add("currency-label-small"); Label paymentMethod = new AutoTooltipLabel(Res.get(id)); paymentMethod.getStyleClass().add("currency-label"); @@ -673,7 +766,7 @@ public class GUIUtil { String currencyName = Config.baseCurrencyNetwork().getCurrencyName(); new Popup().information(Res.get("payment.fasterPayments.newRequirements.info", currencyName)) .width(900) - .actionButtonTextWithGoTo("navigation.account") + .actionButtonTextWithGoTo("mainView.menu.account") .onAction(() -> { navigation.setReturnPath(navigation.getCurrentPath()); navigation.navigateTo(MainView.class, AccountView.class, TraditionalAccountsView.class); @@ -683,10 +776,10 @@ public class GUIUtil { } public static String getMoneroURI(String address, BigInteger amount, String label) { - return MoneroUtils.getPaymentUri(new MoneroTxConfig() - .setAddress(address) - .setAmount(amount) - .setNote(label)); + MoneroTxConfig txConfig = new MoneroTxConfig().setAddress(address); + if (amount != null) txConfig.setAmount(amount); + if (label != null && !label.isEmpty() && !disablePaymentUriLabel) txConfig.setNote(label); + return MoneroUtils.getPaymentUri(txConfig); } public static boolean isBootstrappedOrShowPopup(P2PService p2PService) { @@ -742,7 +835,7 @@ public class GUIUtil { if (user.currentPaymentAccountProperty().get() == null) { new Popup().headLine(Res.get("popup.warning.noTradingAccountSetup.headline")) .instruction(Res.get("popup.warning.noTradingAccountSetup.msg")) - .actionButtonTextWithGoTo("navigation.account") + .actionButtonTextWithGoTo("mainView.menu.account") .onAction(() -> { navigation.setReturnPath(navigation.getCurrentPath()); navigation.navigateTo(MainView.class, AccountView.class, TraditionalAccountsView.class); @@ -1033,4 +1126,269 @@ public class GUIUtil { columnConstraints2.setHgrow(Priority.ALWAYS); gridPane.getColumnConstraints().addAll(columnConstraints1, columnConstraints2); } + + public static void applyFilledStyle(TextField textField) { + textField.textProperty().addListener((observable, oldValue, newValue) -> { + updateFilledStyle(textField); + }); + } + + private static void updateFilledStyle(TextField textField) { + if (textField.getText() != null && !textField.getText().isEmpty()) { + if (!textField.getStyleClass().contains("filled")) { + textField.getStyleClass().add("filled"); + } + } else { + textField.getStyleClass().remove("filled"); + } + } + + public static void applyFilledStyle(ComboBox comboBox) { + comboBox.valueProperty().addListener((observable, oldValue, newValue) -> { + updateFilledStyle(comboBox); + }); + } + + private static void updateFilledStyle(ComboBox comboBox) { + if (comboBox.getValue() != null) { + if (!comboBox.getStyleClass().contains("filled")) { + comboBox.getStyleClass().add("filled"); + } + } else { + comboBox.getStyleClass().remove("filled"); + } + } + + public static void applyTableStyle(TableView tableView) { + applyTableStyle(tableView, true); + } + + public static void applyTableStyle(TableView tableView, boolean applyRoundedArc) { + if (applyRoundedArc) applyRoundedArc(tableView); + addSpacerColumns(tableView); + applyEdgeColumnStyleClasses(tableView); + } + + private static void applyRoundedArc(TableView tableView) { + Rectangle clip = new Rectangle(); + clip.setArcWidth(Layout.ROUNDED_ARC); + clip.setArcHeight(Layout.ROUNDED_ARC); + tableView.setClip(clip); + tableView.layoutBoundsProperty().addListener((obs, oldVal, newVal) -> { + clip.setWidth(newVal.getWidth()); + clip.setHeight(newVal.getHeight()); + }); + } + + private static void addSpacerColumns(TableView tableView) { + TableColumn leftSpacer = new TableColumn<>(); + TableColumn rightSpacer = new TableColumn<>(); + + configureSpacerColumn(leftSpacer); + configureSpacerColumn(rightSpacer); + + tableView.getColumns().add(0, leftSpacer); + tableView.getColumns().add(rightSpacer); + } + + private static void configureSpacerColumn(TableColumn column) { + column.setPrefWidth(15); + column.setMaxWidth(15); + column.setMinWidth(15); + column.setReorderable(false); + column.setResizable(false); + column.setSortable(false); + column.setCellFactory(col -> new TableCell<>()); // empty cell + } + + private static void applyEdgeColumnStyleClasses(TableView tableView) { + ListChangeListener> columnListener = change -> { + UserThread.execute(() -> { + updateEdgeColumnStyleClasses(tableView); + }); + }; + + tableView.getColumns().addListener(columnListener); + tableView.skinProperty().addListener((obs, oldSkin, newSkin) -> { + if (newSkin != null) { + UserThread.execute(() -> { + updateEdgeColumnStyleClasses(tableView); + }); + } + }); + + // react to size changes + ChangeListener sizeListener = (obs, oldVal, newVal) -> updateEdgeColumnStyleClasses(tableView); + tableView.heightProperty().addListener(sizeListener); + tableView.widthProperty().addListener(sizeListener); + + updateEdgeColumnStyleClasses(tableView); + } + + private static void updateEdgeColumnStyleClasses(TableView tableView) { + ObservableList> columns = tableView.getColumns(); + + // find columns with "first-column" and "last-column" classes + TableColumn firstCol = null; + TableColumn lastCol = null; + for (TableColumn col : columns) { + if (col.getStyleClass().contains("first-column")) { + firstCol = col; + } else if (col.getStyleClass().contains("last-column")) { + lastCol = col; + } + } + + // handle if columns do not exist + if (firstCol == null || lastCol == null) { + if (firstCol != null) throw new IllegalStateException("Missing column with 'last-column'"); + if (lastCol != null) throw new IllegalStateException("Missing column with 'first-column'"); + + // remove all classes + for (TableColumn col : columns) { + col.getStyleClass().removeAll("first-column", "last-column"); + } + + // apply first and last classes + if (!columns.isEmpty()) { + TableColumn first = columns.get(0); + TableColumn last = columns.get(columns.size() - 1); + + if (!first.getStyleClass().contains("first-column")) { + first.getStyleClass().add("first-column"); + } + + if (!last.getStyleClass().contains("last-column")) { + last.getStyleClass().add("last-column"); + } + } + } else { + + // done if correct order + if (columns.get(0) == firstCol && columns.get(columns.size() - 1) == lastCol) { + return; + } + + // set first and last columns + if (columns.get(0) != firstCol) { + columns.remove(firstCol); + columns.add(0, firstCol); + } + if (columns.get(columns.size() - 1) != lastCol) { + columns.remove(lastCol); + columns.add(firstCol == lastCol ? columns.size() - 1 : columns.size(), lastCol); + } + } + } + + public static ImageView getCurrencyIcon(String currencyCode) { + return getCurrencyIcon(currencyCode, 24); + } + + public static ImageView getCurrencyIcon(String currencyCode, double size) { + if (currencyCode == null) return null; + ImageView iconView = new ImageView(); + iconView.setFitWidth(size); + iconView.setPreserveRatio(true); + iconView.setSmooth(true); + iconView.setCache(true); + iconView.setId(getImageId(currencyCode)); + return iconView; + } + + public static StackPane getCurrencyIconWithBorder(String currencyCode) { + return getCurrencyIconWithBorder(currencyCode, 25, 1); + } + + public static StackPane getCurrencyIconWithBorder(String currencyCode, double size, double borderWidth) { + if (currencyCode == null) return null; + + ImageView icon = getCurrencyIcon(currencyCode, size); + icon.setFitWidth(size - 2 * borderWidth); + icon.setFitHeight(size - 2 * borderWidth); + icon.setPreserveRatio(true); + icon.setSmooth(true); + icon.setCache(true); + + StackPane circleWrapper = new StackPane(icon); + circleWrapper.setPrefSize(size, size); + circleWrapper.setMaxSize(size, size); + circleWrapper.setMinSize(size, size); + + circleWrapper.setStyle( + "-fx-background-color: white;" + + "-fx-background-radius: 50%;" + + "-fx-border-radius: 50%;" + + "-fx-border-color: white;" + + "-fx-border-width: " + borderWidth + "px;" + ); + + StackPane.setAlignment(icon, Pos.CENTER); + + return circleWrapper; + } + + private static String getImageId(String currencyCode) { + if (currencyCode == null) return null; + return "image-" + currencyCode.toLowerCase() + "-logo"; + } + + public static void adjustHeightAutomatically(TextArea textArea) { + adjustHeightAutomatically(textArea, null); + } + + public static void adjustHeightAutomatically(TextArea textArea, Double maxHeight) { + textArea.sceneProperty().addListener((o, oldScene, newScene) -> { + if (newScene != null) { + // avoid javafx css warning + CssTheme.loadSceneStyles(newScene, CssTheme.getCurrentTheme(), false); + textArea.applyCss(); + var text = textArea.lookup(".text"); + + textArea.prefHeightProperty().bind(Bindings.createDoubleBinding(() -> { + Insets padding = textArea.getInsets(); + double topBottomPadding = padding.getTop() + padding.getBottom(); + double prefHeight = textArea.getFont().getSize() + text.getBoundsInLocal().getHeight() + topBottomPadding; + return maxHeight == null ? prefHeight : Math.min(prefHeight, maxHeight); + }, text.boundsInLocalProperty())); + + text.boundsInLocalProperty().addListener((observableBoundsAfter, boundsBefore, boundsAfter) -> { + Platform.runLater(() -> textArea.requestLayout()); + }); + } + }); + } + + public static Label getLockLabel() { + Label lockLabel = FormBuilder.getIcon(AwesomeIcon.LOCK, "16px"); + lockLabel.setStyle(lockLabel.getStyle() + " -fx-text-fill: white;"); + return lockLabel; + } + + public static MaterialDesignIconView getCopyIcon() { + return new MaterialDesignIconView(MaterialDesignIcon.CONTENT_COPY, "1.35em"); + } + + + public static Tuple2 getSmallXmrQrCodePane() { + return getXmrQrCodePane(150, disablePaymentUriLabel ? 32 : 28, 2); + } + + public static Tuple2 getBigXmrQrCodePane() { + return getXmrQrCodePane(250, disablePaymentUriLabel ? 47 : 45, 3); + } + + private static Tuple2 getXmrQrCodePane(int qrCodeSize, int logoSize, int logoBorderWidth) { + ImageView qrCodeImageView = new ImageView(); + qrCodeImageView.setFitHeight(qrCodeSize); + qrCodeImageView.setFitWidth(qrCodeSize); + qrCodeImageView.getStyleClass().add("qr-code"); + + StackPane xmrLogo = GUIUtil.getCurrencyIconWithBorder(Res.getBaseCurrencyCode(), logoSize, logoBorderWidth); + StackPane qrCodePane = new StackPane(qrCodeImageView, xmrLogo); + qrCodePane.setCursor(Cursor.HAND); + qrCodePane.setMaxSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE); + + return new Tuple2<>(qrCodePane, qrCodeImageView); + } } diff --git a/desktop/src/main/java/haveno/desktop/util/Layout.java b/desktop/src/main/java/haveno/desktop/util/Layout.java index 975bb40df6..f3c705f947 100644 --- a/desktop/src/main/java/haveno/desktop/util/Layout.java +++ b/desktop/src/main/java/haveno/desktop/util/Layout.java @@ -25,7 +25,7 @@ public class Layout { public static final double FIRST_ROW_DISTANCE = 20d; public static final double COMPACT_FIRST_ROW_DISTANCE = 10d; public static final double TWICE_FIRST_ROW_DISTANCE = 20d * 2; - public static final double FLOATING_LABEL_DISTANCE = 18d; + public static final double FLOATING_LABEL_DISTANCE = 23d; public static final double GROUP_DISTANCE = 40d; public static final double COMPACT_GROUP_DISTANCE = 30d; public static final double GROUP_DISTANCE_WITHOUT_SEPARATOR = 20d; @@ -33,6 +33,7 @@ public class Layout { public static final double COMPACT_FIRST_ROW_AND_GROUP_DISTANCE = COMPACT_GROUP_DISTANCE + FIRST_ROW_DISTANCE; public static final double COMPACT_FIRST_ROW_AND_COMPACT_GROUP_DISTANCE = COMPACT_GROUP_DISTANCE + COMPACT_FIRST_ROW_DISTANCE; public static final double COMPACT_FIRST_ROW_AND_GROUP_DISTANCE_WITHOUT_SEPARATOR = GROUP_DISTANCE_WITHOUT_SEPARATOR + COMPACT_FIRST_ROW_DISTANCE; + public static final double TWICE_FIRST_ROW_AND_COMPACT_GROUP_DISTANCE = COMPACT_GROUP_DISTANCE + TWICE_FIRST_ROW_DISTANCE; public static final double TWICE_FIRST_ROW_AND_GROUP_DISTANCE = GROUP_DISTANCE + TWICE_FIRST_ROW_DISTANCE; public static final double PADDING_WINDOW = 20d; public static double PADDING = 10d; @@ -40,4 +41,8 @@ public class Layout { public static final double SPACING_V_BOX = 5d; public static final double GRID_GAP = 5d; public static final double LIST_ROW_HEIGHT = 34; + public static final double ROUNDED_ARC = 20; + public static final double FLOATING_ICON_Y = 9; // adjust when .jfx-text-field padding is changed for right icons + public static final double DETAILS_WINDOW_WIDTH = 950; + public static final double DETAILS_WINDOW_EXTRA_INFO_MAX_HEIGHT = 150; } diff --git a/desktop/src/main/java/haveno/desktop/util/Transitions.java b/desktop/src/main/java/haveno/desktop/util/Transitions.java index 300524ccce..691d9d6243 100644 --- a/desktop/src/main/java/haveno/desktop/util/Transitions.java +++ b/desktop/src/main/java/haveno/desktop/util/Transitions.java @@ -37,7 +37,7 @@ import javafx.util.Duration; @Singleton public class Transitions { - public final static int DEFAULT_DURATION = 600; + public final static int DEFAULT_DURATION = 400; private final Preferences preferences; private Timeline removeEffectTimeLine; @@ -96,7 +96,7 @@ public class Transitions { // Blur public void blur(Node node) { - blur(node, DEFAULT_DURATION, -0.1, false, 15); + blur(node, DEFAULT_DURATION, -0.1, false, 45); } public void blur(Node node, int duration, double brightness, boolean removeNode, double blurRadius) { @@ -111,7 +111,7 @@ public class Transitions { ColorAdjust darken = new ColorAdjust(); darken.setBrightness(0.0); blur.setInput(darken); - KeyValue kv2 = new KeyValue(darken.brightnessProperty(), brightness); + KeyValue kv2 = new KeyValue(darken.brightnessProperty(), CssTheme.isDarkTheme() ? brightness * -0.13 : brightness); KeyFrame kf2 = new KeyFrame(Duration.millis(getDuration(duration)), kv2); timeline.getKeyFrames().addAll(kf1, kf2); node.setEffect(blur); diff --git a/desktop/src/main/resources/images/account.png b/desktop/src/main/resources/images/account.png new file mode 100644 index 0000000000000000000000000000000000000000..53b40584653823d4d648a6e2e08adfe8290be793 GIT binary patch literal 1014 zcmV-}~OKq?H~Nfr#s1gNRS2Eh1iqzweB> zWjBF{cjbpY8~!d@Jb_S_D3OS&mfnP1eV0`*FjkxaK-`vdBHHIvC#-r*Yg!mQM`MVB^i74wn9Kregru5YdJEB>keU+jtXl+;UwPy^TkD&-wQ#{vN2w zYXJBzhHi)`b&2pIh&Wec^W?=#krn5o=JV{EmFM3Wsh=OeTlA}l6#&R|k@Yu|oMR;H z{38)fGY=0o!FEbSk9ywosIcfEM-0yq9_Am|K0(f1f8>a3>~iEu#%;B84^-@Dz!ed# z#N4GIbMc`3&~jN)M-;w=E&tKk_D zJ>=Hqd^;qmwKvbWAfin*F(M8DVCyYbK{Fzvo#uG&C^?XNe$A0NU>Dn3048k2d;nP2ypN(&Cz?(xL?x@u~+;vc@8g2V!1F62c}qXwQ#BZa&Ke zfC!*H5hX=RF7jd;ToKWgnp?&T!1MJ|{gKVFxr@QuiqNfmQN!^nI#c6ci74s6O5MZJ z#j5ONzDU&T`!A9f)1p{&E26dI8wi6)>Uu(kyi2i>Joc#Zm2O%|nJlcikz6rA#_W$) zOIJ(2-~%34nE%IVP+hLofa}XK2k4>8=73$hh;%8s*bZ$IS| zSW8YK8WoluP$gRJOf`8Wx7>`XldCQ0XTkkG`ABHCre$G&r47gZS*v! zzDJKlT0N9Tt$WPG|MQyNr~&iHDt%V7u1Yi)da}*hT0L5$;A_#v0taDLLD`z8Cu(Q* zT)h_iM#XWjE+_N*pNC%8I3k9J-WpRo?%;lju@8J9SuPd6JyyAdM4xMX$o0mgvF6Y? z)FA8@#o%ie&0Wvj*{vPd9Y496O9b$XhOG0E>kp4->UCb#qKz5>`1ogidK-g3W)J;Y zG)cNE%KEEzugE^3c%7lTujtlJ kkG+6;Yy53``U3#|0Lz!Kp5vt10{{R307*qoM6N<$g5vPx?*IS* literal 0 HcmV?d00001 diff --git a/desktop/src/main/resources/images/alert_round.png b/desktop/src/main/resources/images/alert_round.png index c5c182ed94f0f197bb3fdd88d4147ea02fdbd943..4ba0578457abd6197009d0e8fa8363d568f12710 100644 GIT binary patch literal 4964 zcmV-q6PxUbP)q zZrT`VY#<2G#(y+vlR8O}6m3($rjevY55P{+7_|`~LH|h6xImqtK@2C3m^4QdSdo0t zwrpA?O?i3jl3ecjj+yT(W)Es9Q#R!~1MI%pc{B6PcOSE8ZvrR*s5MmYJUuuz6uSI@ zt+4tZD@50=Xf1!SvGEIKU)mSfBmVUabeX@@Mkw{l; zOyYj_fgH*^(3l34{j`2N_Up|4(dk36)N{#b%6V5;G$G^bg{R(({om_vPN=@J1 zfJdL~N`BxIX>@L%>Q6g62mh=mKHj1$wI6wQYAPCTZQbA-=bcrs^29h*F|SmB@BY#1 zYOcYYw#gQqGnoVLXt8T!WMY1PcpZm2`DlYW^w_*r54&zn~k z7A(3&G1vlQOALzAc)plhb@E+_&v69*uI{yTVV&^2LScS*>@$`W+YD=BZ^x$X(S(Yw z=C~z0=S4p_7m0lFF~9JQ>sJ=3eX=FjU*|W}pLfN6Ux@XWi~ig;A?z`~kV@T9lJFxn zkt$f8G(`Q1d2r?11#t(4n;NnBNPjeMQ*LU-mdVBt68i?94cNU0xgbzhS^_5%l=StPg zVc8Of<<+l-4#*E-@VV7j)aQG;j(R=G>#$XQLRB)Ep+IEo+O_#y`vUvif~hl>0I5;< zc*t_XLRLOeqAL8N)x_NvSc$lQp#M}5@$w=Px5D7oh$LiA*t71oz&=ahFLMn^S2b`_ zg-T_u48HCwKx|8R=F$arle_bqkGPaLEKJW>zNyGe(eSTA~r@L_}6%r>8_==)lKsv`1?Nfv%Mokh)3+iGRHFOeZFcS7g~Un|xA*N$&X z#B@5{sRQrBRWZi`!C8o`v!d%sBo6S`zgF^JY$@Rm*|X&QWEve3#0M3NYa~+Oa(VM1 zAly~bf#8rM$qo1Y+5#&ylsd@@R6vx8$`nXsy17>gR54~UEbP}91@5Jr9B{!rkB;JFa^bqLITBA2UN3Ur4yo$ej) zjwIx_h2;H! z@0Q9MM}L5bCO~+s;>AwCBY&=JH#eEdO&^@{MBqc<3)i1sy)O$lt-d^F_jn+k?$`>$ z1cbS$mjAw|T+UHT8lLFw?Ljj2ZV@@|kbI{r5&8AuxjEltCg*+d=w$%F12@O( z{j(ohTRCQ(TC6MaRs(#H1*dC~+GEy_Io3VS4GdhxMot*w=c`rc7ss>bE0vMM(-rV- z6$q-ru!hO@D) zIs=x;Bg-$HyaE9Ad4HAPxYMrng4nRDnhK}u)%`_+3aeK^-M)1gA7nRcE7Wcpz70gz)9EP~^9LFrE%E~& zc;@uxX8B>jst*1&oj&0SpQsA}WajStX5A=R0KL8FjOxU-AbD5a<|y;s{z5oA-E=aq zGADHrHRdgb^}DrTFd#k`FuxeES`2}Fcf4Gy^U&)|L=X&!at|`(5D5+tuqK56kyvf^ zfgCFBfy3oY5KRLSbatky4j=GXu3}JHzyp>f&6P}0d4c3e(Nc_^g|1SmFb(KEnV{Eb z7Wj_&(~T2f*1okjXD^d1NeNJ=8c#aW{G0-C?;h5_p5=CaHZYltMoTGHDsFYnZ@#Mq zBqqWh1wp~j=c{)#g=Nn`tmV!OOeQ&nLzKq6X9RRgm=^Yfm_ z%A@&w_2FHB?I4#W%iAU7vzlb`S3pXipz5e>OnNDe`0gN2__ik~klmDjae-AhJ9Xw7jF zOq8x^ITR94h0H0R=&6wTC#8*z<#uUn_dV_tnXyAI@gEZC00h6(z%RTY_DVK??o~9m zSU(J?(!fAci|o_DO#$=O2AK7Texlm|xvd2+WR9d<_{SA!Q0f5Gv1WS*0;^fixU&EO4(d5 z4n8Q5B{F!lurhk#BxbZ^CT|NN|JCq2yzOu5zNg`XPQHD0X=WO5-BfCy5qYl$A7G*H z6pO_tzY6&Etgci>w^1@>qje^n>Fjiun*cuZIsokF)T4u$k6O}n7YZw$$k~vYd@)n# z?CiEIv8F-=2+|B3G(-#RV{u|`F5t{$DumqkIsob()q{M)Qsgcm(8er=kOfV05J38N zK&hG0(TFHy45TA8dQnA!qNQU9IkQIq_5Q!5htAk_TRoMcPi9ezbZAFsrbZD_s8n2r zbOhjQCu19fL6JM zg)j&?8?pstMtj4s+v?8L%EHMj0Dv#77w(jLtbi^E4Fj_77`ZUJfKc6G868C~6f6de z@v_L|LzzsXiOsRS000*5Dhp z@ROmz%wnOi^3_I&Qx~0o85o#u0x0}#e)Im8Fx>@2ITasoZz_w|j0B)5i)YeVp2jq$4xI3M5IvZ0P{&-!fyX(ef(2OSE@F=DfXe?Gr6$;)g5bhSG>h`f}}x zk!cpq)b}SQ-0^}F^&Oqi5x*No^*~XN1`=A>HO;*ZF7nI3fJI0fdvfc=P$eaWQ0uLL zs90mJR#K^8cy3O=5#;uSj74AuC!DU#3(+ogCd;atauDe=Y=#Il2vFLPbcrC!s0YnK zz-)wwt)Xaq7)C@Hkq8qd3?pH{#6X2bHZ`zn1ZyICHB@TDL?Q(M)#Yq9xCbzMLF}@~ zv^a2K|NdxB$2*n7y{u%|72as1;21*!W>pfBHGp#=R9-1lW_ANZA^UB6T4X$} zqBcY#F)1z5!%9-2P(Q#OS}1Brd?T{S#7mlKfkd)!oXuP==e-_MyCP!|nVXo1+9)&X zn2ZJ{1w26@n@2iOD6n9e1j|NY#ev@NgGf76cQx_C>iEOO>|xX7fOz^iPu1C_i|Y#6N^yNiqW`I zaumQ307(I@lgOOFc>!!*>Gsto_bczr8c|wCnHBkqcb6U}}+TaC_MptjVBt>q|@#;BtBCdxVk*itwT17M;n&H#6wT)ptk7E&Jv+)OIf0b_>Q!6XZg zGSDwT9&DZwa-X*d&9|lDtx!pw&Sc_BVreE!68L%{3<`!vJg;s@br4y-$N{oxp zb%2b)BBj(>5I<)+ycNrxdfHVAL**Ic`oa+-P|RG@;B_TZ5Mydbfo&b}L_Yl{bD}Y? zZEiEy{OCn>hVG7|CSnR&WL-pRY*R)-#9pDeVIzHXtsl6xc?D8eBC6}32*CtI><~%2 zuL!`Y-vlRgbC1Tn`uMHwmbOeb;3T$nQk!YhF7->d{fCX6M5vkGD92{hagiP7+k-6p zCXs0EYal;v1FNDTT%RSb97&2Gt^lpD^K5nYlp_)Lw4+0&Fc9WIBoVOb_YI|NE$?Vk z!O*A#-1{br)6P7+X;ebN{Vg-SwI30oM7QhGFbNJSP%jh|fs9fxI?{+X?4`BTvRSPt zD21RLgI0Cp-Xs&SIpBqqMsIK8+fBqLR^}Hx$jRRn63z7c;cOe6yESzAZ=(t2U0X~o=ZFFLe|4m-*nSBz1BBK992aLhcEtZ+BV zQ;nQ<#}$lYE4ERhArW5xvDQXADWeLUTgqIntP~xbDVF`MmdFo5{BATj?QNn)qW4xvhcR?noG^$7PQQ zaPJq_3kz)_RXvPJ4Ip08@jlWR6GyI5tR{pI6`Kw~o)*IMmm=~iWYA3d-AogJFO zK38a%3APS)6|4g%DKl)jgkBU)F9)e=VKa-@j+)yk)E>Puupc6ajL4`B_B5DsS?b#^SC{`XP4{seUMAl+Zqb z^bp7uNI?iMlhApI-~yc5=3uTKhnd}o_PYNwut*`Dj=RS60(u_{#szXv3kHlxTwBp6 z@eHIWz`8NA!Aflz@D_}!Iw}kdNE`?mjzr;b(gF7fs~#IPI^cdLvR49;y2y%b31QhG z$r|Opq5@gtj%x9>kk}J4uRCc&!xMgtr3dha22|<28WSLhKGe z4cE(r15xAqF^TAe)*NT!#>L?zjC3kT#aZCmqQBFu&?rEdpp{_{4wcEMs&cfaL*;4L zt-0}%H<&{(3z)qk@c%+)w>>OGTezET>}RWnDw~1oHlV7FrkX9OAVmj~$%1Dahc#1x iGz)!ATD`#r;Qs(RPbZ09nW$?30000gtZ# z_=a$=mE)3;31Hlh&?%yBIA_79>ak?7niOI^zkAIfZQ z4su8X*Fpf}5XB-~D(1)%w8OuE9cX_tJet198L}*|=l$Tr()7$f*+1?mn(Kf^A`n7g z&0@@d4d&<12%R_q=jgBZ)03GFmMi4eYm(ZNyLmIIOiY4TtcP%2if2KQHR^XmcgD&J zqStRATf6xC+~h{dI(T*lF2jE{p44?|Q{fe}2RDt^Nsk>j}5F!&Eg`RuPut!lq&tjm&n|s()v> z9&}kq`GmOQfTkh;DSJV#NnsjCZx*4FXasi{sL-&lveD|*U%Poy{$yiv93(u1w$=S(FCN-5xJ%ZzOH#avgG&D32E)`z{7yzQt Vzwv0Rr8ocp002ovPDHLkV1k6tu37*9 diff --git a/desktop/src/main/resources/images/bch_logo.png b/desktop/src/main/resources/images/bch_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..3c30cfb9fb4c23e01afac293ff11825aa831adc0 GIT binary patch literal 30750 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 literal 0 HcmV?d00001 diff --git a/desktop/src/main/resources/images/blue_circle.png b/desktop/src/main/resources/images/blue_circle_solid.png similarity index 100% rename from desktop/src/main/resources/images/blue_circle.png rename to desktop/src/main/resources/images/blue_circle_solid.png diff --git a/desktop/src/main/resources/images/blue_circle@2x.png b/desktop/src/main/resources/images/blue_circle_solid@2x.png similarity index 100% rename from desktop/src/main/resources/images/blue_circle@2x.png rename to desktop/src/main/resources/images/blue_circle_solid@2x.png diff --git a/desktop/src/main/resources/images/btc_logo.png b/desktop/src/main/resources/images/btc_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..d3c3e7a7c813088a510c8346525267c338ad1fae GIT binary patch literal 21730 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 z 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 literal 0 HcmV?d00001 diff --git a/desktop/src/main/resources/images/connection/tor.png b/desktop/src/main/resources/images/connection/tor.png index a88b4310cb864faf5920985fa9156c883349c831..6553e06dac70d02ed4a9697acaeaafe5c36177d4 100644 GIT binary patch literal 3381 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 LNkvXXu0mjfOz(h} literal 3583 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 jCTQ-|$AkY3ei4e|7@z^d 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 p7%>GVU-e{^Xnzwb-%5K_5{ts9nK7s%M literal 0 HcmV?d00001 diff --git a/desktop/src/main/resources/images/green_circle.png b/desktop/src/main/resources/images/green_circle.png index 1555f43f0adda0fe5a1b556bac67a031e83f54ab..439ae2571b585b74f1eb6c6b4d6ef73e6a8e50d8 100644 GIT binary patch literal 5120 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 diff --git a/desktop/src/main/resources/images/green_circle_solid.png b/desktop/src/main/resources/images/green_circle_solid.png new file mode 100644 index 0000000000000000000000000000000000000000..1555f43f0adda0fe5a1b556bac67a031e83f54ab GIT binary patch literal 3351 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 literal 0 HcmV?d00001 diff --git a/desktop/src/main/resources/images/green_circle@2x.png b/desktop/src/main/resources/images/green_circle_solid@2x.png similarity index 100% rename from desktop/src/main/resources/images/green_circle@2x.png rename to desktop/src/main/resources/images/green_circle_solid@2x.png diff --git a/desktop/src/main/resources/images/light_mode_toggle.png b/desktop/src/main/resources/images/light_mode_toggle.png new file mode 100644 index 0000000000000000000000000000000000000000..870c5c209ec911fc16f702e68f501aaa240800c3 GIT binary patch literal 2013 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 Dtl2{# diff --git a/desktop/src/main/resources/images/lock_circle.png b/desktop/src/main/resources/images/lock_circle.png new file mode 100644 index 0000000000000000000000000000000000000000..f176e08203be30f132cbce567767618993354d9c GIT binary patch literal 629 zcmV-*0*d{KP)R9J=WS&MPPFbtJ?9U$F+Za^m>Y`_Q|fe{#i4PXPhLD>K{ zfDK>+^$bpG6xmij4xh`r<3bYq^(nUmklbDDo7jFcq;;ML0zbd0Z z>fM1VBAU@ZW*Wu6(tqA4r#SM^ODyfLf_G1cc~pB6RBU=PrXf$@1H{k0!zm^E?+gCg&9u~tVA|AQ&=JvZq$&=0xYm%RB44$?CJ;<~l80Y2EB#wxO@U$xqWbOlz`aB(n23wx9huYe>H%q{C(Y}ob-Zu$Py$`u|} z-Zt%6Ke4nGL(?qkK5UDK(Y+^J=Y&1WeZ7jlD}qzEQ{>>7IOn67KQp))Wa8-?EUJhL z6I-rqG;|%qxZadgR@}n|$0GvAZeyUJl P00000NkvXXu0mjfHB|9|iy$Qz%IVgK8BYSUt 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%0A( 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 literal 0 HcmV?d00001 diff --git a/desktop/src/main/resources/images/logo_landscape_light.png b/desktop/src/main/resources/images/logo_landscape_light.png new file mode 100644 index 0000000000000000000000000000000000000000..e7ae4282c72ac1bebb860f70930d099712ed2ef3 GIT binary patch literal 22767 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 O+EpKg)2Yz14gDWBe-zFD literal 0 HcmV?d00001 diff --git a/desktop/src/main/resources/images/logo_splash.png b/desktop/src/main/resources/images/logo_splash.png deleted file mode 100644 index 110edbb85a586f05b76633e99ed43665ea1debf2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21326 zcmeFXby(a_wl~<&xVyW1;}+cAEx0xA4nYG1hmhb-aEG8l8v=x2!6A4cxLY9THkr9I zzuEWheRrPS_rIZ=e)?N=&gWE}I#yL3qoF2`jzWS0007Vx6=bvk02n>!_isoD(9hb2 z-<<#eF;ReyftQwrFO{2ztBt)An99rF4NL|0v$p{N{1$7n?B9`gysLh)!ta6`{p`UB zH@b?kdVc3wuXL>--#%A%r>964r(y$(O+j%iA$<4rbkhE`QC7Nb!cjA?S+&EMB(m{S z_BucRH%0ijle34E+bfp)`|hRd1LfpaoNs#B(UV&U+_UJil`wvw0xH94v-p#+Z?B|64fV?f@?>D6gmc2^sj5OX2b4zYucQ zf!(FurWQM?rI7a}W5X7p_!O@Eo^VUl2jeG|>5k6Y=Cuy;(8EBxqb-3+>8$HHCM|JvVJJ(;(kLLFRpL(vneCvX21U%`B zuN<}3v;*q|-LTIuyY+rM--9|`PJB<7$ahzY?{AI>dtSt2#oV9^<#2vR|CQ&vfW{>s zm#{-55Hs~kv{`UNZ8f>9ddkjup)< zEa%zm&C5zvHO;GX*`im=%5_~=$G5wl{mE2cxcBXflDM{iet6HL-YqI*K8~+%=%1>q zZxockCvf`RKi$N4Y2~P4Cx(&SZ)0X!O?*=Y6(NPqsKH^Z9X*?{e8ua6y>->+j@G9|~B(M22xA}&<1h|w`eKtGSt&T0jC@Vy3#f8YdngRnLPvu#Y9-n0P9?qmp zu4GEqB%t4c1IMG;pf)U@()vCPFRS}|3O(hY6ZG)xq_jWDZ0BfQM~yy+y7qis#D;gx zUmkfotRZ<7bG`{U;PFX}K4^O7IFf!X-!>TIgxGQW>dni=(-PH(EvKc4xQycl1l!UJ z4BFwGJr)!*#)k{fH)p#|K!>*2a~2inl^~@us6)$#^%0v0>{$I(kq$ z_`{1US88mpZ@c)b9(7q`wFQKuf0F-##dlCrcZSnArh5(Nd^npV0Q)i|KrLeh~y=^rDlzAi;*v6^Xv`K)6; zz9I90fQrZNhKZQf-EDP7UvU#K$U{>fX%5jVz)=4b;=GbM{p(B$LE%9pGv|G~BkJYY z&+Ss0@{6FkOgzU+*wQdnOs!WL*#w3p>xy(ioVyx#yj7QYWO# zD7X_gWr*OMRW&q#Sh2Qm=aUdvJ>IO8vCnKckE@Xnanc0g(tVvNU!8gTK?Ok2ZU@q~ zV~XWf%q>HVmkgCJNn|CqLHfKL4dxhk9}GNSv`-um9F!%T^rRL86XINtJ zj+{MB)|>B4O}7~JqTY?w)SgQ&TP_p%z!l6(U{|Wb6Xt2oa{G!DkYZ5qfMWAQxIMz< z`=xz?8y6IlGGgLWHC>Q)ajt;8g|;nSzC(eQW!X5!SH{+}Ull53U!#>f5J`b{=M*fK z0Vu8;QxbUly$Oon)UIS`mjJcN2byCNeM-Gz53bsK&ZIYA@67Q*f(HtV2$n`-mr8Ov z$ZkZrvC^+w;U>|FN=!^~5oSKe{ASSYyc9N+kUX;sX1VEKOfOMm?@*ET(ooc|KzbJ> zAMl@`Mh2J1P;qVX%)I z9*1yw{1FaDy~=bXu3*j&@=BcUW6#NHef!5~3M!}e{i~8|Jr)jE8~Mtmz7$4l8k{If zys<-LcW_U(*(6UdGVri;8-1gIMT7`O_40_1xlZ!s4jGiJsw4w?JytTMaH*@vi$C)*hs|m zj24(Xrc^}ERnU2)W1f7gpQS~Y9zwEbmX|3|kfX7y*a1QRwD`siyF2qh(xFJe^MPuN zYenIeLs^%egm55(6BCTaw1Q@7EE5Sy6$4Tn+ywFfuFxs=Ag?_3gj@2cMCOFrj6rm$ zo$1~Qn0G9(a;6WJy+wl9H|j2VBxa*fG`av&Do#7`Q-P4?g-fnpWztR@dVVDv5!-pn zuxfck$NI2rx`evdt_(_ZJADx+!LKkCH{?Npme2JzAGQ*nA9&Xg6w}RrD$zxK>7f8^wI)+hhy7LaSKi#_-;9=wN|chUoL?kaj2puYxy6lzc|9tc=&i zFb-Fh#Zl%b%G}QJ`KV-1?$5PGW?Js~^>_T9cU#)mo14-tQM|(S#)8BjM9w7#$OdCd z7nu<=NdnkX`{t#zQGHe#*pw8hF8L+C#8P;? z=RuLaKbm%n1`ah#RuhV7O2BW0dWE$UN`D>sJZhc$OYB&ifwG`w;7}{1IqDVr@db^s z3UF!aE{r7tG5B|lGu)7DbUCM#)@azPkrtKKqxpK1D`jLPuIRTUYvUifV z!@qWiFvRE`LAO}5u=8CdU%8j1vZcWWVzntu1@Zp$Zz)&8bgFle0 z$Pt}>;j><90$!eRo_M^1=R`K9qb$(Fx>S>&D|w?VslzSiu@q`hip%Q(m}wreL|8cM z-^W1LWGBW{pTSlusBR8U?tt^U$3jbKYP5j|VLyZq0l0n0Q@X|94^@azeZVK?rd1tM zl4L5VKIZJF;RcezJX!$bQD|Tb7v84(n>*Yfc z@P3uWGTS>|g6)-yKiv9tn65ePzB*AidOt8oQz0lVTE zu}O0txSA#Pxt`J;yo(A71S(rS%q!iPrPbZ(KRm7t&p%WJKkaP2zzS-{%)on6 z$fXW_KbI8mQsE$XCd(|sM#L=klb2R@qFOE~e9Be*RqPyN*1J!=vt%+x_0vhT?C{8n z>O|_t6Ll!gpo%d=6+gh}yhxZS^h~^}sp=ky>%%M!0DDu`u@Jcy>zD7DXhp}ZEKgHXWS3=JaWl38 zx@_c3&N{_r5U|YH5H$iN8kAWiqOPjyO0pWg7|XYY#Z#N|A)9l_ErNSJg^=6QlL>`L zRI~oOm$c3xbw1f>G5AwG<10!$UEM_7VpO<|0Tb&l%q*2w-Z(hxI7CRGTs95sC=Xa1 z?Kqv>JkVT$p=u(|JdfBMy3Vh*6}Z$JcE1BR#aJ_knxS3zAANuw8Q;kWIcM^!&z|6Yxe!vjM`f@NNcQRtp^~b8b9!^ zZktcL#U$%#B75^~&~W8@v(G>(23j1!@VIsIRsfDor&ttHU=s-TX0pG@mEx?Xe38>I z(Xg32R<*jtGd;=vQdE8^RA@vr&T>@4VM6ZfF823oQ37*W?BV1$c@HVM0CFzWP1TY6 zDq1pS4B}w7M>({*$ zE96?tDg^YZ^_6L_?W4i;%RTn~ueEDR4#RqhnJEW5?Wm&7@B}O`g_KLN#$mDVrCBB< z%SEUN__Kb<*f8>R6x&gAAAOcR;?jx$rp8zC%P)~DfVGNcrfTYxFtY{%9~X%H(NXLh zu->NC0}ZsD;c2$O4Mnpt=Vk{$Bpr#zX>Q?ZBL{1p(Lssp z(&QIG{jO=-Aw8Ju!L8V*NxxtSY#513OI0!q9`3*hCs+fB>$Z^}o0J~|RlaV7PBfjL zrg78i1-%Z-$tvFTc=IDlRuqnzT**_SW&v$jwohRU0#N=gb}P?Ljv3kfMbr+b3$Yuq zhGDNL-Vflk7z}uU#^>lq4bPY4v|(IBoW3X{ho6t!cKUMhm3>pRuaCTnvu1#*;~Q0R zZ0SrN%ypt2q=spn({w}Jutok#!qF+JQw6=QG(mu*_YN|?L9zsTci8VJ=cb&8c(;>W z;gT#{Go-H!3wQx)szmBph2-VgrCxS#?S$B2OLAeTSBxA@W-=;DPKJ*MaI;guS)-eAt{dv+s*dLoW^- zhOyvazbS8L`grY9yVe)k|Iip2QD4kS%=C3Y-%UykR@d;A2sQe#lEU&%ss@vEv{DGj=?F!t5pchlV(<;o-Z} zcpdZCEbDvabz-B?-sUo=qNja%-H6QSk4XyPN+>&Ydm2_y!_HhtPKn3#KGbw+J^|^C zi0jR)zRSFue!WOJxAfS(e$`BJ@|YWO(@_1()yJVne|CBPW%G!pg5mrx_za_qMmf^q zPfWmJkSBt(q8PRk?6J}8z}Id*QW&kO`1<=ANk=ZLLoLL7d1wFa$29rD$w6`-MCw>G1B2LfY9_hktL6 zPRZKa?k~Squmm!Os83H4myN&@iwaX8zL^%p%e^in_^f*?{nHqr1B+J$v~_IGv>eqs zQ^3K)NIf~*LiInZR{D4}DL7$6ZwM``UR`0;nV}(x$=l*$P5906eurfyzYVWuV`>WB z!d!$AyCi4#M4udEaL&h{iXAUy|1b)BP9y~UELzs675f5_W6+7qX5y`j!h|WZ?p8B( z#@#}L1d};Ih#V!|*HgJe)+UjKU<oOR;M3={0+4j}pC9W?L3(f~z1#U|^ zm>hqUcdwdAcC92(_(GG~+p7Rfj?^d>@=JxPr=lb{aFY2Kh^kbx8nb9`L6N8j!DyEV zUB#k)8#x*1f{RPZlARffxY|aJdyAoPzQc?%3LROjb(7jb*YBfG?Jy147>b>Ft9yJp zSGW>zHC44`b5ZC-ua|$emg+U#PzdSKSC7mhX)2$_k^o|_hxrP;nHP6oY=t4q06{fV z@alzh#PV{UTEn&$ezrwG+N>X}LqwZg(j@%9D&W-(-n;WBUtznQX91y zZOSH3JN6Q$#JltyaE;hZ&6N-D;p&Qbdk!cr_zzFF`WMOpPr*E9jslfY*{G7F+7_xh#JA2eIq3Ceb`u&M$2H-2H8u6X&#n-t0=c= zV`3^}tq{Rz?$#-+_ip#=m#Nj8LD`Lvc;+cDslbtpRlqQW@Z>6}d&*gZvnp_vUj@(M_H z)u;;10uSE>5l`vjs$#ST>|)Hd+b@k3_H|2touR&_r+DP6p@kpnL7PBRw~dys$}`{_ zxLeVaytnJv9&9KE`%P@nbN<3`mwSV;X2+O|vTtfIod}*kjtQ{qEwuuBHNsDSky0<~ zz(%sop~2`>U@f=ENpa~eZ->$FD6w<3;PyR~awN=MkzI*)JNg_u<^C3?a7nmf5NA`e z0KP5=cJFf{E44MeSq3#Q_Yx6)46xiwVK|fbN8*QZv0(&irmm_If?X`N+jAGz z2e8G)wb701%f5$>$hb9-3L6m`?$kkteDPKIZxSzT92#jeZ;TZjxtCktU}IW1_7Lc7 z*S9Gp=4Wrcr;aDFv@AXry>dKuM>5Y5|8*y%W{HJ2A4ujT@vdQ>f>+V!jIT2<{!dQmB z-BMmzB%&c*k{1Yh#i4c9qIAh#Tja#|Nm#s)h+&)vY7UNO zVG8@eXZ@)@op^Yn3QLIOrxg#XLFTTBnm{EUz#sLy|01hPE6au<(Gbb*QD#P=DbD8W zFNR!4oN~^u?9RfG!xw1CL*VBO&zkUPb4+%Z+P7p|sSUJUU1Ed_cBtOJ9VdlNVTdm4 zzqkd}LbAXsdF6L|IsHkIg?Bp&Q9j$=pb5>rJ_dAWN?FI~Fb;gOw}>{DeUolE?i$gH z8%5-NHM@@N?g;~$u8p#pd*|=Z`*_K+D?6A>G@43>itjkwdso6Y=otiuRN&INaNlpf z4#r74=Q|NKqYrd34-MdaGd1Fs<^m=WE1ySN?2{ptZ(^Y zSV(H!tzEGNFom!R@1@h$)4xorNb(?+FV@M}z}V7p@b;JN`p}_CBQ|rf4kKC1#sslt zxd1y|9JpM1h1yfG+Q%{*rWn@+QqT)$`DJAYnVXEUblC@DZL3`m3|s3o`Ilt-@!)ww zJ>IMFtbCBNC9uZU=*8Fyz{KsR=nczzUB~1*h?DK%_cNuhIL<4Y*>{;R=uK^y!pF*^ zmY(xy!!fIO;^)zK%a+$;beDs_zNwu)1YN7jGAW;T?j8C~#7jloVx?*Qw4nOV2{hbv z=wZw3Ot@P_n4PX9=8S79FIA}eg>;R+nmlGPX@oLqE~x@e-Y5U}N2`&EL}@U&0K%&t$* zt~dr8JHI^$-KKPY1>{SJjCg&fS*6NbZSoqw>&Mo6hqvLm;U3PRa6jbzeM?|mHtIoy z$I^Q0q95C`aG}=&vGH9Y&X<^?*h?j{&9_l6%BKuzfnK?JA*S1c;(;r)1*;J zg~pQ%mqVoD^%C%K?HASkZlcJ06gV=C!JHm!1t|FMHW8n~*H*G3D-;Eoa{#YFEe~8M z5k6ryD=j@q&K>@+aCu>@HAi*)p_%Ga2hQvPaF?z?!g*)aRUn6NXd$BsH=R7n#3dmL zH<8-u6~K45-%)$mR*2*5p~yN@6!2sap;(sg`xoHxGuC_24=f~Z03k?HiM;t~MB zjI)Q{SsAFR2wS;2b68lrT7o(JoZX;zS^$8ExSyMal_S`T$`WjA?;=Wb($PahWp6D? zqtB{ z{*2|1UWH{`t-POwD$0n`Jdan{+SSV5TKLab9!^04D_%hhb_;H9L3Un2A!~L)Ft-i6 zC8rIqCBKk>0GN;aFHnjuo?aF%R^VqSC^&~b6i0wlfRl^QLWtdp&w>{UVa3I6$qTk% z=LT~L2=ZD8^708-`~^bY!yf7h7EXU1)iad!GZc>%mym!BCp#Y>uMIn|HNOSBr8So| zyAU@QzmO$2FTb@Q_z%?cItWW@D2mc>b8!B%MZ?L$%f{8iS(HY_-o@MRpB*~(&R}gX zi)W>A@pE(X^7BAH1^9Wn1bF{RqzCr!gc|uXCKo3M_uuAiZ6z!RMYMn#o4vDzEg0nH zV*7{Xd0B*^lYuJM;@LN#$bZ_Qa}k#I09$yudg!>iI*HOekCo~f@{dbViTvGJgjHOv z{xJR_23tS-+uuD-%EA`(=TQXoKN0^gNZNL;zApdoaQ;d94-`ocFJD&=M|BT%O9!x( z*Z++3-x2=p75_Jr`oGbM{LNeypuDag{(q6L4R-(A)87J;ll>o6QBnN~ z0>Tzne^bAwg%8;J&k{iO__r=AI|~Lv$w~9K<)sn;f(He4+biYee2z=-wN_QdwRG@bvsD=qh^;Z9#TZ zcwLX>@#yX%J?+JQt*7g&dwuZ`xKCSpuv#g6y}TAQ z?wJPx#6Vrkt*hbAP(y#kOs)Wo^fzpd?HmJ3(^>Jh13CF zJ&quTAH5s58xbc~_fa5N$c(JrrYa2^HvnD!jl?c*Q8qslxgYX6l{vX#K3WcJH9Wy= zMpQ@z@Z<#~Bfj7nNV{g*sniT{^*h8UYzKDFZI#cE9UVv-MmfZUvVnpXQ6}q+e(JuS z1S2rWTnkiF-Z=*ot4gxzSy-S}evcQ%zF~W7{{!aHW1ViDkh@X}L~X$?)loA8XV^o2 zqu7??3f4`5ee8TY*2ewuwLx{C6#ncMBr41^OeS0H6^R>Kee=M{_8S5>!uGNHtiw%b@yGD3H^+JMB&9>q_L!S3nSi{ojXXRT4KFgzXB448O4q0bCM2+WVB zsVz*s6<#lbX!49+1DJpvp`@YTiamcjdu+jD>lHf3=LO_UnGPVN0YJS@WX^;A*$km~ zVb{PUAbi2=v!cfr3=2K)Q=|nx_weD&PpTfIV%*b8v0kZjb}!nr0sW+f)7Ll}C9y2< z4UGpemkRO;QTABF#9;||JUi|2Po*8zt@_Gw8ZUK6AdF!lYCGp8Bzg|8g5)=Z>!;D4 z#z3LSVG84sZBilthm>6x9_4kork4Fs+qAK zy+DMdbBe9J8^wa^AljnFhQI>$OntYo&Sb#Ju1g?yBnkgxCf%t`Jf)8+>2@>XJzg6U zc;)@-M^zd`IOvoW9cO=@J_(%)1%NKczsOJpPHF8gsdpT_|Hznfj=@Is(obKhA%{cUd8nXKtUE zN1?+JF2ET_VQ{cn^8mSD!8I5}8qV$aLfA8I#*h1bG~@KmDW6cPX_Mq$5=d4k)L*sU z__{{MNVQq!^NFaeybh77NWM%Mfttzym<#xh}UC|?s% zHM80D^(m!)^!b(1Y3(CM$`{+agChhnbmOe?EzF^K92o<6l<%X35Z6%Yc6TL1P%wT_ zh(Gl&>ZtLi`+Q6B0G;4hs9VY)xuoN~BhWrj0C*x-vD6=cUrHl32X9-0VFXzU(=`#I zP8y!@eO$As6RPemhZV$#!BuDYJx?g#7MWGw24<9w#MauuKXSZKh75S!L?bI7^i5Ic{#ZZ>f}envG2 zTMr8O!=Q>7@h01CZ!waGE2eq>?aSkeuVYque$I^Uf*CVlMs=Tf1b|ucZt8n zNYcGKJIH_Xo4_G|UIZ*Ph|bi>Y8$vms@}F0dqJ>o{|=8bsHw=zea3fT>uPps+)z5* z1q}si-$pgFKj2P%Goiurx6B%@wx&;sUV8BxT1dpM& zNUF%gLp?5IZTEfHe3R^5ljV$hebM5#U*}Yq5p_|l;fIyKxF`e@(uTdyT-s5dejH7P zSYVy@s|su$?FIfyMaU~JE#DM*=8dgKxtKQ`yM%c{g8n|cyrT>6I-T&$ ztt4O6wC^wjksmi~to)>YTKU}E|E+im;hA4^zU4ZW%#}#|QIXexz{G%EiMsqrPk!#& zor{xzluqTMr7fTCh?Y2ZjY(z@Dk*Yn)>E?Ai z?>@p#`J~GFq((R})G2PR-SfmMW71ruc{U)B_<@?XA-|56pZ3&$oMf&T!oaVA8%{nB z1?!o8Zq9hyV#56(V-Wcp>L#!!3^pf>=!X~YM1GW5vY!BFv?+$O}S5 zg-&|X)qi@^C_Qq6r4RZVmF9;5C9;x0b<|P$b$8@9No@qOvy5J{Oko3_K_PsVCY(e; z*+P@RDrDEWmCNt`j4fy{=91kkaV7nO78B>=g!Ez8ZyP1wC(L%k+(sAcQwx{(~) zOEMJz2r-yd+wBL9+OOaCH5>@RF^VYBqhjJ;6O}62KP=|Gtkg(EZGoR{Sv{0z>QkVuO%YNRCngdpTsQiSR1W@Ene~rdQ)^Slk5X18+BT6!Ik)QS zu&z=s1&YGy>R}dGj+zKDaPT?5O@y`LZ5k!8Rd(wgmY2%t(0-g(SHbKNM!FFb7DV@B zbYnj)lH$LZs<#QJBB+|+;Q&l~Y{9N;XDnYS99I8o75M)=gD;XZT9zFjnWWA1*bjrKtH0 zC(+0}idj`ztOo9*ujdE|UCF9Q5e=Q&ZI{};hs3l{;;La=+nT&Yh($&(tvzB#|kw8^l%!ux>uaY(-5&<{S>N4!2a z|7i`A)2Bd+tDea*TjIitKIFk`--)M3DiYU_S#UdE-)$e>_4CIK|Bk%n_Ed4>OR>|= z(xjIJhBd0II$~}Dry$6;sZaM2qZ_}&Z(ePq8rC6<)4o~jBojQmwxmOS$?wA*v(=w1 zXG$~RQO#aRHDxOhxU2x$mUK+W&0sFixcFm_>!E4}5=l4xnv4-_wR-tvk9u{jJ zLSRp_!t38*PNrk1gphi8V?cGH=Y6pFq-de8XnY-h$Ak6E2VUfLymgZGbLg^k_LW|F za6!F*gHwMV*7nP%yu6l$9c71COB5d6%0kiLUQB>1Fj#RtpTK7tA4(_mQO{XrBsJAN zQ5>rc$8^@EYF2rWy`;SF;6}ActgBwL9Xq-n#S>g3hAH@-h%}i*aJ{2J1~MPJ64thB zYsu@jVOaAX4aQ9BpjEMo5L?gUSB);Bc7K3<&Zej{7lBNV>la{p@`e`0skTCn#!}6k zA^}W#s906KdoCL@RQ}5*y?Gc{Dw~C6nRPjbwz4o$Q_5C#HgI}6mZq~^bBmMHFCgRV z8;1b&mm^oz%~^`(u?AmUY(*&+RO5;&{AR!84)CU8EbqMk3WM*wX^)C|m9lH3v{|6Y zlrLl8QBPPsCmI|(JLkuGMnK|+1|H#2fyPSuL`42CqJk1!+X5lKuYDABsunHt^Q}eP zm=Fk>hb`2DE306q(!RVk>D67y#Wp-8PkX_yxb>w+rJg5fOb=Vn9UiICyrz^hQqNKV zsTz(zcV&4?e4Vkaig(6G^QvjXqAowz^j!mFux{E)BC0O7#cxXeRKq*;A=tNrHn3)a zkSp66!6Hb-rW5L;FUT-DKc?cQG)<>4`7dtdO=)P?yxgev49^(ewI5Ie4Y<^G2u%FC zvBPaiJhcM zd$BI5(H&m^osZEq%VBiQuxfuD!ROO5Q;1$b0P23N6^+}6b{!S8DzIXCDwK+q34fx` z#a*}RjetK0-)2uArgG#x!tkQQNAqhQio6%o^UKtKLM(zsU9o%&G4X`TePxl?VGDz* zQI_4UA=2f;&Yn zrYtHiby?J8t?lpyxRC+6#kazKztX_Z+@`7SJJjm4-X>f0cO2O{?T49bv%&+W1H+Vi zecg@F8pw!9eimP>Dqv+?=rqY{E=^c>90YI8SGzR_jya_4C$iT>pcps_%nWrw)}Y%9 z_sm5o;_?q{0Y!;DgEP36DYQj=gd-Hhj<+7M?4OVb>I}wN#{-Yz37n=Gyt~AG!|GYg zBaH^_J0;6FlL4=x3m4T|jVU}>+rUh+g3)Ui3G6b!L|baA)7 zqWs>u|I$SFeM5CJJePfp1urREOI5YgYUK&gRDYq#bg}+9tjGq8@FyTsB~`yerte$a z+j_$37qy}JRJBiBwO5?`XNVT0V(Qos>g`6qU$0*e2_QeWa@s?8^Df~scp;>CkTJ&5C^WPW@#6j&f` z0^lcdJZaw{l~1254*dyvSF|^+X=n@<)^qSn*Ilx&Ao^ItTeUou)66$%xcO?e(KK&I zK3G*;e*N1jun5l}zFFX&7pAiv8ZnpiMPrMbesN0cuhpV{AGIDzCEp=?&`Z^Ii6Kwz zeyOgrb~Ryjf4OPa56VV@M?b{GiQlsm2TS~BYhY1dibqKlb|&?3SkOHB ztO@gQ6xU+#dTyfv8>-7_c|&Zy&;cLfHQmGNQX5@k(Qj5eCQB8GX?eG;j9qxI?qx18 z5w<*|Ur73A&30}0NAYCi!&e!e88qiN#{iEEU{nBR$~$uX>a?{0i$uSSj9j=2xQWT0ffouJ(vCnJ#Q5Gay64smYTAio?02SFl4}k{q#5U%7n3LsW!| ztBlkz+$UACDp=DF9_=MX)r#dP zw<+>5niTcy(uC+Sym!V=Dmkewkn!JsRLMSYA<#u#QSy{Vzb9H?(SEi-N0r_Ka&jR) zPHTNZHm=Dt{)LOM;1NdRHE|P!ZmPLTq^b${;?{ULh_n}Hn;oR`<089KMX&IJQsWeR zv!$jP^1Ys%7W8C+f}-?u!tR?x6xNa!3DuF{Op`)TH)#Rf^X{^WI}2J{`64@hu_{gz z0uy}qI8nDvK5~C+98X~<=pev9Pga@D~HMU zTi7bW>lb^=(FRq}5ws?cZ?P6|z>dQm7k%E6C&M3oX)|Yxx^LMroE+nh;jySyv@X0( zkl72RosT_;E_1qOS=WW=OFNcZvH7r=kXNs%ago;t>UGd3DkU~BaH*Mv8zNv=&nk~e zYi77AC>@Ac?(Q$~ohndg`!l)$-I(i(ar09(#DYvZV`d$gQ^ZPyL^SD$@!V78nxCjW z$lczLLXEmmUt?JMz=P2Q78>KIK&AM}l_7+Hr8vtJ=sk~XyfXArA)IPtBtIUsj* z?srmu!#K#DT)rY?fDT0jsjEVQfJ7gjE>(T_T;U}J@|Kk^!=XKY&Fu5x)rV0XsG{fl zy+@+U*j}NYW=#n18_cqj+cllEUhXewsUWW3otcHL9e;UeP=$)bTql&oY|SGLxeG-N z?QRK?Si{-`i6k>NlODK*Tmo=?6D1~A6Bv!`6Ak$ko+d#6eAn0mQFWaAABkxlK(z)v zb@iQd{o(58BoNis4j~Oyhjm;GdOU;t=sh>rXP?j)1nae%G;ci8*DEdqpr?u3agJ2! zbswrJ0ohy@V$OV++AO=3x7CWQ=9$yF)E{)$J!zi9eIV{U`;wP%Iwg!LW?&dU!9a`7 zgp%zPzxwu9sbv%V=>S%@gi$GIHS}m$_bLoE>>|wMNU#hxO)$0gl$qVVl5M}1W?5H- z4w5k{2Biv)bA$&6;ro%hkvcEj9k+_yvWKXJ=tG@virHtWw08wUkm(H-+vkQ3w`cs= z3bBW@N{>{Q(BDXUF7(doW=Su{_~7Na2t2_%NZI5dRn^y5&wmN)fmk~nIc^z4L!@kr z03xnI<0AXJEUP$w1d;VpFPHxGOol!dHW7FBq8WO~9>m1m1XvpG=&nZ#hgaXn?wf7q zt)&x^NC(GeK$ocN7c0f@33vL$S@^PHU*UP*tap^1+BQe;lR6s{`Snjsx;NF zIc1HboF}rmOpxZ}av8hzM2v~e*>+XVno>{%MTFF(yc6!Us&$~p7JBFW%uW)XZELRCgLwKns$IYDaTyH|)~4%EjYw zVCM@3aH!=(o~V|xJ%(r8O8v`{FvbC;=besT|7@=edSFg;q%q6l#-x(?CVM+P8*ddZ zecFPjDPjSVF-#yHcqJWrA~}MF4Q24#Xm`(*Li`K?LzF+XA&5Ai$uuWDbdx$ht-@mX zD6@HPHxxQeJQ@YQ~x)CY&-#i6-H6iGS*Y6H5wUUKDE-Ez+=V~kK z0+Rrgz;0-S#2jLKrdEq~gcU@*L8=$Ya(q7HvhF$A5Wrdx8?j9_vV>&uY`US{pqwhE zTt&0pJ)a9t*TSHZcf#GU)pHCRPZdHaPEF*3(%-e@Igpf3(A7XQcWRSKW))7T?m8i4 zpuk0SM^5C^#&g-0J$JD0mKlhE^+`T%u)pip;xQ#*aL;%e5?Y2DbWmYtF}y~J@CbjC zj8{eg9oQ$ldAw7#^>B%HqaKD*(XlS=wBM;32AB~~MhFzTEgi1V5DfkfdQQGaBt+0X zc50#H`YuPdQ4-7vAoab2_VdbU$GeE>+4ls%mA)Neuq5!XrzO#Lx+P2T&UO+*em)53 zKtSMPHy;P4!74)Xw(79Wvlt&`h0}Y?YGa?vbHaHE67BviLFn1Oj5++oyoKW&Fc@H* zF}{_bQbi4weK8YD-XeKzrDd4}{|Kjj-Mlg|Hv*xDMA1c=L3cF-Ssmu0r;Y_Z31#H) ztC+@l(9Fa=+-Lk_2n)%xe+7b6dMsfKtD>3082W_J9i}y3(mFKW1C`7+)cj6Y5^)~D zC}SY>yYTHXzy;9-?h)4yZl=vz0T~)e`pMspb&_c_34fPRpoDjUcY$@`>Z^>UDTZD$ zygqeu0a5_+#AOv2nN4~u2nd72bt72u)! zNhvg6iJr`;N;)YP=snN>*LUA*IbkFuDNL;)j28vk5Rdf`7}hW_M9f^=jj}Y`S@XM# zH*^7;r_2RFB!}g6IyQ|A=Ky+v(ZjRoJ6aq%uh~J% z0#Qq8=jZ~{W%wKdP7NV7?l}nO(2$k5YnA>Ixf7-SAm>gm#hX%C5Lxg;q;g5vtJJM! zVXrYgbk27B@To9gDJHe%#NQ`g93igpYrkOE+eof65snph&VQGFYjw|!#CQ|RR!{85 zpuAg>XUqI$u?@8a2+<^p)ZIqR1Tur3Yb~AKxXVLlT@Q*c#^}*&|7jIrf z)vhj1WI?YHlqY-rAwoZ-Tj#nQ)jze)N7Si8UQOQ0L)oU8uA=rZ)eE}}xZ#}1l{*Gm z_XVqjcRm)?^2W=KWrubn?3kc*T0r4!bIiW05#KzfkouO+yF*ZEABG|7u(of~+-fc! zaTezKi3ILGN~t>kT2y^(QIF7jsy6*1<+A`yn`M^53y+?5FJEwNZx5gT@WVAUBjR_* zgZeH3TJvlnsRSPvrDWhE0+eJY%edcpBt|t)Z?y}1g7{FmM-a3lld2AT0z_C03_yflD_KxyzhrNX zLaM@gKv0DZQ92qc+@lMdC*;Qvw{nRP`&0)&+$ZZGSs?0-ecTk*^8y5-gA=?b-Acno znodN^KF^QBJReOa!1 z2znzXeb8cq2M;k_kG)KD*6fQ29Ch)lNq2?opbx)LSpO)+-`Ivr=;$?#0N+V*6BYeo zKdKhDZoJa6)G-%+RUn%qF)~yJe$>{{d$T@ruFUE_UsDjzkMH!eZHD8gz>>FPfkK_c zj*+J1s3MrS+dPdmBsUcZdQAx3aau{TH{%qi*z3m#CwE;RtMx2}@lWL;h|qhV4(J_X zbMCwf;;Pd6=Zu6wIi0Y(FvrhltHff}i3pFFgCQ$J3Fa_6;o~p@!zQ$SEWDk{S6ZXB zuXJ$7;7!ejFrMggNzY0jj^1c=zHuab0qCrGOCGE*tj|-tBV%ro`waxM7DMDo3=n`i za<+|ywX@3Zd`sV;_nM09_qnu*l2geW{X)*Uu8)v#2x1tk=00TRv|@6MH04)U5)cF{ z7JHUGZy#N zvb*h>qge~GH6>`43tg?7`xTioVwwvF4C6XN)guHvLlUBn^LHV4u-}G>w3EWSNd#Np zNC-oOpl%#nVc%(SyK67em5{#%G}_>p{EVnHHmSOEZkkRF=scrM>JL(=fsWKK?!S6**l=|F z7u33*);fln4H#uFCgI$qYl;W?IkzpgnYp~go-=j-o5x9m8KtyHfg(j#{&V`dSY(iWu}NrKHptv!6{nmB3q*X zL#q8#E}|J-pp)~-5BoF+!6nqWF!X_pX}%%vo5aBY4;x4sl3w1$Y{}oyWD2VeHtZ6@ zJ22F{#MIcIz}58gs>zM=tAg!et_Ge&B>2XJpqoL$buPAF#Jr>j~bgMhM+2&ETVys~0N# zI-*aT*pMAd2Fy!MaW)R5nlZidDM5b;=xwHMhNJ*yX|SO^^39}nL;7$fU|!>eFa%x= zbDFIzCSg(iVZ{bf@Wp7qaO!Qgo&Z1AjSxLqohvY1TYTma9}>t{;(+cCR_UA zio$_Q;bDwR^&tfewT(MGP+t{3zeVNnyk!LQcg4aNRQg}YCG5N@emgY~bN#+AwG3i6 zjs2b?)H<$u1hcmP3#)3P=eMK^^XGu;8EerMrt9>iGp>1~Qp%MLbw7-&gENGgwR_ww z8U8w(C>A&72^GoYuf}I8UIG%OATQvIGJY3D$uRu;I?7eG$1cc8IXQVf(t=34{e$hB z+D}&Pr>Q8)o*yS)o}K8r^;ulKvbbaaFcJFb3x1kXcR&r&rx~kk7r?6R>iV7LKa!yH1OH1e3CQ|Hzk!f2;0#X zx0VOWf4D-Nc=Y1(B{8sYoeT>Rn!XR*GhT&eqI6&xSEHFe45%;|zv`r=Fjf~|J7hhr zU7dZz<&$CB$K3xbJO#u0`?T*}0DOnqA#E$dhfQ#5O!(VxqCa+m z<(N>ndRFwk4OSn52y6Gb0n*R5_GYjwhBZ36RTbu$pGEzm8y03~zLuq2k7L4{dM4re zFW^p0G(yyK?*+aUGsJ2-6A@!nV=;8~R$a&r=RkiHQ`Nswo0P_u@Rn^sA)C1^e9+W7 zZUFdTA)7fPoy(6#SjjlR-%`7;*W?`i6-rg*c5NN;vCbRm)=NScLL-4xHC~NKF+-i! z(1IHWz8o%Rqdstc4C;|y8}2X>;1b|RwC)$5p2`2Fwtuc2Ovbb>o5_O1)E$W0Y_AM_BX+jZoQrFIA{4A`?HOUU8~Bd~ zLOoeo>kEZ!W=&z^x)~peqnnRag=}UJ^gjasPUF&%BSiBF3*rix--Mw(=Mp`uC@KeI ze1JcgK^2|^{UhK4nlDp$GwltrN;F}~dF`yBn$G2)C}cC|0Y9U~;QI8I5kK@-zZuim zvEF@q7H|?e_);O8xdyWVIB9LJ)_(O>7@zY?ns7;Yz-N`^NR0roScNGZM&oqOc7%Gl zz*}Z2bP`&fU!}Rohsm4yTgpkaE5ynLzK+Q%tM__eVdJ{?&U>Y91>Q|tikGOnfXk>K z(>l3=I0922;WwD9th+E9P1Ur-;bG!D~QE953N-bMsp^nA5$%MV``{2 zW2qg1@zvH6pFhHc8QnL--|of~Y{@q9IrjlS9lGkacHPXA9Y`CA$g=jiM4z%WgF-g* zZA?VQUlU=|YK8|?P{?LJ9*W{CXs)wv64Tx~4vzru4&54p2&-VEA{fMU+^lDX{S5d! zl;t;ZzJtQ$W32+&i-1!{H*NUzbPFe)%a;q;%;zyhlk2%90^o`+YZTb3OQ2fFW|S1fEC-i_8*VzP%+ z4GZZk9eAI|REwIe@Dix(U;65}{Y!0NG&&vuRZ?O>rv`+MI+%P=4INZSZ_*b$(S=0h8t$ z)LD*S)r%okP5C2i_dei!+K_Xf&gJh4-7;6vxV1{N1egebH=?YcN_dqBYfs;A9r6P# z$8=MzcN2Y2S$5Q_NH+ujQ|Ip~;EY1{*)gr5_47k&=e?JKFVE1Va9%KU_3awewU{P; zE&s-_Tfmu^Xp0Al9xFWcfmXnDjd)Eum;bd`s^SXhb~GkzXipt6sYW>0-PCSHT;=Js z!(%Wz!S~d7?-=mCp(ZlErdgdObZ|NKQ!% z>Gw1Ir286-cOjY>j`)Ra<};Yhv}5Xgmxr2p=B9J`iNdD!mlOC`V*Kf60WSQ8<(}ktMZ`CNvi(}n29>ZsUmqv_kTCa5ACoz8YV=)!0 zj>J?no1A|KLhJT{aM^zcN{z{~UxZZR#)K(o|Q##vsFhgM-6XQk2z(gF~oKcNz0bVpNZqC*q2OtgD#|20O^afeO!FjJ#Wq{mXSEPjf;eAhr z;Ll63Bq{qjFWhWHB>h)|)$+BxmA2_%$oJw(eXx zE;YM5e^Y%Cr~SP@HX87;zhvx;Ua+uQ?&nC-wk^EY@tSI@7 zd^-R9Yn7@dz2FUpvLE`!j#ZZeD@SF!Z+wrdycQL5>En=uQnVDv{bRV9h_hqFZm+fx z-K$$?#wse?R}SneK_BCRtol_(UIGp$t+Oaho@)P!pHU885ED8bqs`atWn z@-kA_RA!{CXx&lQghSi%SR+Ac^|yZJX6@F`FF$$emyPzlLiaWGYT**R{ISw!%d?L29$gp2exRr&eRncT*0<&IL#i(>yF zM%nY1r)5JI5rAOD{+kkq3H}!8PLs*00$dMfuuboU?;uKDnXl(U zOl;$f_45|lh#@39iJVZTsKAkLu|Dh4l|$Bfsu5?c00I_^h;Il5&1!y~!}oWFhE^N0 zwjqT{zKs3(I$9;69nAVFf}fjAg@F|>Fq3Pw zaF=8v{^OT*;QX|DERHRx$^X+UqrN}1R5?C*|od^mV%#MwPNGpm(CH~v0V;n?!MZ3r`F<_iX%2@tnm4k z55hW~aoDKWdQ`P_SKsC)kNA;DNBqv1`f!`f^Gy?Whv?^>ivS?0S90Aj!qy5&C9V*n z5#w5&7b@BbkG|hH8=i#OYsq;h_u}?n!ljAN_epCmIQVO23UTxslkZTY0bwd5ws&xRhBsiw|um= zi9?w%{kpKb{2{QRM%q8s$~OCEP?! zHnCCt8vectI!>D}+u{$!zk4ZBXT=8PuT?$1vQLnZT*k;8&bY66^Ygfp9Zw>K7d` zDd`+9tkObdkV$3beo$^o!2N!vjuM>wx*lIZGNVuUw(Sj^*ULKnU8URlN~CrHjo7OU z+;eRSYRO)$wu8xiEppbF88l(5Vow_XI%9{uC2*9)@r#Qk+Xj|sMM6qiQ~VSm1_87W z?JX+P1ySipmghfF$ZYZu5zH!f5qMfS9_Up+uBwe&C^f#{{jf%fJ87y+k`+feM-Ip$ zLt`)nwZA4GK0zNMDpj^#c&AAvY!MY+SRaa7ZC(*x-dM(52K<136)ODeY9v^ND8Re_IPxKAsO@OFxNnkw-F5iOO0K%23Wvyh2@^B%_@LL%Fu}#5OQ$Ae6}3*T zhJYA8$RC3rMqa}!mdO27y=!oby*^4K29F)FJen(fEa;UVTdPv>g@5#>QO2n7QI5TH z?&CWs)&3Sm_FWWvN7y5I*5ywTI1S!7omC-YEjeYj`uW}=oiGqAwKd+xjVX!In-(Qj z7|B`ep&F#*Z3~_sD$%5 z0Cu9kEZK&3CvsqE*VJgTy$`4q65)0)t9WTG-F357by6YV5IKA2Kq`4RTVUo)M7l1; zjp3EB8BGSz-Z< zr&AWPhuBFZ9dR}Kv)qq>^%yBb>g53*t*=+IXHQS6C;Hnf;;`x1`4s~!&vT0 zF_2*;y7moE7IR&UM?RGNQMK}=n9vU`D!Sjq1s@}`sxdF$8d4QWzsfftOGRjlV<~8k z9%W4!D>1~iCMSF0@a0%e1cH@3Ss0SwsYKAx|5k|0>W_7hc30+^vGA*YqDqs&pgW1Q z;cS+r@J1QSs}~|8Hqo~WGF2It&s#W%te4cSjV$0Y-g_lf$Bdk&jePiVWKETXz-S;E zQETYuxR;;#LfodpPWXV-2kUMX9_0g&ZEG31=DmN7FeC0%o%uytGOD+zB54_^5?>?d zf%Gw9gBDhtF^HENUSH+zLJ0xSE}->`*i2sm-L8-V4@D`%2D7048w8gAMnGi02Ol0i zH@)zIOC@)hcRF5Y6keW6#i<2z`@7y=xs_G=#@aywaipx@M>`{gJ4!QVnOJ*5X_%}u z1xLXrL7y=UU#;@|$q`>+SJ@9Mtow$*u*9tUQq;KgTcKRo>jPpul&d#>__k-Fzc`CH zFVFP&I{uXI)c;XT-f)n9==}EWcjwm6Q$exS-DpY7){XCFTj(f@GZ1_P?q)K4x;zlJ zPU*K!M>g|<5}u+h2BQ#=D3Xt^()NHHfbyHx+via-({v;TsSo`v`g0`j86-10ygrys z?k4A~bdcJoV;NA{-WCKI3?BWsdp#tx#?cv&Ee$p{G?RsdL3 z>6edeghcVJG7i4BOATR!pSp1A`dG`LD|R!ao~%B=k*dJd9wb|-lhj`mg13oy5mjfu z1Uy2vi(Zi@1Gx`VTZHXmtz5zL8|SVf=}L!B;h%J%91~Tw}7i3lYI8QbNT^) zmKa@gh6ABU96xb%j-iobOGd%Y_7_$LvX&4P?R%wk_D@?bKUH_#{0TzEhd^4yXjbMV zG;6=42}?)Er+M_yRwB7JXc47YD3jFa_lfmFwUi2uV-eIkFck^y9p5RBWafwwp46oI ze!(k?U0CFX+lhMT(=Kjzj&SW3Czan|F!$WkQU_cM-w%0V?v&%$@mhs@uuTl6r#329@BbMNMp4S&Rlr@7$xkDE@i?~*!t@iEj*#`sG zvfO%Rj!t8KIV)X#GrZQz4v=ZcRk~sw>>l>`*?~FDj6!jT&`%@uG9>?V;7h?$)zAU- zx69atvS|cIo7NH3?o6>~#8gGWUTB5JnOWepUqYHpX-HLZ?=>B@Ihg3Rc<03&_JjIf z<2IAunjSn`$vH9(9>z;T*AfY~ROKl95$p7x}*J(r|fu5S*$1-e)IYRpA+iS*sL}SVz6NTTpz0qHs4rMOzM(;mFD6S=o>2YdQXdIO7zB@P3cl) zD8SS~(zMkVi9miePYm%TcYrsicd^&^_|#9fg8HD~F~Q9$?y-{R%ayBScghOvW%<}s z`%_A4=U&s%ky>3guWV7>XKDaOq)cV+(0Zwm=(g|?T`JsH{8I<8uBhWa1aW_4;=-Wp z9aBn4iGzq`k31x>t2~U5pr<`6o3_LwTjLv?ji#}y;YOqU9GOdB#D_q`_uh_FJ3}Ot zU{Lz24(r!ER7Tv7#(YZz-+roC+N^cEh@F|0sElxEphb#h1UKbf;pdHiz*-W6!EbjIC^w_Yd!i)qV)pK*b)s<4&1&_z8WRpTO}) zo#kEg0NMOo-_q|dc$gB6M%Aj1JTwmgr`8s&?XM*urN<#93U4NkjUBHhG6 zK0^vMpX)~=#FJ43bX{>FvT!6F@|Er+%wB9#nurWIR{3Hsb5*=On#o*c?4;(lWH*@s zqVzVN5i*3l@3o;}w!Dt-?@|PY-~gH!vHkVAYZ&;5JIV{4vAkS>8f@Kw3F)r3b;!~zl3>d&??btse??o>zRHwx4pN*R(XXjAPp2?MJ zm6I5h>YL!WLB!eh2KbgNXe^{r?7s2+TK$f+u>-&9toZ=;o%jPD3a?LG!e)G+Dvd!S z8hmfneV(s#aWYulFwtIg*&L@Xz^`h7ay-_FN?CnX9=n3m-@Zg94CelDa$6d zYL;fsBGOs&NVnc+DqE*D#QXfhW1$Wx+_!g@~jn7PkkJT7@~SS zQI*MGQ^rrX>XMPI%*zt);i;`wIU;u!Z)4QG>u=P+%CU@LMonPaLmI`^rC+e#Af6KY zy2C*i%^HoUWYhBQXo;0{J~SIdaGPGX9s`%pp(f6XMyAhX7D>ajGKK4=B?cvJdoc6ZR+G_N1cROTwSUNoPa zw|Xe5<2$Kd*5k%Te{7cNOobNvpya9EJ(rSVG8PrWpEI9ce;5>q-;vHclSNjiC%zqD zWLAHzNN?*XzH2mZ8=w*gm!VhJNf5T0SoglHBF^Q+O@<1DEwW}s^E~xb#bnpsyZ-m{Z$d&vmetK-hgf1Rz>SY+OOc1XgYXc3 z6$!9YCfoQho-rEZ+sHmveBjz$S{S}W*;q>GLPtgHAm%im?o&d;s;bz6!ta?NNsAG| zs=}kBt9*y~^81A)V^+M@@`^>m4uK^-#E1bb+;r;}^fP84l+;GycDI_G1U$!IEpYkv zBWN+slUn_&Wa(O{460!J2iaoQ56_A^!SMiwcW}H2W%5zF9ZuWb>bnMa3GcZXMPPrf9=%tpBJKqK4&Pvn;t6RAqtw5V*z%PIG+-9d= z@OiDe?e*gy8I!Sfw6kyCT{dC`m*`Q#iF-^K=6y<)2WhZJliZ@@n!UTJ62D}g#(a+` z3J(R+HNnhF{tF#`S3!dZe0>R^o`nB!vIt-(_dU7RfOUcW7U}h`_r_n(Ngd1c`r4-! z_i=E*vRHiY;BcyZX7fF;v8Dd}!0Gj{U>{T{f|EwV%H}{!``Jvs2A9+-|H2_W?3*7^ zRkoe&7(*{L!kb{<{y*x}jT)hMU21+JI$oOa58=AZ@Z3iYz6rzd7+wdk7 z?8!%Z#s)jjeHCOBfNjCQs<+D$GPp34klB1Q$ zkz2D7Bp4-h4n;_~iZmK#>vYOjl6oF*EY!Z{IVFAngU0zzM02zhd3AAAq`mq8!c+hG zg?~InMcn0HHO-o)rmMKB;c)GU==U(`rS^?rG8AT{PwlDbgp`d6(GpZv1t_Z}HR^)f zl<4Z3%7cx%aKd(()rVLC9MwTrdGEs28+ys>-*pM+_XVxAO$V^f;4Z}94A~TDGVNpg zWs&EWrZdPI_ovbdh){_zEop zs{ZQFbj~QuuFrm6att+ePCW|Wee4{C%as%x2lvyh)8wo-f+z0>@by1D`|keTHH1U) zVZ>wkhv1}K#G@Fmh1JaY0KP^3Qm-3o!-sr=;i!W5@z~piiY=11Z9sRNBV0Zmm6Ci2GNHt)IT8`YZ+l> zN`lN;aPK&qAGt9?J%g>+ntS3MJA4q3a)Mc_j%x&h(lurd9oc=6E}RjG7ai5s5jnhq z@|jF|7!)wRTo7YG?n(W1a7bE_eHT~Pjqip0;dtis4p4-IkcQrW>^k{*k~6vfTYUE5 z6jhVnE!|;euBHyQ-J+c8vA6|r=7n%gc+yi*1h=KeQ&Fv$8}2Ku-4z~1~X zia^EvlmEX_{|8=Ax;)`3BI|7F0TZeuD^3T~SH#NM5@aRv^e=#$TTsxNSD4*`kIRak z&yvTI-9o_1g5Ap68VKa&wh{pH^ZbpKl9M|a;A9Dev4S$^073ciSPOCq30YgQ3v*lZ zu=7FZu?txU@vsXBbMx^CKpAig^8by6h8qYP2>^$`YXxIv1!ZN$0|W?j@e8mE0=c=N z+F0_l1FQg+?A!nWZXiFem9?;i^%EUGm8jxF;HLuu-Jfdej10aMMTC82mm{~={h?*h||GzrGc@03Mm?~ ze*}w&s2ebtL*L?mn;(v0|hUPkVu$zz4|7KDDH#)Ju+*J`e*V)bIZ}N43u75rKm5>}j zPf*d&JS71UfaPECy8}FdR!=5?;`rAqOIv`G4G@}b{}F2cF%J4q+!$aX#LW%lvtqXv z7P4T6dI*3Wz%5|K4z%F06yg!EwC3fu{CDo|&emWrfE!TC2I>P)J3~Y3$<8z{pS+Lx z->JQ9flwb|<>C?H;-dS@yfk8*u!Q;_%M*j895pqOzXKo!OGzS1FwfKRaB*<}0p0!` znE!B;{|CCi&Hsl}{_m#$o$RmKGR`hO(4e&iYj`>Rm*W2u!oN7Ef-Hef?#};}>VGHs z3zom_J5Zhf8iVd<(Cv})pZnuK+yWLk{|A5m;cov2B|usKkC6WszW)Q)|G@R%Lg2qe z{6F6HKXCoG5cqEq|BrY5e+C!EzdCH76SUCtg7(i~F-ZYv?~P)iq96M)OyFQEGwi66`T@SWXGbE|5aq`(_*ieVjH zEBKhCGd|CaZ5Ch4D8(`rcv`ibzQ12^aDQLmn>YycvTpAvkH0?uxrB;20oL;`Z$B{I zxLdf!r2GHTk3ZAPZ4ESxTM6wKva&M(T?G8ZkiPwwwHQLrg>Y|@hBjn269ekN73BVOR%UwlEhdtPX3* zGKe2y>0s!Ga&}|1zG)T|yNAeu0&&FDsw(N| zMqQwb-Tz+OPH}^B=7r9AHw{U<3W|(GsE8c-)^{MXnF?uR&Kk!$lR8F*YNOsxN4NG& z*Bx>gEC}DvT&;|4xJ#_EG5-c1AH>;I`(9BnY`zJy8{}0;p%2dk87GG~mX%#mEI^7> z+(n)DC?RnGvz!ra0>G2A`6c-A`@SVtA0id+OT+YJMMNu~4~aQkU~EP}(82l!#af%6 zS5)Zej*DJZ(#`Y&_$6e;*7tJ>CElIN-UsVESb*FkkRP$)bgpZbZ9N#GU@9y6^&QX$ zO%dSI&X2O4IZ<5KtD{Fq(f(oE6oxnlX9mSWjTjNfg0E#xgb$KiGWL2O5}SkgmdxI`Yd|u63Mnw6JizsJ zRn3!NaYAkJO>T%0WGjd;7?(uwJZ!qcxv}9P26^C7?@#0SlnVc*V?f=-dZ^tWhfN-u zjqmZlh0dUQWGvuw&|yGvjD_b%je#2`Y&j68K8rHB#=%N9a1PB;d9)~d8#-lz_?A+r zL19v9UPv|rx2d3r-!L$Pww#kb;NcHK9wj01TyPJ6oY(ChzaZ!%JPgJ}4z#jE4Sv~g z^uZ-?8VKsB3zOdsdJtb@%f{k?4YqPomf+doU%_Q9SiY!$cfO7;x^Day{%tC_1s)f4 zE+7k$8yXJ)UX+tRGnSJLhB^UT9@GhHU;SiLC&rg3H&a_}nakc94#WL*9oZ@;@U;L~ zDe&%x0@V1$$Wk6eu4eN#iK}d3*Kit8*5sE}c=*Wr`1=WY84@@`)I#JWi0ayU8J@^4 zqi<#GEz@cwP}il!*d1q_P$$o$i_}(OYE(K=^}J zd0HzUhPDLPIqAK;u_wp&ge*h<0bL$ar8Yl-E72JN)R#QY0!YpwZ?HP?4QG1~dw6l? zcBmh|d#gb;g1J~1fS;=R;1O~Nex*1U2+w<_jF;sKF~xUK1xnGj;IE@dp3Qjp9_W zd&rImh7-udoP9eUB>;n>!j9pjCyMEfH`SSSdqbEXldE6^?&Oa!9()*Gx{A3!@0pVx z9lu|YoM`|@GlVm!XGlo!mfR_>(d5Z)#zguW2-S%4df1@gE(lX^=Nf06fgTP%=moYG z-Ap{dV=WD{6U8vkPBEGdfdlzIDK9}n=LcS*zBXosq6Er&BJx8eBXgmA|Us+&5!V_I9l7f6M_q}Z&_^L8&6>eR4itfC%VMOfU=caGNUDugc zi{@jmSDD#8wnHs>R(A&S_4Cb3lJgBa$0uNgRn0%=s)`{^*OqfjOx7`RpOve)Y6y`g zjZQuy_KAF(q}2U=>2|PjuiumGjB&&05@p7AyfDoG6Yl0>XzKO8S6%`sC(mv|#_+}Y zQ7Z3QUr!$vR^#URfo^w4@B!0$YJb7O<1Z zlRBh3RQg>duabf#0&rR#eWk`oc;bE8+6;P7KXtzq3E(>$%u5JV(IlD0PKMd^q98AU z8fhK1wS0&|Bf(z7dJ|X^eB1e&fAP!R64R#mZTh3}fz&3Y`3C8;U-TlsU~&roh!G2E z0n<7rcyl7QI908TtX(2kb02Sy!bj{rbe+ccT0~NvG5tRMImIx7{06sENg*flp;uV@ z1>U}?6s?4+m{QQ2<_yb1_c))I{nYmajDl%UbDf|i3Af9~a_)GD@6l*lZ!b9In6pn9a3qCTF^Ffh zL~OlGY+Ys?F(M7_@m`tieuI+sUaYu+Dnxyxe1cDy$>9f2L=a)np$Efz=-#V{+W>In zK5MbH3c3xe=f~}ji79S=d!1HLCPBlXuD~&?75J#G8qjugflwEZ4<<-g5ca{Mf~I-kW(Iph2EhW zi2Gjb#i2nHj@Nv6*>W)lH!eu^E$tI4R%kfsZu*qVo*$iLYgQFD zKRlW=2koCkp;aXIuQk!qiJl${p%_*dXu-0Z?~BT6^79~-Amnm+OoYQ7lgO)**)cM` z3RUN|8A6XN4DsgqUGbb_jDJ|VEEK4Llv5+!_>yn^*8bC4hqjTY$Hpt`p<%z`6u-lt zNT}xt@KiPbU=A5TwjtTv!HXox%#yKPLf_dy9rujwjHmbo0plf&0nYy?>lGgD;HMuAEcpP zeNe}&cwTB0G0N3{!lm-U&4x$^Ym+4Ubb-Xf4oiqOM+r3Y@X~5$tcLsIeGf8clH@d- zN%y1USf=x7TP?=V0fhSlqvw9^zUhwe38+qUEvAJ%Whz061`YA3IqRU_?dk)rOcu-~ zZE`E?6u_nA7wjs4hWI$;JU~ECzO_Q6Z=EZ1OjYC3zDNDhfSk}s|90JP`RcMJxT(vh z5aH_~qVDcG|K@A|sV9r8AcxgaoJolk+G`B9uj)}+y8}a`xMbEciVZ5QDH)~X+2?7U zvPpp^jtOqt_>4`X$)sbazLe9Gl*iaZv$GC&BN1hnnj7;nP9^*f71)zSIzjL`dCncM zya{H%$J(5mTSQoTL%n@9h9;E;GWj$6h0Jr_ef16HPTUyU*Tay{II0pI$C<0+7k=xq zjTPeg{8{PWt^(WMZMGl!*0Vwok8ycb!`%-o>R4WG^Uv49s%CIzUcwZ9d`E_wyEw}N zPzMMv3jMxWAib8zs2{vtUy!o3kgg?Qpa9JXWyURz0O9t_R8_M(w?4m9&_4bB$^cS} z|2FmVZEVf8oZ;gm_9WzWyLTnR^L-OeShuvcOA0XpRYQJr_3wcgd?Rl;n#(2(|EKflJ?%fu^C&RpR zAnx#Y>f@p*u?E)W9{i$qP2ZTRkAQ@4ml<1jnRCyRffNiW5uD(ta)y3ghYFvR;aD`W z222?{bWNV2Gx1#1MkJc}@TLD4M`L12Z#(2D4sd7X^;(zGEn_L9>O*gP+|HtJt`CPG z^cb>KSkOHx5u!$dQ&ncMD!!H@VpIgWe0AnZ$C}M_wz<1f`>A5IFvO`vx#wjb({~Wk zTJmb7Zh4fxDTic}88Tm>VT-hopVBbt9Q2~$2wK1$A2%0nlE~NlX z3DiUKrmNUcrZQn=2MRx{s&0va$w7jCDaiiMTNx)P+2k}8cbEF0dZ6Q_| zQW0c;_y^~K>*e+o^tr9PRMB~w_f;Ye@v~n{@DemslH%OFiQ~m%k#|+f7Q_Yf%b>R{ zBAL`Z!L1V4+>phf#QVn{5f|jMDQL50ucr^IgjO6Z9zjF$m)8i+s%~xg;i4?`s&aOv zAGWR32G9`+8@jk0QBmOjNd5jp_5J7jjbmaJo$_j!bsLp5M{*G!ut=1_ZGe=H3K_CK zWdbj7@dQE5%vgLUkvH*~8nlDc4U>aa`N{dNYiZF<1_&A&Zf*Je56wKvrUeL!7${i@ zTlGoa#YHX99#DQ0o8(*vRuU^mLoGk z7HVBSE4wLFLHe7)=?yHI+^Xw$_Mb+t1azy@{mmBI$`Q>NPBz-LY)GFP4aUl9iFv#* zGx*PZoU~&pCYxPIs8QTEP-=H5@1(@;X0l?rLtOvOMo&mt z?n7(U_|KRhp{q#Mpm70XbIvo+rnt~aVc=)^%K-d+7qS}pLPEK3Iv;Q<;O_ezEG(8^*3LYnK#>cAS)->?j%gR0*Pg z1tkSjhdic2Y4zrbVbRfVJMrZ_fPiYK+=-I$Q;cU2_~Y>EF5fL#Hd z5QOARBGzd4sa_A{Usn&1Nbpve@y~w~lQIbm%5HjXTvdrG_6gs3m~m3V^wAim*j;iL zl40_N_Td76XU1h>=Gz92xy{|0ehU0XJ-|`b?}^zdG}~Zo$`x{9*?tx&>vRAHm&=PM z3#XxpmkV5~EEsRDK#>tO^m<>N3u~eLf6#3vg4n#BG1lPi7QT7$zG~T%<4g}J_gKn& zW43T_g7FrUylU~yQ?-c+#p#*1J=u6Oq?>D*qrtDAVo{A9=yzW8#g{ZWM2LW;K8W5wI4Wt;E9ADf?$zOUkTzYB6&Du=W(KrHBRH zH@To8TOy?(=z-nLIg7a(8D<<;R7{r$zI7YOm;388{UW*Y1%TI**6?gilY#7t274tP zGxX?kD_CX5IAD#;dPPhGqWkgN&M)a=`W7bN3i-7AcFolu$EmY>Mz>+ZEZLQ+`&4+( zMK?ANn={pergcnJ`b_SQqn zwmSIT7&|$al+UliNuPHRb%O8fPvGrV$YBQV+T+nCBi)n~vR^#XhvsQDQ$B*ALA+2Z zsgFej!tB&`tbKU0q>P`igbC}uRMmgc3wh0|OBVs{KR$sE%bx4QR9Yu|_6u48rtgS+ zq;FyGH=3{NXXnLCKlo59VoB*#5u>S*7d$DD;^pigBo0U^=p+r#YVXEV$=k5n&AXkH$h^li4kQov?P9>s*3OvN!F z`x5@r$+1^$MmEjZzy;IcvSzt-?Io<8(4oS}%Dqzl`1K3rX3h$iNkxX8M>8E(M?ahB zL5OU|&x*2vR?dlHy?(T%;w~E4410BkA5KE};mXk+h#%=wHzs3VdVlU;=jy-8FgDKQ zCiT^&W6G`}beN{XgEf5oEk#`jiTt`ILd1Ty*gY9WOEXhPf`rhf44@yD#)Nr<;SO5m zkb@krAU+{%O`LFmN%r$ht?GW|v=X>m zc`+wTJ4Tb6KD{*&=+Zw8FS9GX3SaY3GZQt-=T~LeP^P>Y{9xS%s|EIjiOdx;ST&u# z@s)WQM6`P{R;cpZ)e|z8UGHOUw_H zNtXtHF`eSBHb8DJX?jrJHaEeGWUEz^;cnU`(L&ECU(4Z|4uFu+O2YVE$Gq`RdU4X9 zp}bJBvTz~TMACeSoG*|S!!F+fuT~}({$8!p?2=LMHH_F`O-+nK{X5HoqY?#1U(B6x zbFe*Ij+VLOm34X6M0Ai}FzFMaY;aQ%v77Nipa;uEBk@;A|Mv{VFxy{3H4KD?UeMG6 z%P#ef`$+mSRdV7C^C(MHGU}zTD%rU#O=b=5(XJ5m^h}=Q9V^kF8+~0NtuDNj&&qiJ z1%dyN;V1G}nX!h2DA<1g^=5F!v~aG^INo^nz{MoFw^e`7X+-oS3$2apa!qGSNk4J)KUcR?qf~X6G3Ik z`HO{3H9wixlJ2>$?{eonzvKDmk7`@RI#3W~KX`h5z~q%&`X2mS+>(@hbyzY!hTJab zM|%1|i%pryapbGUt^wZWHTP9{+Z0w<$iGUsq1dj<;M@F-k}Xn#eGDvBSadThu_b%a^QPuS9yTE#a{V#lIJx%#sy;_J%0>gFHs ztiK9>@59;jg5c_c61PFxE{WTEQ6-*Y)v53LES$p!*RiD10QMef(5ypx*x32Uf#7TX zY9=&&lsY;NA*wH(3@`YDzMzx3^;xs}k-KO;7`P*%`VOqO^!pZWHN*C2ie9YR1_QxK zFMBqMRfR#3j|Ess`NP#*U$Ji-)t~PZk08zE> zG5SNUI}f6dR&v(GMDyua(M7ox8893kKc}%QoE-m9YaBrQZnK8bZ2}|?{!b}IE9FtVxNvB&+LQ?^BWKm!HS|K(*#u;Sxm>kLsJHJ%r&xT6VaxVpHj3n z0mQHsNQ=p;TYS#RnXV9-@q-@a74XhhIaN?2J`|_CqQ##R~Pu?KQ>NGz0Vikq`%Tb`tItmXjQT!JkwVxMj)jBm%G~sxQfwzt25-s zF1>gs#bo8^*Utjs*#tjDt?cI2K=^B7h* zCI+-HkA`83dSK6UniA_9085M#nM*v5s&O%HwT{uBC_H68V3LCme19sphug5(*Xyvk zRe;uQv(YN&)}OWr8$ENDvM^HffvU2DnV+vMg#|&(p)X{AIxL2z zuZ`uLoj(7XvC{Ci4>+1}6vYLqB{e&wONs@^PvsRc^a@8_tW!^!32uBk9mJD;@7a72 zh1Q>PKr=siLUx9>>3z(exD7i@K5&AcRJH#`N?6Az`Wbo@C2fCKvM14c)4sZC4w|(&NJq{P^|_ zI!EHtm$8;Y`>Vqj7Xj(-#hT~BjWV+f5uI~S;HzhtRbZx}UYkW{h1OdH)>vQdLi`Dh zW8H5Qx8bS@C90i>iJ?oz$U&q42rC$Gys4A8fSwwRjH$LI>5n6945W2{#|V^i?rPr` zo-U{xB@Vs`v#g&bIZnDk0@>QLW7YOmaP934wxF(%!;YYm^vkf`LN>UnaW-In>A!!a zZiFwhI@8W5TEuM(pIYkZ&rHe2+C?hAR^AOCfoI%*;@h00zh3~m{=JHU8LMXnB{aKH zy1*&cvOxz+2w-+fvS=8-qKSw|@k8&Ke!RKa5_j+=)h5PY1#Eym%9Y28GPbZs+_-vO z-W0U~r-#i*v&#}E?s&PX5y0d5!2)=U%V48V$jde!X6~1@o-|Xkm7{8PJiH+?fu8s- zvA`k&C#A(8c*1%S-p`Rbvsw}=lz4_E!Pj@>PWl^qFave#+rNG$dcgoNI`~{GAi6e_ zGUNm;tt&sPav#jO?BcJQvSDVERq)Pk-*sjVzLwjM$9}-?F`OBJqhp?})u-FfES51b zJarzZa_uCnFA^EBnhPw6i-|H!XVz)r_%Qw zhRdCO14{$=!`X}&*I*S}Q4Yb;t|lqZ7kr4iOx%)_q1&qoVX?1D^$zijWvYJewL4Kt zLkn!Eb{SXA%h9^+m-hm39ZG&ug@_!=CP&G!nzYb$b)C;t`%DGQ7h$FQK#I;#v>FDQhObslR&nv>;6DL0>1NZSM=-$8&t(HGk&~&I@*FUqKrbj|`3nwxzl1uTPZyfsF1TlHU-tu*T z71?yx(2pBTH!`-;BlVVfgcbaai$+gFO!_sP8EaX6zum)1ce-OYg%)QhJkX>Kx_|?H z9~y>k*Gl$3{em>?IANZkh>NNp89^t)s54|6I3~t+$pY#gxJHa2U3>yO5p|Il@nd|u zUP{rTGNb7jbk|lpZLNb89dxk3_(yn|a5_%>QkA1e9jU|?qGg?RO#jnp#`dW1Ck?BE zaGdQq9bxz+Wn$vtWK zTBgE`%^&Z^a&xz<(GdlDRWO&@G?gY#y#qbNh3-ivYzJPFki%dcxc-xdl8xx8j1U`)4j~#s*JEaJOGXz7eZ%qUNBk z&ai%Mny4scvd68D%@a9T5E9YZxmo>v&xp7O(Z4hcx{bQZP`p0VMM#{q?mTL7?DA!I zwLfeKel6%cg;282>`zjpg!6oO!1<9ATyf)seH{SbB;%|~x5*yh+F36C>Nu-1p+<2!BXn$12Y{TQyFj`mEQQl9A-1HrMtK+nu zPXPf~OwhQ2x1=TJh3I+&9$3@o43}tg}ib%wH-sY$Vvv zieh2FzBR3j-7K|EUZ5%{C6E2MBJDSZZ5)_j4L*MPTu)(vDB!#D8?iU5Cz>Sc z-ptN-cqSiA3&y!BejfSuFkYY1#ohW{$kM^${mI(z0$a8LZFO_|SFCYO==T*k%UVu3 zA&4LRwVD0_UF5r(51M_!>E>Pgx-(*8jebxGic9*DeMQxa&;87@OHUx1w?w9e>7m~hZ}oumWK6cYxe)bIpeP>e zs8w_7D1PmCOAkFCa55J1G5M_LlR0}id`RwcIB{d*RBR@cwJJPvj87{No;yhtI%P_7 zD*WF{!Tkp~a7ltAiY?j(pR|L`MKd}V`-nQhsQ!(YtbJdLBtVtsS-YrK$!BX4>SwbZ zjUAATu+7x49{vO=O5O!fb1UL>ec(-)Z|mLsKX35=?BND@_1l|mCkEPVC0m3{I;dTc8edT+`efvwxq}4@2xElajr*h409iSKY7A{8lv5!7 zz)MaC-_DjF{#J;}1Dd2S8lihC@He6E?i#KV_~ImWChgagPPSGBaBGw|EO&_ zfZ#B+o4S#EOHj$FA+kG?;a{%2nyw{BvA75E$`Pd|(Q$RAQTS|gwWi|Ug9wp!fKwd# zuPn{{+kQXFFz|D!;cEsLLo4W?dpS_WhWvQJm#k5P$_wPV3}2NyC6X$ieB(}N$*O!K zhZzEQx^cj??Ob%VB&-EiyjtqvMeF|1s>pZcw*G=P{YV}C=(w}oJU-$JU+5nDzB$!D z*Nb=CKe-bSur%CPe{1$xJnZsymw%Ip+)@-Lx$i^kf!;+_2$X1CdDvb2i5LG=(DkTB zWa;aFY%9_!HG9r?Z7oDZv%Es1(9CE_ExeeyoD~Ju$M^0nT@oKb%4jk z8jBKpd&eX`KG`^|rh8rk zi1zz~2gS^DBq%|SAgndJCu1dSKv+fKP2v1HuMG3FwlLi@^k#rkO+?WqMF=G(=Mq)*vo@s)$Wq}<0+82bD_L-XOT+Qi#^iXKBr-z`bC=L|n-;x59C+UX&_S2viebs4_6 zsrg?-xch9)o2j#z@e#`;PCCEiB}LiOacTcXr-av3)USP@4%DpZfOC7HLOKJi0s9D5 znN$u5S$B%!AKBe4nI1~MVC=A+B+%dN|BFsbzkXEFbA?3CuN#HuH1aO9qWM1E&aGzj zTq8c5sw>V2(@Rg+XdRnsZ04;@y*g9Vx8o#>x1tWY(8)iHaWo|E>+2j0X=RsI6nCq{ z{#z`)zPt4>9?SYjpFFHGaf<~qF$_c%4@yIq7N1I{bJ?;88j?n z*DhF_Tx3+aHMA=YXRU*=7^v##&D1~k&H<^70R!9DS+|p&S^A<%P0`=XDJO;4v^unk zw~l(}CEOIIbGLrRc%KoZR>taCQg@do*V!`Om5Ih>!pl06Ou@~)Pn~jQ0Mk#4$o%NM z=?Ogff9bQB`8s^c3Tf;~2?gMXzGan+>2J}A7w|XJKUZQ&dDbdYXc=Sp;%dx7EqdOcs9X4j8OcinKwQc!_Zca&mGDz1(R_S_I z*KgcbF~)DlIKWC#+Y)F~x7ZWeD;$_jZxD?yL7FD})EI~TJF%htvr(~|Rib)2FZ1Ze z^osDc0tX=&uyc*zps-hh3PDL5Y8u&!NAX(BPIPC|fWeZzy&IR&LO(g#Jj_r^ITYeY zR@VpOTcO6)0PbcsOdulgZoM{>qUbHw4{@merj_d=h$?)Q{M>Oqy4nu;5Y>xhIc^`0 zYazKxFPNB(cJD$i3B^SVs;a=1H{cV+^O*~lQ30-6{Ux2#W-AZ;Eexs=sDW#v5LeT3 zBrM57`662tR-5=K>tB_S@Xjt@eWP22cV?bjFZc<$H%Z5<;iST=5oYNOCejzovceg@ zd6-l5R-rO{qGrvblDlk=37jSiDmlcIOIthj&vCa9i$1zhCMC6(S29YU+f^AjepHV{ z3f$CY=?{WMJV)K6#|6$O=XevS=7__aIaC5XVdW^cEg`fUI73z0+N&dX!ku`E5ZU%F z3BY4JFbJ5XXaB+pSVRu@FrG>Y0s~b0LZe55Hx=G#Ezy%?Z_zog)AnI&rv9tBHZvOF zBXwZ`WW;ys*RMUZApu`x?c=d9E{;?UJNQItdFDiCp}(Z9|5s;QX|(L_!1c*%O|ZT? zQ^vB@d(>F(`j?3IA$1nn+Nx4{b4#s~%fmk3FsL1!d=0-VDYQ83ITz)wR>tp|i*kv$)W$>X(P*qKwsl%i=A#dw#&Sg8IVZCuKSwj!IjCJ~DW1KD?>dvL7KUYC&YSP<0~ z`m)Q1af|K5;$E=}8qCbSG75&ZSn_`fTR_Z)OFW)&O`NU{@`#)kmm20s_9_<_K`1`R zols*}cID7=wIP#F5d`J*-uIk!76+4!o8&WbO#uCUTIF?Rli3sB*8mI`axM3SI^S`5 z@TxWmdeP3Ea3SNXA$<~`M-eJlwhJWq$m^3pJuv}M%@#8eErDE;Sx96x-@7`1^Q>!4 zjX3Bv)y=X!UqH?XsB zcfuHr3#_h@WNlv$&9{&cb$kSv*ap7O^e3piKwRYvhL%+tMbo4<*J!xJ_|j_PH>1Ba zokax9Zfi(gq1O^KIN;?>Ma{@TKMrRCbk12k2i$2pJZGNqGak4^aHMyQ2Dk$ckJ~lw zB>M{`Ku-zG{)kJyz~8zAO$uL1*6JPm+J5Dr!jY{wQ7}AcUT}B;^c0y1!@v+$O*4Cc zwI>XveM0jn%YydylhZuANCg zo&Ez+g+`^WVCHcxr~Pibla5P}c_U(lqh&urso|#KEp5-FT019E5 z6s6lAI=l~WlDxN+Dd6&rE|;jLza~ovWZA;^mL@>d8$)TM+8s!XLUwG>EP)zoU8Z2z zpoZeBbikmLIj271D}KWxJ+B_40m|nKiJ<=NjDGu;@ZiS6_^!2ICwGG5FnDkq9b02! zJQ~fw0=}$ws$!OQbbm)tBU@Qwye;;nINzfm#wNhbG77b`W|wbV^(Fj>$NHh2@^dl?xxZ(ejHjg|xw z5DXB(AW7A+K*gOqE!Z#Wh}`l4Q`-znasaW7XMTDj0KRgDMAlib@`i5JDe5U`ljGwylX&Y0f2u2H^YMzPH3u`b-OO32e$M*Q5VTD&QT3i(K?D zp@dV7HVM)78$^c|%v%&GzV|s`7a-0Yto;-VC2E4&`=0Q+t93=2ZJZ(xA+y6I3*u-o z(CgIILHS>TyR|@9YfTjz(q~)k2If9Gbx{uPY=|zbpH$gH{nUiELKdoOv-6A{Ir#Ul zST?6~yHQjs7g14om?@9n!&%{919s@n$h%k6!N;5SA;_1tE(8H0otdo^lVeMKs-OM2 zO-t@FQ&!Tk007r4dc-_BgbD#WWF8$nCbjUpF=Mtpwc| zK{@ADjpPjNlgk}&4*5)zoSJW8J6VIPDBc;}zU#KNeqc^+o4ppZar z!HSvZD1PLjVCByCWYIyt@;JH{RZTE@Vj7uxEL-g06CDYZ5*r_a#vrPlzYalO1IS|9 z=!OLb>M<60Qz;1x6ErpKZsw72#IA8f1s*)A?XR}*Bxs=I-@avbi!c!yKzR0gQEYy7 zh(;z%*26p3lr!GLkqY2S+ZWRC^ER-U zT{(xMym$wq*a#o{>_^tfMo+bV`(>(P&MbMYUEm>w1!b78vWsOU&}s(FP!iRA+QN5l>E&$U zi1*$pZi?@54}@kQAoM}wTW^CNYd_%q7pk(Rik#4v;qJEkf-M%@SsvU5%J6_7UTzS! zjZ2Q6fFTtY#kXRq9Zz0Q{44S3j77Oj@TuBi=hr)Uz8G^*;*)#)YnBUGaGZ=Lj1E-dGj5# zfE9GVcfxTTM^0$qdE-uAPJCXQC?tUOpa3p(S8mi91zlr+uak5flFf*!ws#N3h7Ex= zDgoh+7V_@)C4_SNN?uHCoJlHB@4wk!RiG2kw7Gk*P$jYFM9czfp_ zOFT6qO_S^-vV$1OyG1`tyiVpK*lQ4JMNhMB&CZ;JBawBx%z>IhmBU#Jzhs0yg{6!J z&A>Jp|HFIgG0HI1&?9x=igk6p;c{Ypd2@6fQb$d4U+=ch(Jp_m;}w z0~sqX|5O}Qpa!i^iNIc~0>LU(U0oPo+Vhzck5f*>fr!8a6A;Qe-Ctk5ix=!y$-Pn+ zSw6dovJ87^Yj*&c<-iHtHC6wRfc}oE&Xwb6kK6_&<#YG0r|kn zb9=FJimJIvU!S#NW0JJ#A9vSTGcL9R2L;LX9;0!;HE5QmE>32%uro`#w$O~IqgClM zchSEk9E1J+W2ss*ygiGuAIFNg`#*}X4xvnqymo}MMVyi7Wf?llwm(X&z7TxD)##aIp6=oPa z&cn-EFU*imoE2kds~gu$6+a;dN6zb#2$6-zV$1mVqBrNtgA1&NIjWknEtfeGb*#hQ z2sbS#2=I)sdHnlLY@Jf8q<<}qTwPs>99O_4YDQ#5DWJ)sUXO7g<9uNK@ygL@vBw3P z4}*A#Ib46eoPOxf%&;QcGBMUL`g_u;rlI0vw0mR41W)A@1Lt%Boc(!At!n&WXU)uZ zlVzIj^N)gPd8G;>h9JSJ=g^|iY>MMKPt>(uYscxO!Cs!mLkF()0xmoG^c}j%l93q# z{;Y{^-!Z+AWCx7HHhq~Hhr9fJVQ#_d-N5b1KS#KgGZuDBzSCOUex9}B2%#v=4Tkg; zPN$oUsIi~axW%j=$DxxOMj5TP&Z~I8>CLD;j~`5aTZcf1ov`l$s1GU!+;`vn!Kr#r z_aP7a4ue>-Rb=gy#uLLY1di7oN6Q~<%UCWq(RS8TB_&9?JwqBQ%o`>Cj{M!_pH7_e zdfLI!wrEH8D_c-%2)CVOI1DiIF62xLkh~Yd7L#u^E9)Wa61{;QmP@{kIfRaV?K7T{ z^PjI{X1ZI`BaEkh2_riNb~Nxxmfaq+cnbwBtKhVlZpZ%pTTU|kE3mfcLLcY1FC9+t zKK-yO-V%IXMT(y4skGu4%ov=;pk;9| z>0_dX%%{Cux#jKn3CjLW8T;jgnNPWU7cJH&q5$ot6XW=gbKP2O4)>xuIG}Z8$Jf$( z$=5JPtWW>_6zvNOza?LPp<^+YM#hv4)E|0R9VvSvc8_ES*l7#OAqJz(S2D9o{43PA zOZK0Q0CwT`Ye9E=D?TqhPsV=q?rax|3Ikx?T zC0vN*ORncgMRAByN6WSEo2aU#%91Co_SA+oYS9I8+8db25>PP<$VPG!f-77r zh&SG=XN*+o-@+wlJJ-C;WA;BfztA^PT#8am&NG+7rtMswsU_oZVVavR{y$ za|Ous3EmINj~b04hrS4E?T(sonRUaBwY7ny{xG4rpK@3m(I|`!L{N81%F@_Bk9`H@ zukej~t?)}r+Hv=cw(iHyxd?0_T5&Ano&K~zsV;IwaEXub;*B#E54B#Ll-tW^>$>@C zy$_@3W9zbhF(e#(Z+WEFC{u9{06DHZ(fcj-L*NNmbxlJBl`h|M!^#7?p48biWm_v9 zu@RO>g8IEaQGW~(hwQ;rKO}7Y@hkDBYb;&4l@Q~fms(}^JbD)oI}y7&QwGs<^H9>N z^=HI3wh`cOcX#6_^(gCKR_XlnQ&E9^buEEraQq>!Wq2Eo?0jfaMEXc{he0!EP63u; z-Fx_t(TvSAzQlm{j?A6YPy0?uCkHjr)A*1HsV4scsiKXDt(!`7ONF7+!Uxcn_vg6< zYFWDPzoUr0@nhb^vfo`6K&ypMh%qieQDXO_8_AC< z+HpN)(HwoHJFd$2f3B}8-2D0~OCkuu`)uIhbuwI)3gT>16^XT9jAGLtr5IMsP2bRl z&xbsFCCQ`IZA=c1K}7F2-tVu)43U0LR#XF4s_PH>#R*+5yABonQ+5m}IO9{@TP1UI z&pd2NU4xv>mwXT5S-pO<2J1bxH7^M7-Dt-_`%DM66_l77#!H z(;{AnxP2$esh^HgV=#j3_ggqNU}g_`g{&K;>QoYMr~?&(F^$!a##nxG>5!4G# zaV|5Ag|*h3DO@|F3Z7E-q20vlOH++GliUwo16_V>CqAGpK7exJ)pUOn!7auHf(D9A zh+j*pXA2n*2mMeI$CgKs^PMd^N)y+ZnM+4vVZf@5XDX> z)ZLEO^H@53;fyc3F}0`vs{}9xR+9Qc++fAPMWirmwfr){_0)HjX18bpdr9(GElY17XJENqs1pY=R)yk!m7nfrw_wXf4oUhIQ%J+#4O zK<0W@m5>%l{TK|B6;ttv3Eahx#M-+0R-0SKV8d((B8E89|Qy5!re ziI82VJ)Uzxg(nd@o%5|vr=o)B<29sv+Bh`g%siG5q)ZML-IAS_cbr0_bF%#TJ_zOg zHAZoFb8CLbLmwzCcPjlGdDJGzd<1hg`*tEouF3j3I%4`URjmDMl_lTkB!>KypAH!; zPV90U(FISu7Z~5$Gfwk%MH5-Pv1(&n2eoUGb53~{5z;+P&HPA6ELq*X^Y^Utr?}R+)w<*T@Q^!_ zy4SsmBmFS({b!l89L|Nif%f~)uVV89e67FVwQOn3tz(B9STv*k%}D)IzOVbYfs~o+B);rutqfy+d-^|MY@Lc9woX5-~>UC#JB<+1Q5rIZ^QN zE!LirJrXRT_h?@lNTZQ$@ggrLvhKH5e=|-MgE_x;#6Pv!Q&!Uoqj7TF0Q8Lx`ElmB zI{qV<>30=RN}lsGp(5C(UXOp<(@uceKjr~37o(gSF;lR%?>$rVTLk@pE`5OGQ@2CL z{O1^5^e4e<6Vffh%#-+&MNiJ{qWmzf=-TeO&CO-V%AI5pX+mX1m~l9Xk~1!UD(Nt!lGvnivb!Mrx7iNs*rD{xWY4S{|UIIs`z> z$cHr{sWnNS)B{3I_EGQ)FS}KCnC^`N)B6u0Dw^dSz#-ToSzfdT9;uquTQOMl z7UXQ={We7PMQ{x((>u;ax@itc)V@o0vvB5ubK#A9RSLUvp;s#W^{3q>fM0LHCx0P#K zeQ|12I)5NJFzk|;J6CfVc^E(@78o*f(?}cHGH3SMPZFJEBW-aqWtnGvi=ErPUIHg& zHhH&-vcV9qX5wE0|T`$H$z^3*xVxn3p?8!`pR#P6TFrRx zK3QPAMMJCuw^*yC?WI1}>pkbKV*@W$?0MMrk5W@{Q_6ic)*SErDfMmtv* zyCI5GiA$5=r7Xf?mGLL|8*EnFNNLJPPFmiKVv`Qpy$TLdt(2K(=^h+>cpyk2!;(Cv zgV!pjXNJLXy%4^p3Inf-SR`a=4ez4L0{v%hHcgX!k9OCO$#@bB&$hXMHQBhh46OCO zCU?H`XD~^;HM!Lgrd0lSgb=+?7$C75I1hny{?a4au8FH<-nKKdSI9IEM~2LP${7~n zH{{_*j*%Knz!epb0x&RL^|?MP{NuQv8|xi^+PV&9;4~T+N^1&c$^+-btgYL;q4+Qh zp43Xu_H6H7*RM9Wt!;m1p#)KWxCO(MB0faY($YIL$8(4zzIJBz!yPUZ>HSn&eCoTx zMcmLqSb%EAlTdJih)sUq|t86J)Z8DQ0xM#NH;Q ksE_9V$&*|kV=iG9L=pbM-7!Jnl`@F@a~0{*r)F>d4~HHpA^-pY literal 0 HcmV?d00001 diff --git a/desktop/src/main/resources/images/logo_splash_testnet.png b/desktop/src/main/resources/images/logo_splash_testnet.png deleted file mode 100644 index 00e44cc5b1ce8ef316367d1b2a6e9801e35f94f9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15693 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ʓe{JL3z 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 F{{hY4yx{-< literal 0 HcmV?d00001 diff --git a/desktop/src/main/resources/images/logo_splash_testnet@2x.png b/desktop/src/main/resources/images/logo_splash_testnet_light.png similarity index 100% rename from desktop/src/main/resources/images/logo_splash_testnet@2x.png rename to desktop/src/main/resources/images/logo_splash_testnet_light.png diff --git a/desktop/src/main/resources/images/ltc_logo.png b/desktop/src/main/resources/images/ltc_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..0c7de040771baf2148fa1982446cca9b0ffb1ccb GIT binary patch literal 77610 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 literal 0 HcmV?d00001 diff --git a/desktop/src/main/resources/images/red_circle_solid.png b/desktop/src/main/resources/images/red_circle_solid.png new file mode 100644 index 0000000000000000000000000000000000000000..c5c182ed94f0f197bb3fdd88d4147ea02fdbd943 GIT binary patch literal 895 zcmV-_1AzRAP)gtZ# z_=a$=mE)3;31Hlh&?%yBIA_79>ak?7niOI^zkAIfZQ z4su8X*Fpf}5XB-~D(1)%w8OuE9cX_tJet198L}*|=l$Tr()7$f*+1?mn(Kf^A`n7g z&0@@d4d&<12%R_q=jgBZ)03GFmMi4eYm(ZNyLmIIOiY4TtcP%2if2KQHR^XmcgD&J zqStRATf6xC+~h{dI(T*lF2jE{p44?|Q{fe}2RDt^Nsk>j}5F!&Eg`RuPut!lq&tjm&n|s()v> z9&}kq`GmOQfTkh;DSJV#NnsjCZx*4FXasi{sL-&lveD|*U%Poy{$yiv93(u1w$=S(FCN-5xJ%ZzOH#avgG&D32E)`z{7yzQt Vzwv0Rr8ocp002ovPDHLkV1k6tu37*9 literal 0 HcmV?d00001 diff --git a/desktop/src/main/resources/images/alert_round@2x.png b/desktop/src/main/resources/images/red_circle_solid@2x.png similarity index 100% rename from desktop/src/main/resources/images/alert_round@2x.png rename to desktop/src/main/resources/images/red_circle_solid@2x.png diff --git a/desktop/src/main/resources/images/settings.png b/desktop/src/main/resources/images/settings.png new file mode 100644 index 0000000000000000000000000000000000000000..8a534387f017b183f2e8bf63434b43de2747a118 GIT binary patch literal 1193 zcmV;a1XlZrP)e6SB{%yP@7+IjlLkaV;dNk(Q?^5iFk=-CE-UqF;%q6UJKqtJvK>csJIlY|yJgvM`E3||3pAe0 z&`@-p?7Jy@Nv|dj$~x#eRn^Cu?oq81k_~TE^@Ju|qrD2Q(^>HPqpF_zMoh4C{MnC2 z>>RY7MWj*H;|K-Vm4Dw=^?PAV4iA%j`c<_t23kZMb959J=2Ugz+i2VvhS^?sW*4v) zC4ypth5P%7$Cz6th6VnHbdQP-khR@1uh|A8zVx$}1Yy9t=NUF;En*5P;qjJV zfof6ew=1OC;>%J#P>==-+W{XH_-)|?$%p3xUigH( zI!609RrR^JSMX|Qwi>gAICo9ED^}1ILDMUc$&IRB?F;I)f{e?V;n(XEgU0HwO9KaZ zZ-JY6M5zYvIkhXSx91P}mF+4QJs+LkKmPTgVBG4%IRV~a%Z-m?OMU*PTaiz$tHtN( zF$JF)Y>^^VzYA^%=TXSR;veV`*4_=S-DS8zh-`UXiyr(GvJ+Pw?*=6dmxz|0E&(M@ zqUf>TljdI{o*rjd>EA|=b53ld%HWl|0f$mZ7#sX1!M&t65n+)&UboI;di>{ffA#?u zRDrmGFX0sd&@%$ufWxC9nG?J9!!@_~`jX4eVR;J)c0Eom1l*_b75+cxt&Y9!*L!Iy9^00000NkvXX Hu0mjfBIGvH literal 0 HcmV?d00001 diff --git a/desktop/src/main/resources/images/support.png b/desktop/src/main/resources/images/support.png new file mode 100644 index 0000000000000000000000000000000000000000..b40bdd28e1b50a4294638785a7f4293b65fb2bf8 GIT binary patch literal 728 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

    Example:

    ```shell -% sudo bash QubesIncoming/dispXXXX/1.0-haveno-templatevm.sh "https://github.com/havenoexample/haveno-example/releases/download/1.1.1/haveno-v1.1.1-linux-x86_64-installer.deb" "ABAF11C65A2970B130ABE3C479BE3E4300411886" +% sudo bash QubesIncoming/dispXXXX/1.0-haveno-templatevm.sh "https://github.com/havenoexample/haveno-example/releases/download/1.1.2/haveno-v1.1.2-linux-x86_64-installer.deb" "ABAF11C65A2970B130ABE3C479BE3E4300411886" ``` #### *TemplateVM Using Precompiled Package From `git` Repository (CLI)* @@ -195,9 +195,9 @@ $ printf 'haveno-Haveno.desktop' | qvm-appmenus --set-whitelist – haveno ```shell # export https_proxy=http://127.0.0.1:8082 -# curl -sSLo /tmp/haveno.deb https://github.com/havenoexample/haveno-example/releases/download/1.1.1/haveno-v1.1.1-linux-x86_64-installer.deb -# curl -sSLo /tmp/haveno.deb.sig https://github.com/havenoexample/haveno-example/releases/download/1.1.1/haveno-v1.1.1-linux-x86_64-installer.deb.sig -# curl -sSLo /tmp/haveno-jar.SHA-256 https://github.com/havenoexample/haveno-example/releases/download/1.1.1/haveno-v1.1.1-linux-x86_64-SNAPSHOT-all.jar.SHA-256 +# curl -sSLo /tmp/haveno.deb https://github.com/havenoexample/haveno-example/releases/download/1.1.2/haveno-v1.1.2-linux-x86_64-installer.deb +# curl -sSLo /tmp/haveno.deb.sig https://github.com/havenoexample/haveno-example/releases/download/1.1.2/haveno-v1.1.2-linux-x86_64-installer.deb.sig +# curl -sSLo /tmp/haveno-jar.SHA-256 https://github.com/havenoexample/haveno-example/releases/download/1.1.2/haveno-v1.1.2-linux-x86_64-SNAPSHOT-all.jar.SHA-256 ```

    Note:

    @@ -206,9 +206,9 @@ $ printf 'haveno-Haveno.desktop' | qvm-appmenus --set-whitelist – haveno

    For Whonix On Anything Other Than Qubes OS:

    ```shell -# curl -sSLo /tmp/haveno.deb https://github.com/havenoexample/haveno-example/releases/download/1.1.1/haveno-v1.1.1-linux-x86_64-installer.deb -# curl -sSLo /tmp/haveno.deb.sig https://github.com/havenoexample/haveno-example/releases/download/1.1.1/haveno-v1.1.1-linux-x86_64-installer.deb.sig -# curl -sSLo /tmp/haveno-jar.SHA-256 https://github.com/havenoexample/haveno-example/releases/download/1.1.1/haveno-v1.1.1-linux-x86_64-SNAPSHOT-all.jar.SHA-256 +# curl -sSLo /tmp/haveno.deb https://github.com/havenoexample/haveno-example/releases/download/1.1.2/haveno-v1.1.2-linux-x86_64-installer.deb +# curl -sSLo /tmp/haveno.deb.sig https://github.com/havenoexample/haveno-example/releases/download/1.1.2/haveno-v1.1.2-linux-x86_64-installer.deb.sig +# curl -sSLo /tmp/haveno-jar.SHA-256 https://github.com/havenoexample/haveno-example/releases/download/1.1.2/haveno-v1.1.2-linux-x86_64-SNAPSHOT-all.jar.SHA-256 ```

    Note:

    diff --git a/scripts/install_whonix_qubes/README.md b/scripts/install_whonix_qubes/README.md index 21b6beb468..4ca3cef867 100644 --- a/scripts/install_whonix_qubes/README.md +++ b/scripts/install_whonix_qubes/README.md @@ -35,7 +35,7 @@ $ bash 0.0-dom0.sh && bash 0.1-dom0.sh && bash 0.2-dom0.sh

    Example:

    ```shell -% sudo bash 1.0-haveno-templatevm.sh "https://github.com/havenoexample/haveno-example/releases/download/1.1.1/haveno-v1.1.1-linux-x86_64-installer.deb" "ABAF11C65A2970B130ABE3C479BE3E4300411886" +% sudo bash 1.0-haveno-templatevm.sh "https://github.com/havenoexample/haveno-example/releases/download/1.1.2/haveno-v1.1.2-linux-x86_64-installer.deb" "ABAF11C65A2970B130ABE3C479BE3E4300411886" ``` ### *Via Source* diff --git a/seednode/src/main/java/haveno/seednode/SeedNodeMain.java b/seednode/src/main/java/haveno/seednode/SeedNodeMain.java index b846dff4c9..c92a3d61d8 100644 --- a/seednode/src/main/java/haveno/seednode/SeedNodeMain.java +++ b/seednode/src/main/java/haveno/seednode/SeedNodeMain.java @@ -41,7 +41,7 @@ import lombok.extern.slf4j.Slf4j; @Slf4j public class SeedNodeMain extends ExecutableForAppWithP2p { private static final long CHECK_CONNECTION_LOSS_SEC = 30; - private static final String VERSION = "1.1.1"; + private static final String VERSION = "1.1.2"; private SeedNode seedNode; private Timer checkConnectionLossTime; From ee49324fbb86456446c122a41f215f520fd311b8 Mon Sep 17 00:00:00 2001 From: woodser Date: Sun, 8 Jun 2025 10:14:27 -0400 Subject: [PATCH 58/74] fix divide by zero error opening trade summary with no history --- .../main/java/haveno/core/trade/ClosedTradableFormatter.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/haveno/core/trade/ClosedTradableFormatter.java b/core/src/main/java/haveno/core/trade/ClosedTradableFormatter.java index db42547d45..cb5b0f1d6e 100644 --- a/core/src/main/java/haveno/core/trade/ClosedTradableFormatter.java +++ b/core/src/main/java/haveno/core/trade/ClosedTradableFormatter.java @@ -76,7 +76,7 @@ public class ClosedTradableFormatter { } public String getTotalTxFeeAsString(BigInteger totalTradeAmount, BigInteger totalTxFee) { - double percentage = HavenoUtils.divide(totalTxFee, totalTradeAmount); + double percentage = totalTradeAmount.equals(BigInteger.ZERO) ? 0 : HavenoUtils.divide(totalTxFee, totalTradeAmount); return Res.get(I18N_KEY_TOTAL_TX_FEE, HavenoUtils.formatXmr(totalTxFee, true), formatToPercentWithSymbol(percentage)); @@ -104,7 +104,7 @@ public class ClosedTradableFormatter { } public String getTotalTradeFeeAsString(BigInteger totalTradeAmount, BigInteger totalTradeFee) { - double percentage = HavenoUtils.divide(totalTradeFee, totalTradeAmount); + double percentage = totalTradeAmount.equals(BigInteger.ZERO) ? 0 : HavenoUtils.divide(totalTradeFee, totalTradeAmount); return Res.get(I18N_KEY_TOTAL_TRADE_FEE_BTC, HavenoUtils.formatXmr(totalTradeFee, true), formatToPercentWithSymbol(percentage)); From 183782982c1b4e6e7c67c5a7e7ab9ea73c3a0404 Mon Sep 17 00:00:00 2001 From: woodser Date: Mon, 9 Jun 2025 06:51:59 -0400 Subject: [PATCH 59/74] fix display name of non-fiat traditional currencies --- core/src/main/java/haveno/core/locale/CurrencyUtil.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/core/src/main/java/haveno/core/locale/CurrencyUtil.java b/core/src/main/java/haveno/core/locale/CurrencyUtil.java index 911bb65d96..c94d55c70b 100644 --- a/core/src/main/java/haveno/core/locale/CurrencyUtil.java +++ b/core/src/main/java/haveno/core/locale/CurrencyUtil.java @@ -406,6 +406,13 @@ public class CurrencyUtil { removedCryptoCurrency.isPresent() ? removedCryptoCurrency.get().getName() : Res.get("shared.na"); return getCryptoCurrency(currencyCode).map(TradeCurrency::getName).orElse(xmrOrRemovedAsset); } + if (isTraditionalNonFiatCurrency(currencyCode)) { + return getTraditionalNonFiatCurrencies().stream() + .filter(currency -> currency.getCode().equals(currencyCode)) + .findAny() + .map(TradeCurrency::getName) + .orElse(currencyCode); + } try { return Currency.getInstance(currencyCode).getDisplayName(); } catch (Throwable t) { From 62d5eb4bc35121018dfa3e502adf808f0b466ac4 Mon Sep 17 00:00:00 2001 From: woodser Date: Mon, 9 Jun 2025 07:05:46 -0400 Subject: [PATCH 60/74] fix distorted confirm payment sent checkbox in windows dark mode --- desktop/src/main/java/haveno/desktop/haveno.css | 1 + 1 file changed, 1 insertion(+) diff --git a/desktop/src/main/java/haveno/desktop/haveno.css b/desktop/src/main/java/haveno/desktop/haveno.css index 7c24526aaf..7fc6948fda 100644 --- a/desktop/src/main/java/haveno/desktop/haveno.css +++ b/desktop/src/main/java/haveno/desktop/haveno.css @@ -2283,6 +2283,7 @@ textfield */ -fx-background-insets: 44; -fx-background-radius: 15; -fx-border-radius: 15; + -fx-effect: dropshadow(gaussian, -bs-text-color-dropshadow-light-mode, 44, 0, 0, 0); } .notification-popup-bg, .peer-info-popup-bg { From 53e2c5cc243c8f8b0d0914af7ea109e2869c34c9 Mon Sep 17 00:00:00 2001 From: woodser Date: Mon, 9 Jun 2025 07:06:00 -0400 Subject: [PATCH 61/74] center the top nav buttons when window is small --- desktop/src/main/java/haveno/desktop/main/MainView.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/desktop/src/main/java/haveno/desktop/main/MainView.java b/desktop/src/main/java/haveno/desktop/main/MainView.java index ee7434324c..2a5b994cdb 100644 --- a/desktop/src/main/java/haveno/desktop/main/MainView.java +++ b/desktop/src/main/java/haveno/desktop/main/MainView.java @@ -305,8 +305,12 @@ public class MainView extends InitializableView { } }); + // add spacer to center the nav buttons when window is small + Region rightSpacer = new Region(); + HBox.setHgrow(rightSpacer, Priority.ALWAYS); + HBox primaryNav = new HBox(getLogoPane(), marketButton, getNavigationSpacer(), buyButton, getNavigationSpacer(), - sellButton, getNavigationSpacer(), portfolioButtonWithBadge, getNavigationSpacer(), fundsButton); + sellButton, getNavigationSpacer(), portfolioButtonWithBadge, getNavigationSpacer(), fundsButton, rightSpacer); primaryNav.setAlignment(Pos.CENTER_LEFT); primaryNav.getStyleClass().add("nav-primary"); From e4e118f70cfdd6d84728d340b2940a8b09484c76 Mon Sep 17 00:00:00 2001 From: woodser Date: Mon, 9 Jun 2025 07:06:43 -0400 Subject: [PATCH 62/74] rename logos with dark_mode and light_mode postfix --- .../main/java/haveno/desktop/app/HavenoAppMain.java | 2 +- desktop/src/main/java/haveno/desktop/theme-dark.css | 6 +++--- .../src/main/java/haveno/desktop/theme-light.css | 6 +++--- ...dscape_dark.png => logo_landscape_dark_mode.png} | Bin ...cape_light.png => logo_landscape_light_mode.png} | Bin ...go_splash_dark.png => logo_splash_dark_mode.png} | Bin ..._splash_light.png => logo_splash_light_mode.png} | Bin ...t_dark.png => logo_splash_testnet_dark_mode.png} | Bin ...light.png => logo_splash_testnet_light_mode.png} | Bin 9 files changed, 7 insertions(+), 7 deletions(-) rename desktop/src/main/resources/images/{logo_landscape_dark.png => logo_landscape_dark_mode.png} (100%) rename desktop/src/main/resources/images/{logo_landscape_light.png => logo_landscape_light_mode.png} (100%) rename desktop/src/main/resources/images/{logo_splash_dark.png => logo_splash_dark_mode.png} (100%) rename desktop/src/main/resources/images/{logo_splash_light.png => logo_splash_light_mode.png} (100%) rename desktop/src/main/resources/images/{logo_splash_testnet_dark.png => logo_splash_testnet_dark_mode.png} (100%) rename desktop/src/main/resources/images/{logo_splash_testnet_light.png => logo_splash_testnet_light_mode.png} (100%) diff --git a/desktop/src/main/java/haveno/desktop/app/HavenoAppMain.java b/desktop/src/main/java/haveno/desktop/app/HavenoAppMain.java index 73e78ab6d7..c6217cfe09 100644 --- a/desktop/src/main/java/haveno/desktop/app/HavenoAppMain.java +++ b/desktop/src/main/java/haveno/desktop/app/HavenoAppMain.java @@ -216,7 +216,7 @@ public class HavenoAppMain extends HavenoExecutable { // Set the dialog content VBox vbox = new VBox(10); - ImageView logoImageView = new ImageView(ImageUtil.getImageByPath("logo_splash_light.png")); + ImageView logoImageView = new ImageView(ImageUtil.getImageByPath("logo_splash_light_mode.png")); logoImageView.setFitWidth(342); logoImageView.setPreserveRatio(true); vbox.getChildren().addAll(logoImageView, passwordField, errorMessageField, versionField); diff --git a/desktop/src/main/java/haveno/desktop/theme-dark.css b/desktop/src/main/java/haveno/desktop/theme-dark.css index 1e56814261..5316735b69 100644 --- a/desktop/src/main/java/haveno/desktop/theme-dark.css +++ b/desktop/src/main/java/haveno/desktop/theme-dark.css @@ -578,15 +578,15 @@ } #image-logo-splash { - -fx-image: url("../../images/logo_splash_dark.png"); + -fx-image: url("../../images/logo_splash_dark_mode.png"); } #image-logo-splash-testnet { - -fx-image: url("../../images/logo_splash_testnet_dark.png"); + -fx-image: url("../../images/logo_splash_testnet_dark_mode.png"); } #image-logo-landscape { - -fx-image: url("../../images/logo_landscape_dark.png"); + -fx-image: url("../../images/logo_landscape_dark_mode.png"); } .table-view .placeholder { diff --git a/desktop/src/main/java/haveno/desktop/theme-light.css b/desktop/src/main/java/haveno/desktop/theme-light.css index 160a21e3c3..99bc71a49c 100644 --- a/desktop/src/main/java/haveno/desktop/theme-light.css +++ b/desktop/src/main/java/haveno/desktop/theme-light.css @@ -144,15 +144,15 @@ } #image-logo-splash { - -fx-image: url("../../images/logo_splash_light.png"); + -fx-image: url("../../images/logo_splash_light_mode.png"); } #image-logo-splash-testnet { - -fx-image: url("../../images/logo_splash_testnet_light.png"); + -fx-image: url("../../images/logo_splash_testnet_light_mode.png"); } #image-logo-landscape { - -fx-image: url("../../images/logo_landscape_light.png"); + -fx-image: url("../../images/logo_landscape_light_mode.png"); } #charts .default-color0.chart-series-area-fill { diff --git a/desktop/src/main/resources/images/logo_landscape_dark.png b/desktop/src/main/resources/images/logo_landscape_dark_mode.png similarity index 100% rename from desktop/src/main/resources/images/logo_landscape_dark.png rename to desktop/src/main/resources/images/logo_landscape_dark_mode.png diff --git a/desktop/src/main/resources/images/logo_landscape_light.png b/desktop/src/main/resources/images/logo_landscape_light_mode.png similarity index 100% rename from desktop/src/main/resources/images/logo_landscape_light.png rename to desktop/src/main/resources/images/logo_landscape_light_mode.png diff --git a/desktop/src/main/resources/images/logo_splash_dark.png b/desktop/src/main/resources/images/logo_splash_dark_mode.png similarity index 100% rename from desktop/src/main/resources/images/logo_splash_dark.png rename to desktop/src/main/resources/images/logo_splash_dark_mode.png diff --git a/desktop/src/main/resources/images/logo_splash_light.png b/desktop/src/main/resources/images/logo_splash_light_mode.png similarity index 100% rename from desktop/src/main/resources/images/logo_splash_light.png rename to desktop/src/main/resources/images/logo_splash_light_mode.png diff --git a/desktop/src/main/resources/images/logo_splash_testnet_dark.png b/desktop/src/main/resources/images/logo_splash_testnet_dark_mode.png similarity index 100% rename from desktop/src/main/resources/images/logo_splash_testnet_dark.png rename to desktop/src/main/resources/images/logo_splash_testnet_dark_mode.png diff --git a/desktop/src/main/resources/images/logo_splash_testnet_light.png b/desktop/src/main/resources/images/logo_splash_testnet_light_mode.png similarity index 100% rename from desktop/src/main/resources/images/logo_splash_testnet_light.png rename to desktop/src/main/resources/images/logo_splash_testnet_light_mode.png From 4f9e39410d9071626761a49791680fd6efbb7543 Mon Sep 17 00:00:00 2001 From: woodser <13068859+woodser@users.noreply.github.com> Date: Mon, 9 Jun 2025 07:22:23 -0400 Subject: [PATCH 63/74] make account creation buttons green --- .../main/account/content/cryptoaccounts/CryptoAccountsView.java | 1 + .../content/traditionalaccounts/TraditionalAccountsView.java | 1 + 2 files changed, 2 insertions(+) diff --git a/desktop/src/main/java/haveno/desktop/main/account/content/cryptoaccounts/CryptoAccountsView.java b/desktop/src/main/java/haveno/desktop/main/account/content/cryptoaccounts/CryptoAccountsView.java index bdadfffbda..7a4f20cacf 100644 --- a/desktop/src/main/java/haveno/desktop/main/account/content/cryptoaccounts/CryptoAccountsView.java +++ b/desktop/src/main/java/haveno/desktop/main/account/content/cryptoaccounts/CryptoAccountsView.java @@ -181,6 +181,7 @@ public class CryptoAccountsView extends PaymentAccountsView tuple3 = add3ButtonsAfterGroup(root, ++gridRow, Res.get("shared.addNewAccount"), Res.get("shared.ExportAccounts"), Res.get("shared.importAccounts")); addAccountButton = tuple3.first; + addAccountButton.setId("buy-button-big"); exportButton = tuple3.second; importButton = tuple3.third; } diff --git a/desktop/src/main/java/haveno/desktop/main/account/content/traditionalaccounts/TraditionalAccountsView.java b/desktop/src/main/java/haveno/desktop/main/account/content/traditionalaccounts/TraditionalAccountsView.java index 79243691bc..d3555d197b 100644 --- a/desktop/src/main/java/haveno/desktop/main/account/content/traditionalaccounts/TraditionalAccountsView.java +++ b/desktop/src/main/java/haveno/desktop/main/account/content/traditionalaccounts/TraditionalAccountsView.java @@ -462,6 +462,7 @@ public class TraditionalAccountsView extends PaymentAccountsView tuple3 = add3ButtonsAfterGroup(root, ++gridRow, Res.get("shared.addNewAccount"), Res.get("shared.ExportAccounts"), Res.get("shared.importAccounts")); addAccountButton = tuple3.first; + addAccountButton.setId("buy-button-big"); exportButton = tuple3.second; importButton = tuple3.third; } From 145f0c9cf680dd127f812d7dd31863bc852ea5b0 Mon Sep 17 00:00:00 2001 From: woodser <13068859+woodser@users.noreply.github.com> Date: Tue, 10 Jun 2025 09:03:22 -0400 Subject: [PATCH 64/74] make select backup location button green --- .../haveno/desktop/main/account/content/backup/BackupView.java | 1 + 1 file changed, 1 insertion(+) diff --git a/desktop/src/main/java/haveno/desktop/main/account/content/backup/BackupView.java b/desktop/src/main/java/haveno/desktop/main/account/content/backup/BackupView.java index d8d3eea770..98d1e80af3 100644 --- a/desktop/src/main/java/haveno/desktop/main/account/content/backup/BackupView.java +++ b/desktop/src/main/java/haveno/desktop/main/account/content/backup/BackupView.java @@ -91,6 +91,7 @@ public class BackupView extends ActivatableView { Tuple2 tuple2 = add2ButtonsAfterGroup(root, ++gridRow, Res.get("account.backup.selectLocation"), Res.get("account.backup.backupNow")); selectBackupDir = tuple2.first; + selectBackupDir.setId("buy-button-big"); backupNow = tuple2.second; updateButtons(); From ab5f6c81919a09d2eb724681c3563baa37482cf0 Mon Sep 17 00:00:00 2001 From: atsamd21 Date: Thu, 12 Jun 2025 14:16:59 +0200 Subject: [PATCH 65/74] Add disableRateLimits option to daemon (#1803) --- .../java/haveno/common/config/Config.java | 10 ++++ .../java/haveno/daemon/grpc/GrpcServer.java | 51 +++++++++++++------ 2 files changed, 45 insertions(+), 16 deletions(-) diff --git a/common/src/main/java/haveno/common/config/Config.java b/common/src/main/java/haveno/common/config/Config.java index b162e211b4..03a61cbd40 100644 --- a/common/src/main/java/haveno/common/config/Config.java +++ b/common/src/main/java/haveno/common/config/Config.java @@ -119,6 +119,7 @@ public class Config { public static final String PASSWORD_REQUIRED = "passwordRequired"; public static final String UPDATE_XMR_BINARIES = "updateXmrBinaries"; public static final String XMR_BLOCKCHAIN_PATH = "xmrBlockchainPath"; + public static final String DISABLE_RATE_LIMITS = "disableRateLimits"; // Default values for certain options public static final int UNSPECIFIED_PORT = -1; @@ -208,6 +209,7 @@ public class Config { public final boolean passwordRequired; public final boolean updateXmrBinaries; public final String xmrBlockchainPath; + public final boolean disableRateLimits; // Properties derived from options but not exposed as options themselves public final File torDir; @@ -639,6 +641,13 @@ public class Config { .ofType(String.class) .defaultsTo(""); + ArgumentAcceptingOptionSpec disableRateLimits = + parser.accepts(DISABLE_RATE_LIMITS, + "Disables all API rate limits") + .withRequiredArg() + .ofType(boolean.class) + .defaultsTo(false); + try { CompositeOptionSet options = new CompositeOptionSet(); @@ -753,6 +762,7 @@ public class Config { this.passwordRequired = options.valueOf(passwordRequiredOpt); this.updateXmrBinaries = options.valueOf(updateXmrBinariesOpt); this.xmrBlockchainPath = options.valueOf(xmrBlockchainPathOpt); + this.disableRateLimits = options.valueOf(disableRateLimits); } catch (OptionException ex) { throw new ConfigException("problem parsing option '%s': %s", ex.options().get(0), diff --git a/daemon/src/main/java/haveno/daemon/grpc/GrpcServer.java b/daemon/src/main/java/haveno/daemon/grpc/GrpcServer.java index 1de4580038..d2a5fc49b5 100644 --- a/daemon/src/main/java/haveno/daemon/grpc/GrpcServer.java +++ b/daemon/src/main/java/haveno/daemon/grpc/GrpcServer.java @@ -22,11 +22,15 @@ import com.google.inject.Singleton; import haveno.common.config.Config; import haveno.core.api.CoreContext; import haveno.daemon.grpc.interceptor.PasswordAuthInterceptor; -import io.grpc.Server; -import io.grpc.ServerBuilder; import static io.grpc.ServerInterceptors.interceptForward; import java.io.IOException; import java.io.UncheckedIOException; +import io.grpc.Metadata; +import io.grpc.ServerCall; +import io.grpc.ServerCallHandler; +import io.grpc.ServerInterceptor; +import io.grpc.Server; +import io.grpc.ServerBuilder; import lombok.extern.slf4j.Slf4j; @Singleton @@ -55,26 +59,41 @@ public class GrpcServer { GrpcXmrConnectionService moneroConnectionsService, GrpcXmrNodeService moneroNodeService) { this.server = ServerBuilder.forPort(config.apiPort) - .addService(interceptForward(accountService, accountService.interceptors())) - .addService(interceptForward(disputeAgentsService, disputeAgentsService.interceptors())) - .addService(interceptForward(disputesService, disputesService.interceptors())) - .addService(interceptForward(helpService, helpService.interceptors())) - .addService(interceptForward(offersService, offersService.interceptors())) - .addService(interceptForward(paymentAccountsService, paymentAccountsService.interceptors())) - .addService(interceptForward(priceService, priceService.interceptors())) .addService(shutdownService) - .addService(interceptForward(tradeStatisticsService, tradeStatisticsService.interceptors())) - .addService(interceptForward(tradesService, tradesService.interceptors())) - .addService(interceptForward(versionService, versionService.interceptors())) - .addService(interceptForward(walletsService, walletsService.interceptors())) - .addService(interceptForward(notificationsService, notificationsService.interceptors())) - .addService(interceptForward(moneroConnectionsService, moneroConnectionsService.interceptors())) - .addService(interceptForward(moneroNodeService, moneroNodeService.interceptors())) .intercept(passwordAuthInterceptor) + .addService(interceptForward(accountService, config.disableRateLimits ? interceptors() : accountService.interceptors())) + .addService(interceptForward(disputeAgentsService, config.disableRateLimits ? interceptors() : disputeAgentsService.interceptors())) + .addService(interceptForward(disputesService, config.disableRateLimits ? interceptors() : disputesService.interceptors())) + .addService(interceptForward(helpService, config.disableRateLimits ? interceptors() : helpService.interceptors())) + .addService(interceptForward(offersService, config.disableRateLimits ? interceptors() : offersService.interceptors())) + .addService(interceptForward(paymentAccountsService, config.disableRateLimits ? interceptors() : paymentAccountsService.interceptors())) + .addService(interceptForward(priceService, config.disableRateLimits ? interceptors() : priceService.interceptors())) + .addService(interceptForward(tradeStatisticsService, config.disableRateLimits ? interceptors() : tradeStatisticsService.interceptors())) + .addService(interceptForward(tradesService, config.disableRateLimits ? interceptors() : tradesService.interceptors())) + .addService(interceptForward(versionService, config.disableRateLimits ? interceptors() : versionService.interceptors())) + .addService(interceptForward(walletsService, config.disableRateLimits ? interceptors() : walletsService.interceptors())) + .addService(interceptForward(notificationsService, config.disableRateLimits ? interceptors() : notificationsService.interceptors())) + .addService(interceptForward(moneroConnectionsService, config.disableRateLimits ? interceptors() : moneroConnectionsService.interceptors())) + .addService(interceptForward(moneroNodeService, config.disableRateLimits ? interceptors() : moneroNodeService.interceptors())) .build(); + coreContext.setApiUser(true); } + private ServerInterceptor[] interceptors() { + return new ServerInterceptor[]{callLoggingInterceptor()}; + } + + private ServerInterceptor callLoggingInterceptor() { + return new ServerInterceptor() { + @Override + public ServerCall.Listener interceptCall(ServerCall call, Metadata headers, ServerCallHandler next) { + log.debug("GRPC endpoint called: " + call.getMethodDescriptor().getFullMethodName()); + return next.startCall(call, headers); + } + }; + } + public void start() { try { server.start(); From 2f18a74478d8e36c81124ebdd87ab3ab86ce2482 Mon Sep 17 00:00:00 2001 From: woodser Date: Fri, 13 Jun 2025 07:57:02 -0400 Subject: [PATCH 66/74] support sweeping funds from grpc api, relay multiple txs --- .../main/java/haveno/core/api/CoreApi.java | 8 +++-- .../haveno/core/api/CoreWalletsService.java | 16 +++++++-- .../core/xmr/wallet/XmrWalletService.java | 34 +++++++++++++----- .../daemon/grpc/GrpcWalletsService.java | 36 ++++++++++++++----- proto/src/main/proto/grpc.proto | 20 ++++++++--- 5 files changed, 89 insertions(+), 25 deletions(-) diff --git a/core/src/main/java/haveno/core/api/CoreApi.java b/core/src/main/java/haveno/core/api/CoreApi.java index e8e83978eb..5162bfdb33 100644 --- a/core/src/main/java/haveno/core/api/CoreApi.java +++ b/core/src/main/java/haveno/core/api/CoreApi.java @@ -299,8 +299,12 @@ public class CoreApi { return walletsService.createXmrTx(destinations); } - public String relayXmrTx(String metadata) { - return walletsService.relayXmrTx(metadata); + public List createXmrSweepTxs(String address) { + return walletsService.createXmrSweepTxs(address); + } + + public List relayXmrTxs(List metadatas) { + return walletsService.relayXmrTxs(metadatas); } public long getAddressBalance(String addressString) { diff --git a/core/src/main/java/haveno/core/api/CoreWalletsService.java b/core/src/main/java/haveno/core/api/CoreWalletsService.java index 68ec8c13ea..0433a8e994 100644 --- a/core/src/main/java/haveno/core/api/CoreWalletsService.java +++ b/core/src/main/java/haveno/core/api/CoreWalletsService.java @@ -173,12 +173,24 @@ class CoreWalletsService { } } - String relayXmrTx(String metadata) { + List createXmrSweepTxs(String address) { accountService.checkAccountOpen(); verifyWalletsAreAvailable(); verifyEncryptedWalletIsUnlocked(); try { - return xmrWalletService.relayTx(metadata); + return xmrWalletService.createSweepTxs(address); + } catch (Exception ex) { + log.error("", ex); + throw new IllegalStateException(ex); + } + } + + List relayXmrTxs(List metadatas) { + accountService.checkAccountOpen(); + verifyWalletsAreAvailable(); + verifyEncryptedWalletIsUnlocked(); + try { + return xmrWalletService.relayTxs(metadatas); } catch (Exception ex) { log.error("", ex); throw new IllegalStateException(ex); diff --git a/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java b/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java index 65cd28d3d8..b2c4ce90e2 100644 --- a/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java +++ b/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java @@ -441,6 +441,12 @@ public class XmrWalletService extends XmrWalletBase { if (name.contains(File.separator)) throw new IllegalArgumentException("Path not expected: " + name); } + public MoneroTxWallet createTx(List destinations) { + MoneroTxWallet tx = createTx(new MoneroTxConfig().setAccountIndex(0).setDestinations(destinations).setRelay(false).setCanSplit(false)); + //printTxs("XmrWalletService.createTx", tx); + return tx; + } + public MoneroTxWallet createTx(MoneroTxConfig txConfig) { synchronized (walletLock) { synchronized (HavenoUtils.getWalletFunctionLock()) { @@ -455,18 +461,30 @@ public class XmrWalletService extends XmrWalletBase { } } - public String relayTx(String metadata) { + public List createSweepTxs(String address) { + return createSweepTxs(new MoneroTxConfig().setAccountIndex(0).setAddress(address).setRelay(false)); + } + + public List createSweepTxs(MoneroTxConfig txConfig) { synchronized (walletLock) { - String txId = wallet.relayTx(metadata); - requestSaveWallet(); - return txId; + synchronized (HavenoUtils.getWalletFunctionLock()) { + List txs = wallet.sweepUnlocked(txConfig); + if (Boolean.TRUE.equals(txConfig.getRelay())) { + for (MoneroTxWallet tx : txs) cachedTxs.addFirst(tx); + cacheWalletInfo(); + requestSaveWallet(); + } + return txs; + } } } - public MoneroTxWallet createTx(List destinations) { - MoneroTxWallet tx = createTx(new MoneroTxConfig().setAccountIndex(0).setDestinations(destinations).setRelay(false).setCanSplit(false)); - //printTxs("XmrWalletService.createTx", tx); - return tx; + public List relayTxs(List metadatas) { + synchronized (walletLock) { + List txIds = wallet.relayTxs(metadatas); + requestSaveWallet(); + return txIds; + } } /** diff --git a/daemon/src/main/java/haveno/daemon/grpc/GrpcWalletsService.java b/daemon/src/main/java/haveno/daemon/grpc/GrpcWalletsService.java index 7c3ca22e3b..9fbfa02089 100644 --- a/daemon/src/main/java/haveno/daemon/grpc/GrpcWalletsService.java +++ b/daemon/src/main/java/haveno/daemon/grpc/GrpcWalletsService.java @@ -43,6 +43,8 @@ import static haveno.core.api.model.XmrTx.toXmrTx; import haveno.daemon.grpc.interceptor.CallRateMeteringInterceptor; import haveno.daemon.grpc.interceptor.GrpcCallRateMeter; import static haveno.daemon.grpc.interceptor.GrpcServiceRateMeteringConfig.getCustomRateMeteringInterceptor; +import haveno.proto.grpc.CreateXmrSweepTxsReply; +import haveno.proto.grpc.CreateXmrSweepTxsRequest; import haveno.proto.grpc.CreateXmrTxReply; import haveno.proto.grpc.CreateXmrTxRequest; import haveno.proto.grpc.GetAddressBalanceReply; @@ -61,8 +63,8 @@ import haveno.proto.grpc.GetXmrTxsReply; import haveno.proto.grpc.GetXmrTxsRequest; import haveno.proto.grpc.LockWalletReply; import haveno.proto.grpc.LockWalletRequest; -import haveno.proto.grpc.RelayXmrTxReply; -import haveno.proto.grpc.RelayXmrTxRequest; +import haveno.proto.grpc.RelayXmrTxsReply; +import haveno.proto.grpc.RelayXmrTxsRequest; import haveno.proto.grpc.RemoveWalletPasswordReply; import haveno.proto.grpc.RemoveWalletPasswordRequest; import haveno.proto.grpc.SetWalletPasswordReply; @@ -185,7 +187,7 @@ class GrpcWalletsService extends WalletsImplBase { .stream() .map(s -> new MoneroDestination(s.getAddress(), new BigInteger(s.getAmount()))) .collect(Collectors.toList())); - log.info("Successfully created XMR tx: hash {}", tx.getHash()); + log.info("Successfully created XMR tx, hash: {}", tx.getHash()); var reply = CreateXmrTxReply.newBuilder() .setTx(toXmrTx(tx).toProtoMessage()) .build(); @@ -197,12 +199,30 @@ class GrpcWalletsService extends WalletsImplBase { } @Override - public void relayXmrTx(RelayXmrTxRequest req, - StreamObserver responseObserver) { + public void createXmrSweepTxs(CreateXmrSweepTxsRequest req, + StreamObserver responseObserver) { try { - String txHash = coreApi.relayXmrTx(req.getMetadata()); - var reply = RelayXmrTxReply.newBuilder() - .setHash(txHash) + List xmrTxs = coreApi.createXmrSweepTxs(req.getAddress()); + log.info("Successfully created XMR sweep txs, hashes: {}", xmrTxs.stream().map(MoneroTxWallet::getHash).collect(Collectors.toList())); + var reply = CreateXmrSweepTxsReply.newBuilder() + .addAllTxs(xmrTxs.stream() + .map(s -> toXmrTx(s).toProtoMessage()) + .collect(Collectors.toList())) + .build(); + responseObserver.onNext(reply); + responseObserver.onCompleted(); + } catch (Throwable cause) { + exceptionHandler.handleException(log, cause, responseObserver); + } + } + + @Override + public void relayXmrTxs(RelayXmrTxsRequest req, + StreamObserver responseObserver) { + try { + List txHashes = coreApi.relayXmrTxs(req.getMetadatasList()); + var reply = RelayXmrTxsReply.newBuilder() + .addAllHashes(txHashes) .build(); responseObserver.onNext(reply); responseObserver.onCompleted(); diff --git a/proto/src/main/proto/grpc.proto b/proto/src/main/proto/grpc.proto index b9615b5bcb..b2af139042 100644 --- a/proto/src/main/proto/grpc.proto +++ b/proto/src/main/proto/grpc.proto @@ -944,7 +944,9 @@ service Wallets { } rpc CreateXmrTx (CreateXmrTxRequest) returns (CreateXmrTxReply) { } - rpc relayXmrTx (RelayXmrTxRequest) returns (RelayXmrTxReply) { + rpc CreateXmrSweepTxs (CreateXmrSweepTxsRequest) returns (CreateXmrSweepTxsReply) { + } + rpc RelayXmrTxs (RelayXmrTxsRequest) returns (RelayXmrTxsReply) { } rpc GetAddressBalance (GetAddressBalanceRequest) returns (GetAddressBalanceReply) { } @@ -1036,12 +1038,20 @@ message CreateXmrTxReply { XmrTx tx = 1; } -message RelayXmrTxRequest { - string metadata = 1; +message CreateXmrSweepTxsRequest { + string address = 1; } -message RelayXmrTxReply { - string hash = 1; +message CreateXmrSweepTxsReply { + repeated XmrTx txs = 1; +} + +message RelayXmrTxsRequest { + repeated string metadatas = 1; +} + +message RelayXmrTxsReply { + repeated string hashes = 2; } message GetAddressBalanceRequest { From bc8f473b469c6f3c2aa662bbf0e202d10c1ee0d3 Mon Sep 17 00:00:00 2001 From: woodser Date: Mon, 16 Jun 2025 09:24:05 -0400 Subject: [PATCH 67/74] adjust spacing when creating new accounts --- .../desktop/components/paymentmethods/AssetsForm.java | 2 +- .../content/cryptoaccounts/CryptoAccountsView.java | 7 ++++--- .../traditionalaccounts/TraditionalAccountsView.java | 11 ++++++----- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/desktop/src/main/java/haveno/desktop/components/paymentmethods/AssetsForm.java b/desktop/src/main/java/haveno/desktop/components/paymentmethods/AssetsForm.java index 7baa76c564..0548c5d4b6 100644 --- a/desktop/src/main/java/haveno/desktop/components/paymentmethods/AssetsForm.java +++ b/desktop/src/main/java/haveno/desktop/components/paymentmethods/AssetsForm.java @@ -192,7 +192,7 @@ public class AssetsForm extends PaymentMethodForm { @Override protected void addTradeCurrencyComboBox() { currencyComboBox = FormBuilder.addLabelAutocompleteComboBox(gridPane, ++gridRow, Res.get("payment.crypto"), - Layout.FIRST_ROW_AND_GROUP_DISTANCE).second; + Layout.GROUP_DISTANCE).second; currencyComboBox.setPromptText(Res.get("payment.select.crypto")); currencyComboBox.setButtonCell(getComboBoxButtonCell(Res.get("payment.select.crypto"), currencyComboBox)); diff --git a/desktop/src/main/java/haveno/desktop/main/account/content/cryptoaccounts/CryptoAccountsView.java b/desktop/src/main/java/haveno/desktop/main/account/content/cryptoaccounts/CryptoAccountsView.java index 7a4f20cacf..ba62725aab 100644 --- a/desktop/src/main/java/haveno/desktop/main/account/content/cryptoaccounts/CryptoAccountsView.java +++ b/desktop/src/main/java/haveno/desktop/main/account/content/cryptoaccounts/CryptoAccountsView.java @@ -175,7 +175,8 @@ public class CryptoAccountsView extends PaymentAccountsView, VBox> tuple = addTopLabelListView(root, gridRow, Res.get("account.crypto.yourCryptoAccounts"), Layout.FIRST_ROW_DISTANCE); paymentAccountsListView = tuple.second; int prefNumRows = Math.min(4, Math.max(2, model.dataModel.getNumPaymentAccounts())); - paymentAccountsListView.setMinHeight(prefNumRows * Layout.LIST_ROW_HEIGHT + 28); + paymentAccountsListView.setMinHeight(prefNumRows * Layout.LIST_ROW_HEIGHT + 34); + paymentAccountsListView.setMaxHeight(prefNumRows * Layout.LIST_ROW_HEIGHT + 34); setPaymentAccountsCellFactory(); Tuple3 tuple3 = add3ButtonsAfterGroup(root, ++gridRow, Res.get("shared.addNewAccount"), @@ -191,7 +192,7 @@ public class CryptoAccountsView extends PaymentAccountsView, VBox> tuple = addTopLabelListView(root, gridRow, Res.get("account.traditional.yourTraditionalAccounts"), Layout.FIRST_ROW_DISTANCE); paymentAccountsListView = tuple.second; int prefNumRows = Math.min(4, Math.max(2, model.dataModel.getNumPaymentAccounts())); - paymentAccountsListView.setMinHeight(prefNumRows * Layout.LIST_ROW_HEIGHT + 28); + paymentAccountsListView.setMinHeight(prefNumRows * Layout.LIST_ROW_HEIGHT + 34); + paymentAccountsListView.setMaxHeight(prefNumRows * Layout.LIST_ROW_HEIGHT + 34); setPaymentAccountsCellFactory(); Tuple3 tuple3 = add3ButtonsAfterGroup(root, ++gridRow, Res.get("shared.addNewAccount"), @@ -473,10 +474,10 @@ public class TraditionalAccountsView extends PaymentAccountsView Date: Tue, 17 Jun 2025 09:56:16 -0400 Subject: [PATCH 68/74] increase contrast of nav balance subtext --- desktop/src/main/java/haveno/desktop/theme-light.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/desktop/src/main/java/haveno/desktop/theme-light.css b/desktop/src/main/java/haveno/desktop/theme-light.css index 99bc71a49c..44e703ef69 100644 --- a/desktop/src/main/java/haveno/desktop/theme-light.css +++ b/desktop/src/main/java/haveno/desktop/theme-light.css @@ -69,7 +69,7 @@ -bs-rd-font-light: #8d8d8d; -bs-rd-font-lighter: #a7a7a7; -bs-rd-font-confirmation-label: #504f52; - -bs-rd-font-balance-label: #bbbbbb; + -bs-rd-font-balance-label: rgb(215, 215, 215, 1); -bs-text-color-dropshadow: rgba(0, 0, 0, 0.54); -bs-text-color-dropshadow-light-mode: rgba(0, 0, 0, 0.54); -bs-text-color-transparent: rgba(0, 0, 0, 0.2); From 81f7bac4527036903b240629794c91606203f71a Mon Sep 17 00:00:00 2001 From: Olexandr88 Date: Wed, 18 Jun 2025 15:13:22 +0300 Subject: [PATCH 69/74] added link to build badge --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c8078a97aa..9469a16e2f 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@
    Haveno logo - ![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/haveno-dex/haveno/build.yml?branch=master) + [![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/haveno-dex/haveno/build.yml?branch=master)](https://github.com/haveno-dex/haveno/actions) [![GitHub issues with bounty](https://img.shields.io/github/issues-search/haveno-dex/haveno?color=%23fef2c0&label=Issues%20with%20bounties&query=is%3Aopen+is%3Aissue+label%3A%F0%9F%92%B0bounty)](https://github.com/haveno-dex/haveno/issues?q=is%3Aopen+is%3Aissue+label%3A%F0%9F%92%B0bounty) [![Twitter Follow](https://img.shields.io/twitter/follow/HavenoDEX?style=social)](https://twitter.com/havenodex) [![Matrix rooms](https://img.shields.io/badge/Matrix%20room-%23haveno-blue)](https://matrix.to/#/#haveno:monero.social) [![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg)](https://github.com/haveno-dex/.github/blob/master/CODE_OF_CONDUCT.md) From b82d6c3004b417f14bf3a5ddda42f51f31fb6511 Mon Sep 17 00:00:00 2001 From: woodser Date: Thu, 19 Jun 2025 08:09:43 -0400 Subject: [PATCH 70/74] widen withdraw amount field --- .../haveno/desktop/main/funds/withdrawal/WithdrawalView.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/desktop/src/main/java/haveno/desktop/main/funds/withdrawal/WithdrawalView.java b/desktop/src/main/java/haveno/desktop/main/funds/withdrawal/WithdrawalView.java index 6217bb1e04..04a960f486 100644 --- a/desktop/src/main/java/haveno/desktop/main/funds/withdrawal/WithdrawalView.java +++ b/desktop/src/main/java/haveno/desktop/main/funds/withdrawal/WithdrawalView.java @@ -144,7 +144,7 @@ public class WithdrawalView extends ActivatableView { amountLabel = feeTuple3.first; amountTextField = feeTuple3.second; - amountTextField.setMinWidth(200); + amountTextField.setMinWidth(225); HyperlinkWithIcon sendMaxLink = feeTuple3.third; withdrawMemoTextField = addTopLabelInputTextField(gridPane, ++rowIndex, From 32148e744010e88180578024098a8c8c7642fba0 Mon Sep 17 00:00:00 2001 From: woodser Date: Thu, 19 Jun 2025 09:03:51 -0400 Subject: [PATCH 71/74] fix translations by automatically escaping single quotes, remove escapes --- .../i18n/displayStrings-assets.properties | 5 - .../src/main/java/haveno/core/locale/Res.java | 6 +- .../resources/i18n/displayStrings.properties | 95 ++++--- .../i18n/displayStrings_cs.properties | 19 +- .../i18n/displayStrings_de.properties | 15 +- .../i18n/displayStrings_es.properties | 11 +- .../i18n/displayStrings_fa.properties | 55 ++-- .../i18n/displayStrings_fr.properties | 239 +++++++++--------- .../i18n/displayStrings_it.properties | 31 +-- .../i18n/displayStrings_ja.properties | 7 +- .../i18n/displayStrings_pt-br.properties | 31 +-- .../i18n/displayStrings_pt.properties | 33 +-- .../i18n/displayStrings_ru.properties | 51 ++-- .../i18n/displayStrings_th.properties | 55 ++-- .../i18n/displayStrings_tr.properties | 17 +- .../i18n/displayStrings_vi.properties | 53 ++-- .../i18n/displayStrings_zh-hans.properties | 15 +- .../i18n/displayStrings_zh-hant.properties | 15 +- 18 files changed, 336 insertions(+), 417 deletions(-) diff --git a/assets/src/main/resources/i18n/displayStrings-assets.properties b/assets/src/main/resources/i18n/displayStrings-assets.properties index ae23634d1c..5d67b53eab 100644 --- a/assets/src/main/resources/i18n/displayStrings-assets.properties +++ b/assets/src/main/resources/i18n/displayStrings-assets.properties @@ -4,11 +4,6 @@ # E.g.: [main-view].[component].[description] # In some cases we use enum values or constants to map to display strings -# A annoying issue with property files is that we need to use 2 single quotes in display string -# containing variables (e.g. {0}), otherwise the variable will not be resolved. -# In display string which do not use a variable a single quote is ok. -# E.g. Don''t .... {1} - # We use sometimes dynamic parts which are put together in the code and therefore sometimes use line breaks or spaces # at the end of the string. Please never remove any line breaks or spaces. They are there with a purpose! # To make longer strings with better readable you can make a line break with \ which does not result in a line break diff --git a/core/src/main/java/haveno/core/locale/Res.java b/core/src/main/java/haveno/core/locale/Res.java index e44092561f..3ea4d1c5ac 100644 --- a/core/src/main/java/haveno/core/locale/Res.java +++ b/core/src/main/java/haveno/core/locale/Res.java @@ -103,7 +103,11 @@ public class Res { } public static String get(String key, Object... arguments) { - return MessageFormat.format(Res.get(key), arguments); + return MessageFormat.format(escapeQuotes(get(key)), arguments); + } + + private static String escapeQuotes(String s) { + return s.replace("'", "''"); } public static String get(String key) { diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index a0fd2962b1..1ebbbb2829 100644 --- a/core/src/main/resources/i18n/displayStrings.properties +++ b/core/src/main/resources/i18n/displayStrings.properties @@ -4,11 +4,6 @@ # E.g.: [main-view].[component].[description] # In some cases we use enum values or constants to map to display strings -# A annoying issue with property files is that we need to use 2 single quotes in display string -# containing variables (e.g. {0}), otherwise the variable will not be resolved. -# In display string which do not use a variable a single quote is ok. -# E.g. Don''t .... {1} - # We use sometimes dynamic parts which are put together in the code and therefore sometimes use line breaks or spaces # at the end of the string. Please never remove any line breaks or spaces. They are there with a purpose! # To make longer strings with better readable you can make a line break with \ which does not result in a line break @@ -115,7 +110,7 @@ shared.belowInPercent=Below % from market price shared.aboveInPercent=Above % from market price shared.enterPercentageValue=Enter % value shared.OR=OR -shared.notEnoughFunds=You don''t have enough funds in your Haveno wallet for this transaction—{0} is needed but only {1} is available.\n\nPlease add funds from an external wallet, or fund your Haveno wallet at Funds > Receive Funds. +shared.notEnoughFunds=You don't have enough funds in your Haveno wallet for this transaction—{0} is needed but only {1} is available.\n\nPlease add funds from an external wallet, or fund your Haveno wallet at Funds > Receive Funds. shared.waitingForFunds=Waiting for funds... shared.yourDepositTransactionId=Your deposit transaction ID shared.peerDepositTransactionId=Peer's deposit transaction ID @@ -350,9 +345,9 @@ offerbook.takeOffer=Take offer offerbook.takeOffer.createAccount=Create account and take offer offerbook.takeOffer.enterChallenge=Enter the offer passphrase offerbook.trader=Trader -offerbook.offerersBankId=Maker''s bank ID (BIC/SWIFT): {0} -offerbook.offerersBankName=Maker''s bank name: {0} -offerbook.offerersBankSeat=Maker''s seat of bank country: {0} +offerbook.offerersBankId=Maker's bank ID (BIC/SWIFT): {0} +offerbook.offerersBankName=Maker's bank name: {0} +offerbook.offerersBankSeat=Maker's seat of bank country: {0} offerbook.offerersAcceptedBankSeatsEuro=Accepted seat of bank countries (taker): All Euro countries offerbook.offerersAcceptedBankSeats=Accepted seat of bank countries (taker):\n {0} offerbook.availableOffersToBuy=Buy {0} with {1} @@ -398,7 +393,7 @@ offerbook.clonedOffer.info=Cloning an offer creates a copy without reserving add For more information about cloning offers see: [HYPERLINK:https://docs.haveno.exchange/haveno-ui/cloning_an_offer/] offerbook.timeSinceSigning.help=When you successfully complete a trade with a peer who has a signed payment account, your payment account is signed.\n\ - {0} days later, the initial limit of {1} is lifted and your account can sign other peers'' payment accounts. + {0} days later, the initial limit of {1} is lifted and your account can sign other peers' payment accounts. offerbook.timeSinceSigning.notSigned=Not signed yet offerbook.timeSinceSigning.notSigned.ageDays={0} days offerbook.timeSinceSigning.notSigned.noNeed=N/A @@ -436,8 +431,8 @@ offerbook.warning.newVersionAnnouncement=With this version of the software, trad For more information on account signing, please see the documentation at [HYPERLINK:https://docs.haveno.exchange/the-project/account_limits/#account-signing]. popup.warning.tradeLimitDueAccountAgeRestriction.seller=The allowed trade amount is limited to {0} because of security restrictions based on the following criteria:\n\ - - The buyer''s account has not been signed by an arbitrator or a peer\n\ - - The time since signing of the buyer''s account is not at least 30 days\n\ + - The buyer's account has not been signed by an arbitrator or a peer\n\ + - The time since signing of the buyer's account is not at least 30 days\n\ - The payment method for this offer is considered risky for bank chargebacks\n\n{1} popup.warning.tradeLimitDueAccountAgeRestriction.buyer=The allowed trade amount is limited to {0} because of security restrictions based on the following criteria:\n\ - Your account has not been signed by an arbitrator or a peer\n\ @@ -546,7 +541,7 @@ createOffer.tac=With publishing this offer I agree to trade with any trader who createOffer.setDeposit=Set buyer's security deposit (%) createOffer.setDepositAsBuyer=Set my security deposit as buyer (%) createOffer.setDepositForBothTraders=Set both traders' security deposit (%) -createOffer.securityDepositInfo=Your buyer''s security deposit will be {0} +createOffer.securityDepositInfo=Your buyer's security deposit will be {0} createOffer.securityDepositInfoAsBuyer=Your security deposit as buyer will be {0} createOffer.minSecurityDepositUsed=Minimum security deposit is used createOffer.buyerAsTakerWithoutDeposit=No deposit required from buyer (passphrase protected) @@ -730,11 +725,11 @@ portfolio.pending.step2_buyer.cash.extra=IMPORTANT REQUIREMENT:\nAfter you have # suppress inspection "TrailingSpacesInProperty" portfolio.pending.step2_buyer.moneyGram=Please pay {0} to the XMR seller by using MoneyGram.\n\n portfolio.pending.step2_buyer.moneyGram.extra=IMPORTANT REQUIREMENT:\nAfter you have done the payment send the Authorisation number and a photo of the receipt by email to the XMR seller.\n\ - The receipt must clearly show the seller''s full name, country, state and the amount. The seller''s email is: {0}. + The receipt must clearly show the seller's full name, country, state and the amount. The seller's email is: {0}. # suppress inspection "TrailingSpacesInProperty" portfolio.pending.step2_buyer.westernUnion=Please pay {0} to the XMR seller by using Western Union.\n\n portfolio.pending.step2_buyer.westernUnion.extra=IMPORTANT REQUIREMENT:\nAfter you have done the payment send the MTCN (tracking number) and a photo of the receipt by email to the XMR seller.\n\ - The receipt must clearly show the seller''s full name, city, country and the amount. The seller''s email is: {0}. + The receipt must clearly show the seller's full name, city, country and the amount. The seller's email is: {0}. # suppress inspection "TrailingSpacesInProperty" portfolio.pending.step2_buyer.postal=Please send {0} by \"US Postal Money Order\" to the XMR seller.\n\n @@ -743,13 +738,13 @@ portfolio.pending.step2_buyer.payByMail=Please send {0} using \"Pay by Mail\" to Specific instructions are in the trade contract, or if unclear you may ask questions via trader chat. \ See more details about Pay by Mail on the Haveno wiki [HYPERLINK:https://docs.haveno.exchange/the-project/payment_methods/Pay_By_Mail].\n\n # suppress inspection "TrailingSpacesInProperty" -portfolio.pending.step2_buyer.pay=Please pay {0} via the specified payment method to the XMR seller. You''ll find the seller's account details on the next screen.\n\n +portfolio.pending.step2_buyer.pay=Please pay {0} via the specified payment method to the XMR seller. You'll find the seller's account details on the next screen.\n\n # suppress inspection "TrailingSpacesInProperty" portfolio.pending.step2_buyer.f2f=Please contact the XMR seller by the provided contact and arrange a meeting to pay {0}.\n\n portfolio.pending.step2_buyer.startPaymentUsing=Start payment using {0} portfolio.pending.step2_buyer.recipientsAccountData=Recipients {0} portfolio.pending.step2_buyer.amountToTransfer=Amount to transfer -portfolio.pending.step2_buyer.sellersAddress=Seller''s {0} address +portfolio.pending.step2_buyer.sellersAddress=Seller's {0} address portfolio.pending.step2_buyer.buyerAccount=Your payment account to be used portfolio.pending.step2_buyer.paymentSent=Payment sent portfolio.pending.step2_buyer.warn=You still have not done your {0} payment!\nPlease note that the trade has to be completed by {1}. @@ -761,15 +756,15 @@ portfolio.pending.step2_buyer.paperReceipt.msg=Remember:\n\ Then tear it in 2 parts, make a photo and send it to the XMR seller's email address. portfolio.pending.step2_buyer.moneyGramMTCNInfo.headline=Send Authorisation number and receipt portfolio.pending.step2_buyer.moneyGramMTCNInfo.msg=You need to send the Authorisation number and a photo of the receipt by email to the XMR seller.\n\ - The receipt must clearly show the seller''s full name, country, state and the amount. The seller''s email is: {0}.\n\n\ + The receipt must clearly show the seller's full name, country, state and the amount. The seller's email is: {0}.\n\n\ Did you send the Authorisation number and contract to the seller? portfolio.pending.step2_buyer.westernUnionMTCNInfo.headline=Send MTCN and receipt portfolio.pending.step2_buyer.westernUnionMTCNInfo.msg=You need to send the MTCN (tracking number) and a photo of the receipt by email to the XMR seller.\n\ - The receipt must clearly show the seller''s full name, city, country and the amount. The seller''s email is: {0}.\n\n\ + The receipt must clearly show the seller's full name, city, country and the amount. The seller's email is: {0}.\n\n\ Did you send the MTCN and contract to the seller? portfolio.pending.step2_buyer.halCashInfo.headline=Send HalCash code portfolio.pending.step2_buyer.halCashInfo.msg=You need to send a text message with the HalCash code as well as the \ - trade ID ({0}) to the XMR seller.\nThe seller''s mobile nr. is {1}.\n\n\ + trade ID ({0}) to the XMR seller.\nThe seller's mobile nr. is {1}.\n\n\ Did you send the code to the seller? portfolio.pending.step2_buyer.fasterPaymentsHolderNameInfo=Some banks might verify the receiver's name. \ Faster Payments accounts created in old Haveno clients do not provide the receiver's name, \ @@ -789,8 +784,8 @@ portfolio.pending.step2_seller.f2fInfo.headline=Buyer's contact information portfolio.pending.step2_seller.waitPayment.msg=The deposit transaction is unlocked.\nYou need to wait until the XMR buyer starts the {0} payment. portfolio.pending.step2_seller.warn=The XMR buyer still has not done the {0} payment.\nYou need to wait until they have started the payment.\nIf the trade has not been completed on {1} the arbitrator will investigate. portfolio.pending.step2_seller.openForDispute=The XMR buyer has not started their payment!\nThe max. allowed period for the trade has elapsed.\nYou can wait longer and give the trading peer more time or contact the arbitrator for assistance. -disputeChat.chatWindowTitle=Dispute chat window for trade with ID ''{0}'' -tradeChat.chatWindowTitle=Trader Chat window for trade with ID ''{0}'' +disputeChat.chatWindowTitle=Dispute chat window for trade with ID '{0}' +tradeChat.chatWindowTitle=Trader Chat window for trade with ID '{0}' tradeChat.openChat=Open chat window tradeChat.rules=You can communicate with your trade peer to resolve potential problems with this trade.\n\ It is not mandatory to reply in the chat.\n\ @@ -818,7 +813,7 @@ message.state.ACKNOWLEDGED=Peer confirmed message receipt message.state.FAILED=Sending message failed portfolio.pending.step3_buyer.wait.headline=Wait for XMR seller's payment confirmation -portfolio.pending.step3_buyer.wait.info=Waiting for the XMR seller''s confirmation for the receipt of the {0} payment. +portfolio.pending.step3_buyer.wait.info=Waiting for the XMR seller's confirmation for the receipt of the {0} payment. portfolio.pending.step3_buyer.wait.msgStateInfo.label=Payment started message status portfolio.pending.step3_buyer.warn.part1a=on the {0} blockchain portfolio.pending.step3_buyer.warn.part1b=at your payment provider (e.g. bank) @@ -857,7 +852,7 @@ portfolio.pending.step3_seller.amazonGiftCard=The buyer has sent you an Amazon e message to your mobile phone. Please redeem now the Amazon eGift Card at your Amazon account and once accepted \ confirm the payment receipt. -portfolio.pending.step3_seller.bankCheck=\n\nPlease also verify that the name of the sender specified on the trade contract matches the name that appears on your bank statement:\nSender''s name, per trade contract: {0}\n\n\ +portfolio.pending.step3_seller.bankCheck=\n\nPlease also verify that the name of the sender specified on the trade contract matches the name that appears on your bank statement:\nSender's name, per trade contract: {0}\n\n\ If the names are not exactly the same, {1} # suppress inspection "TrailingSpacesInProperty" portfolio.pending.step3_seller.openDispute=don't confirm payment receipt. Instead, open a dispute by pressing \"alt + o\" or \"option + o\".\n\n @@ -882,7 +877,7 @@ portfolio.pending.step3_seller.openForDispute=You have not confirmed the receipt # suppress inspection "TrailingSpacesInProperty" portfolio.pending.step3_seller.onPaymentReceived.part1=Have you received the {0} payment from your trading partner?\n\n # suppress inspection "TrailingSpacesInProperty" -portfolio.pending.step3_seller.onPaymentReceived.name=Please also verify that the name of the sender specified on the trade contract matches the name that appears on your bank statement:\nSender''s name, per trade contract: {0}\n\nIf the names are not exactly the same, don''t confirm payment receipt. Instead, open a dispute by pressing \"alt + o\" or \"option + o\".\n\n +portfolio.pending.step3_seller.onPaymentReceived.name=Please also verify that the name of the sender specified on the trade contract matches the name that appears on your bank statement:\nSender's name, per trade contract: {0}\n\nIf the names are not exactly the same, don't confirm payment receipt. Instead, open a dispute by pressing \"alt + o\" or \"option + o\".\n\n # suppress inspection "TrailingSpacesInProperty" portfolio.pending.step3_seller.onPaymentReceived.note=Please note, that as soon you have confirmed the receipt, the locked trade amount will be released to the XMR buyer and the security deposit will be refunded.\n\n portfolio.pending.step3_seller.onPaymentReceived.confirm.headline=Confirm that you have received the payment @@ -963,7 +958,7 @@ portfolio.pending.mediationResult.info.selfAccepted=You have accepted the mediat portfolio.pending.mediationResult.info.peerAccepted=Your trade peer has accepted the mediator's suggestion. Do you accept as well? portfolio.pending.mediationResult.button=View proposed resolution portfolio.pending.mediationResult.popup.headline=Mediation result for trade with ID: {0} -portfolio.pending.mediationResult.popup.headline.peerAccepted=Your trade peer has accepted the mediator''s suggestion for trade {0} +portfolio.pending.mediationResult.popup.headline.peerAccepted=Your trade peer has accepted the mediator's suggestion for trade {0} portfolio.pending.mediationResult.popup.info=The mediator has suggested the following payout:\n\ You receive: {0}\n\ Your trading peer receives: {1}\n\n\ @@ -972,12 +967,12 @@ portfolio.pending.mediationResult.popup.info=The mediator has suggested the foll 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 open a \ second-round dispute with an arbitrator who will investigate the case again and do a payout based on their findings.\n\n\ - The arbitrator may charge a small fee (fee maximum: the trader''s security deposit) as compensation for their work. \ - Both traders agreeing to the mediator''s suggestion is the happy path—requesting arbitration is meant for \ + The arbitrator may charge a small fee (fee maximum: the trader's security deposit) as compensation for their work. \ + Both traders agreeing to the mediator's suggestion is the happy path—requesting arbitration is meant for \ exceptional circumstances, such as if a trader is sure the mediator did not make a fair payout suggestion \ (or if the other peer is unresponsive).\n\n\ More details about the new arbitration model: [HYPERLINK:https://haveno.exchange/wiki/Dispute_resolution#Level_3:_Arbitration] -portfolio.pending.mediationResult.popup.selfAccepted.lockTimeOver=You have accepted the mediator''s suggested payout \ +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\ 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\ @@ -1271,7 +1266,7 @@ support.initialInfo=Please enter a description of your problem in the text field \t Sometimes the data directory gets corrupted and leads to strange bugs. \n\ \t See: https://docs.haveno.exchange/backup-recovery.html#switch-to-a-new-data-directory\n\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● You need to respond to the {0}'s requests within 2 days.\n\ \t● Mediators respond in between 2 days. Arbitrators respond in between 5 business days.\n\ \t● The maximum period for a dispute is 14 days.\n\ \t● You need to cooperate with the {1} and provide the information they request to make your case.\n\ @@ -1284,9 +1279,9 @@ support.youOpenedDisputeForMediation=You requested mediation.\n\n{0}\n\nHaveno v support.peerOpenedTicket=Your trading peer has requested support due to technical problems.\n\n{0}\n\nHaveno version: {1} support.peerOpenedDispute=Your trading peer has requested a dispute.\n\n{0}\n\nHaveno version: {1} support.peerOpenedDisputeForMediation=Your trading peer has requested mediation.\n\n{0}\n\nHaveno version: {1} -support.mediatorsDisputeSummary=System message: Mediator''s dispute summary:\n{0} +support.mediatorsDisputeSummary=System message: Mediator's dispute summary:\n{0} support.mediatorReceivedLogs=System message: Mediator has received logs: {0} -support.mediatorsAddress=Mediator''s node address: {0} +support.mediatorsAddress=Mediator's node address: {0} support.warning.disputesWithInvalidDonationAddress=The delayed payout transaction has used an invalid receiver address. \ It does not match any of the DAO parameter values for the valid donation addresses.\n\nThis might be a scam attempt. \ Please inform the developers about that incident and do not close that case before the situation is resolved!\n\n\ @@ -1428,19 +1423,19 @@ setting.about.subsystems.label=Versions of subsystems setting.about.subsystems.val=Network version: {0}; P2P message version: {1}; Local DB version: {2}; Trade protocol version: {3} setting.about.shortcuts=Short cuts -setting.about.shortcuts.ctrlOrAltOrCmd=''Ctrl + {0}'' or ''alt + {0}'' or ''cmd + {0}'' +setting.about.shortcuts.ctrlOrAltOrCmd='Ctrl + {0}' or 'alt + {0}' or 'cmd + {0}' setting.about.shortcuts.menuNav=Navigate main menu setting.about.shortcuts.menuNav.value=To navigate the main menu press: 'Ctrl' or 'alt' or 'cmd' with a numeric key between '1-9' setting.about.shortcuts.close=Close Haveno -setting.about.shortcuts.close.value=''Ctrl + {0}'' or ''cmd + {0}'' or ''Ctrl + {1}'' or ''cmd + {1}'' +setting.about.shortcuts.close.value='Ctrl + {0}' or 'cmd + {0}' or 'Ctrl + {1}' or 'cmd + {1}' setting.about.shortcuts.closePopup=Close popup or dialog window setting.about.shortcuts.closePopup.value='ESCAPE' key setting.about.shortcuts.chatSendMsg=Send trader chat message -setting.about.shortcuts.chatSendMsg.value=''Ctrl + ENTER'' or ''alt + ENTER'' or ''cmd + ENTER'' +setting.about.shortcuts.chatSendMsg.value='Ctrl + ENTER' or 'alt + ENTER' or 'cmd + ENTER' setting.about.shortcuts.openDispute=Open dispute setting.about.shortcuts.openDispute.value=Select pending trade and click: {0} @@ -1518,8 +1513,8 @@ account.arbitratorRegistration.registerFailed=Could not complete registration.{0 account.crypto.yourCryptoAccounts=Your cryptocurrency accounts account.crypto.popup.wallet.msg=Please be sure that you follow the requirements for the usage of {0} wallets as \ -described on the {1} web page.\nUsing wallets from centralized exchanges where (a) you don''t control your keys or \ -(b) which don''t use compatible wallet software is risky: it can lead to loss of the traded funds!\nThe mediator or arbitrator is \ +described on the {1} web page.\nUsing wallets from centralized exchanges where (a) you don't control your keys or \ +(b) which don't use compatible wallet software is risky: it can lead to loss of the traded funds!\nThe mediator or arbitrator is \ not a {2} specialist and cannot help in such cases. account.crypto.popup.wallet.confirm=I understand and confirm that I know which wallet I need to use. # suppress inspection "UnusedProperty" @@ -1824,8 +1819,8 @@ account.notifications.marketAlert.manageAlerts.header.offerType=Offer type account.notifications.marketAlert.message.title=Offer alert account.notifications.marketAlert.message.msg.below=below account.notifications.marketAlert.message.msg.above=above -account.notifications.marketAlert.message.msg=A new ''{0} {1}'' offer with price {2} ({3} {4} market price) and \ - payment method ''{5}'' was published to the Haveno offerbook.\n\ +account.notifications.marketAlert.message.msg=A new '{0} {1}' offer with price {2} ({3} {4} market price) and \ + payment method '{5}' was published to the Haveno offerbook.\n\ Offer ID: {6}. account.notifications.priceAlert.message.title=Price alert for {0} account.notifications.priceAlert.message.msg=Your price alert got triggered. The current {0} price is {1} {2} @@ -2158,7 +2153,7 @@ error.closedTradeWithNoDepositTx=The deposit transaction of the closed trade wit popup.warning.walletNotInitialized=The wallet is not initialized yet popup.warning.wrongVersion=You probably have the wrong Haveno version for this computer.\n\ -Your computer''s architecture is: {0}.\n\ +Your computer's architecture is: {0}.\n\ The Haveno binary you installed is: {1}.\n\ Please shut down and re-install the correct version ({2}). popup.warning.incompatibleDB=We detected incompatible data base files!\n\n\ @@ -2208,7 +2203,7 @@ popup.warning.mandatoryUpdate.trading=Please update to the latest Haveno version Please check out the Haveno Forum for more information. popup.warning.noFilter=We did not receive a filter object from the seed nodes. Please inform the network administrators to register a filter object. popup.warning.burnXMR=This transaction is not possible, as the mining fees of {0} would exceed the amount to transfer of {1}. \ - Please wait until the mining fees are low again or until you''ve accumulated more XMR to transfer. + Please wait until the mining fees are low again or until you've accumulated more XMR to transfer. popup.warning.openOffer.makerFeeTxRejected=The maker fee transaction for offer with ID {0} was rejected by the Monero network.\n\ Transaction ID={1}.\n\ @@ -2230,7 +2225,7 @@ popup.warning.openOfferWithInvalidMakerFeeTx=The maker fee transaction for offer For further help please contact the Haveno support channel at the Haveno Keybase team. popup.info.cashDepositInfo=Please be sure that you have a bank branch in your area to be able to make the cash deposit.\n\ - The bank ID (BIC/SWIFT) of the seller''s bank is: {0}. + The bank ID (BIC/SWIFT) of the seller's bank is: {0}. popup.info.cashDepositInfo.confirm=I confirm that I can make the deposit popup.info.shutDownWithOpenOffers=Haveno is being shut down, but there are open offers. \n\n\ These offers won't be available on the P2P network while Haveno is shut down, but \ @@ -2298,8 +2293,8 @@ popup.accountSigning.success.headline=Congratulations popup.accountSigning.success.description=All {0} payment accounts were successfully signed! popup.accountSigning.generalInformation=You'll find the signing state of all your accounts in the account section.\n\n\ For further information, please visit [HYPERLINK:https://docs.haveno.exchange/payment-methods#account-signing]. -popup.accountSigning.signedByArbitrator=One of your payment accounts has been verified and signed by an arbitrator. Trading with this account will automatically sign your trading peer''s account after a successful trade.\n\n{0} -popup.accountSigning.signedByPeer=One of your payment accounts has been verified and signed by a trading peer. Your initial trading limit will be lifted and you''ll be able to sign other accounts in {0} days from now.\n\n{1} +popup.accountSigning.signedByArbitrator=One of your payment accounts has been verified and signed by an arbitrator. Trading with this account will automatically sign your trading peer's account after a successful trade.\n\n{0} +popup.accountSigning.signedByPeer=One of your payment accounts has been verified and signed by a trading peer. Your initial trading limit will be lifted and you'll be able to sign other accounts in {0} days from now.\n\n{1} popup.accountSigning.peerLimitLifted=The initial limit for one of your accounts has been lifted.\n\n{0} popup.accountSigning.peerSigner=One of your accounts is mature enough to sign other payment accounts \ and the initial limit for one of your accounts has been lifted.\n\n{0} @@ -2681,13 +2676,13 @@ payment.zelle.info=Zelle is a money transfer service that works best *through* a 3. If your bank does not work with Zelle, you can still use it through the Zelle mobile app, but your transfer limits will be much lower.\n\n\ 4. The name specified on your Haveno account MUST match the name on your Zelle/bank account. \n\n\ If you cannot complete a Zelle transaction as specified in your trade contract, you may lose some (or all) of your security deposit.\n\n\ - Because of Zelle''s somewhat higher chargeback risk, sellers are advised to contact unsigned buyers through email or SMS to verify that the buyer \ + Because of Zelle's somewhat higher chargeback risk, sellers are advised to contact unsigned buyers through email or SMS to verify that the buyer \ really owns the Zelle account specified in Haveno. -payment.fasterPayments.newRequirements.info=Some banks have started verifying the receiver''s full name for Faster \ +payment.fasterPayments.newRequirements.info=Some banks have started verifying the receiver's full name for Faster \ Payments transfers. Your current Faster Payments account does not specify a full name.\n\n\ Please consider recreating your Faster Payments account in Haveno to provide future {0} buyers with a full name.\n\n\ When you recreate the account, make sure to copy the precise sort code, account number and account age verification \ - salt values from your old account to your new account. This will ensure your existing account''s age and signing \ + salt values from your old account to your new account. This will ensure your existing account's age and signing \ status are preserved. payment.fasterPayments.ukSortCode="UK sort code" payment.moneyGram.info=When using MoneyGram the XMR buyer has to send the Authorisation number and a photo of the receipt by email to the XMR seller. \ @@ -2736,8 +2731,8 @@ payment.cashDeposit.info=Please confirm your bank allows you to send cash deposi payment.revolut.info=Revolut requires the 'Username' as account ID not the phone number or email as it was the case in the past. payment.account.revolut.addUserNameInfo={0}\n\ - Your existing Revolut account ({1}) does not have a ''Username''.\n\ - Please enter your Revolut ''Username'' to update your account data.\n\ + Your existing Revolut account ({1}) does not have a 'Username'.\n\ + Please enter your Revolut 'Username' to update your account data.\n\ This will not affect your account age signing status. payment.revolut.addUserNameInfo.headLine=Update Revolut account @@ -3103,7 +3098,7 @@ payment.amazonGiftCard.info=To pay with Amazon eGift Card, you will need to send Please see the wiki [HYPERLINK:https://docs.haveno.exchange/the-project/payment_methods/Amazon_eGift_card] for further details and best practices. \n\n\ Three important notes:\n\ - try to send gift cards with amounts of 100 USD or smaller, as Amazon is known to flag larger gift cards as fraudulent\n\ - - try to use creative, believable text for the gift card''s message (e.g., "Happy birthday Susan!") along with the trade ID (and use trader chat \ + - try to use creative, believable text for the gift card's message (e.g., "Happy birthday Susan!") along with the trade ID (and use trader chat \ to tell your trading peer the reference text you picked so they can verify your payment)\n\ - Amazon eGift Cards can only be redeemed on the Amazon website they were purchased on (e.g., a gift card purchased on amazon.it can only be redeemed on amazon.it) payment.paysafe.info=For your protection, we strongly discourage using Paysafecard PINs for payment.\n\n\ diff --git a/core/src/main/resources/i18n/displayStrings_cs.properties b/core/src/main/resources/i18n/displayStrings_cs.properties index 53658b4038..14cd7b0812 100644 --- a/core/src/main/resources/i18n/displayStrings_cs.properties +++ b/core/src/main/resources/i18n/displayStrings_cs.properties @@ -4,11 +4,6 @@ # E.g.: [main-view].[component].[description] # In some cases we use enum values or constants to map to display strings -# A annoying issue with property files is that we need to use 2 single quotes in display string -# containing variables (e.g. {0}), otherwise the variable will not be resolved. -# In display string which do not use a variable a single quote is ok. -# E.g. Don''t .... {1} - # We use sometimes dynamic parts which are put together in the code and therefore sometimes use line breaks or spaces # at the end of the string. Please never remove any line breaks or spaces. They are there with a purpose! # To make longer strings with better readable you can make a line break with \ which does not result in a line break @@ -754,8 +749,8 @@ portfolio.pending.step2_seller.f2fInfo.headline=Kontaktní informace kupujícíh portfolio.pending.step2_seller.waitPayment.msg=Vkladová transakce má alespoň jedno potvrzení na blockchainu.\nMusíte počkat, než kupující XMR zahájí platbu {0}. portfolio.pending.step2_seller.warn=Kupující XMR dosud neprovedl platbu {0}.\nMusíte počkat, než zahájí platbu.\nPokud obchod nebyl dokončen dne {1}, bude rozhodce vyšetřovat. portfolio.pending.step2_seller.openForDispute=Kupující XMR ještě nezačal s platbou!\nMax. povolené období pro obchod vypršelo.\nMůžete počkat déle a dát obchodnímu partnerovi více času nebo požádat o pomoc mediátora. -disputeChat.chatWindowTitle=Okno chatu sporu pro obchod s ID ''{0}'' -tradeChat.chatWindowTitle=Okno chatu pro obchod s ID ''{0}'' +disputeChat.chatWindowTitle=Okno chatu sporu pro obchod s ID '{0}' +tradeChat.chatWindowTitle=Okno chatu pro obchod s ID '{0}' tradeChat.openChat=Otevřít chatovací okno tradeChat.rules=Můžete komunikovat se svým obchodním partnerem a vyřešit případné problémy s tímto obchodem.\n\ Odpovídat v chatu není povinné.\n\ @@ -1391,19 +1386,19 @@ setting.about.subsystems.label=Verze subsystémů setting.about.subsystems.val=Verze sítě: {0}; Verze zpráv P2P: {1}; Verze lokální DB: {2}; Verze obchodního protokolu: {3} setting.about.shortcuts=Zkratky -setting.about.shortcuts.ctrlOrAltOrCmd=''Ctrl + {0}'' nebo ''alt + {0}'' nebo ''cmd + {0}'' +setting.about.shortcuts.ctrlOrAltOrCmd='Ctrl + {0}' nebo 'alt + {0}' nebo 'cmd + {0}' setting.about.shortcuts.menuNav=Procházet hlavní nabídku setting.about.shortcuts.menuNav.value=Pro pohyb v hlavním menu stiskněte: 'Ctrl' nebo 'alt' nebo 'cmd' s numerickou klávesou mezi '1-9' setting.about.shortcuts.close=Zavřít Haveno -setting.about.shortcuts.close.value=''Ctrl + {0}'' nebo ''cmd + {0}'' nebo ''Ctrl + {1}'' nebo ''cmd + {1}'' +setting.about.shortcuts.close.value='Ctrl + {0}' nebo 'cmd + {0}' nebo 'Ctrl + {1}' nebo 'cmd + {1}' setting.about.shortcuts.closePopup=Zavřete vyskakovací nebo dialogové okno setting.about.shortcuts.closePopup.value=Klávesa 'ESCAPE' setting.about.shortcuts.chatSendMsg=Odeslat obchodní soukromou zprávu -setting.about.shortcuts.chatSendMsg.value=''Ctrl + ENTER'' nebo ''alt + ENTER'' nebo ''cmd + ENTER'' +setting.about.shortcuts.chatSendMsg.value='Ctrl + ENTER' nebo 'alt + ENTER' nebo 'cmd + ENTER' setting.about.shortcuts.openDispute=Otevřít spor setting.about.shortcuts.openDispute.value=Vyberte nevyřízený obchod a klikněte na: {0} @@ -1787,8 +1782,8 @@ account.notifications.marketAlert.manageAlerts.header.offerType=Typ nabídky account.notifications.marketAlert.message.title=Upozornění na nabídku account.notifications.marketAlert.message.msg.below=pod account.notifications.marketAlert.message.msg.above=nad -account.notifications.marketAlert.message.msg=Do Haveno byla zveřejněna nová nabídka ''{0} {1}'' s cenou {2} ({3} {4} tržní cena) a \ - způsob platby ''{5}''.\n\ +account.notifications.marketAlert.message.msg=Do Haveno byla zveřejněna nová nabídka '{0} {1}' s cenou {2} ({3} {4} tržní cena) a \ + způsob platby '{5}'.\n\ ID nabídky: {6}. account.notifications.priceAlert.message.title=Upozornění na cenu pro {0} account.notifications.priceAlert.message.msg=Vaše upozornění na cenu bylo aktivováno. Aktuální {0} cena je {1} {2} diff --git a/core/src/main/resources/i18n/displayStrings_de.properties b/core/src/main/resources/i18n/displayStrings_de.properties index 57fd93df67..d04889babd 100644 --- a/core/src/main/resources/i18n/displayStrings_de.properties +++ b/core/src/main/resources/i18n/displayStrings_de.properties @@ -4,11 +4,6 @@ # E.g.: [main-view].[component].[description] # In some cases we use enum values or constants to map to display strings -# A annoying issue with property files is that we need to use 2 single quotes in display string -# containing variables (e.g. {0}), otherwise the variable will not be resolved. -# In display string which do not use a variable a single quote is ok. -# E.g. Don''t .... {1} - # We use sometimes dynamic parts which are put together in the code and therefore sometimes use line breaks or spaces # at the end of the string. Please never remove any line breaks or spaces. They are there with a purpose! # To make longer strings with better readable you can make a line break with \ which does not result in a line break @@ -676,7 +671,7 @@ portfolio.pending.step2_seller.f2fInfo.headline=Kontaktinformation des Käufers portfolio.pending.step2_seller.waitPayment.msg=Die Kautionstransaktion hat mindestens eine Blockchain-Bestätigung.\nSie müssen warten bis der XMR-Käufer die {0}-Zahlung beginnt. portfolio.pending.step2_seller.warn=Der XMR-Käufer hat die {0}-Zahlung noch nicht getätigt.\nSie müssen warten bis die Zahlung begonnen wurde.\nWenn der Handel nicht bis {1} abgeschlossen wurde, wird der Vermittler diesen untersuchen. portfolio.pending.step2_seller.openForDispute=Der XMR-Käufer hat seine Zahlung nicht begonnen!\nDie maximal zulässige Frist für den Handel ist abgelaufen.\nSie können länger warten und dem Handelspartner mehr Zeit geben oder den Vermittler um Hilfe bitten. -tradeChat.chatWindowTitle=Chat-Fenster für Trade mit ID ''{0}'' +tradeChat.chatWindowTitle=Chat-Fenster für Trade mit ID '{0}' tradeChat.openChat=Chat-Fenster öffnen tradeChat.rules=Sie können mit Ihrem Trade-Partner kommunizieren, um mögliche Probleme mit diesem Trade zu lösen.\nEs ist nicht zwingend erforderlich, im Chat zu antworten.\nWenn ein Trader gegen eine der folgenden Regeln verstößt, eröffnen Sie einen Streitfall und melden Sie ihn dem Mediator oder Vermittler.\n\nChat-Regeln:\n\t● Senden Sie keine Links (Risiko von Malware). Sie können die Transaktions-ID und den Namen eines Block-Explorers senden.\n\t● Senden Sie keine Seed-Wörter, Private Keys, Passwörter oder andere sensible Informationen!\n\t● Traden Sie nicht außerhalb von Haveno (keine Sicherheit).\n\t● Beteiligen Sie sich nicht an Betrugsversuchen in Form von Social Engineering.\n\t● Wenn ein Partner nicht antwortet und es vorzieht, nicht über den Chat zu kommunizieren, respektieren Sie seine Entscheidung.\n\t● Beschränken Sie Ihre Kommunikation auf das Traden. Dieser Chat ist kein Messenger-Ersatz oder eine Trollbox.\n\t● Bleiben Sie im Gespräch freundlich und respektvoll. @@ -980,7 +975,7 @@ support.buyerTaker=XMR-Käufer/Abnehmer support.sellerTaker=XMR-Verkäufer/Abnehmer support.backgroundInfo=Haveno ist kein Unternehmen, daher behandelt es Konflikte unterschiedlich.\n\nTrader können innerhalb der Anwendung über einen sicheren Chat auf dem Bildschirm für offene Trades kommunizieren, um zu versuchen, Konflikte selbst zu lösen. Wenn das nicht ausreicht, kann ein Mediator einschreiten und helfen. Der Mediator wird die Situation bewerten und eine Auszahlung von Trade Funds vorschlagen. -support.initialInfo=Bitte geben Sie eine Beschreibung Ihres Problems in das untenstehende Textfeld ein. Fügen Sie so viele Informationen wie möglich hinzu, um die Zeit für die Konfliktlösung zu verkürzen.\n\nHier ist eine Checkliste für Informationen, die Sie angeben sollten:\n\t● Wenn Sie der XMR-Käufer sind: Haben Sie die Traditional- oder Crypto-Überweisung gemacht? Wenn ja, haben Sie in der Anwendung auf die Schaltfläche "Zahlung gestartet" geklickt?\n\t● Wenn Sie der XMR-Verkäufer sind: Haben Sie die Traditional- oder Crypto-Zahlung erhalten? Wenn ja, haben Sie in der Anwendung auf die Schaltfläche "Zahlung erhalten" geklickt?\n\t● Welche Version von Haveno verwenden Sie?\n\t● Welches Betriebssystem verwenden Sie?\n\t● Wenn Sie ein Problem mit fehlgeschlagenen Transaktionen hatten, überlegen Sie bitte, in ein neues Datenverzeichnis zu wechseln.\n\t Manchmal wird das Datenverzeichnis beschädigt und führt zu seltsamen Fehlern. \n\t Siehe: https://docs.haveno.exchange/backup-recovery.html#switch-to-a-new-data-directory\n\nBitte machen Sie sich mit den Grundregeln für den Konfliktprozess vertraut:\n\t● Sie müssen auf die Anfragen der {0}'' innerhalb von 2 Tagen antworten.\n\t● Mediatoren antworten innerhalb von 2 Tagen. Die Vermittler antworten innerhalb von 5 Werktagen.\n\t● Die maximale Frist für einen Konflikt beträgt 14 Tage.\n\t● Sie müssen mit den {1} zusammenarbeiten und die Informationen zur Verfügung stellen, die sie anfordern, um Ihren Fall zu bearbeiten.\n\t● Mit dem ersten Start der Anwendung haben Sie die Regeln des Konfliktdokuments in der Nutzervereinbarung akzeptiert.\n\nSie können mehr über den Konfliktprozess erfahren unter: {2} +support.initialInfo=Bitte geben Sie eine Beschreibung Ihres Problems in das untenstehende Textfeld ein. Fügen Sie so viele Informationen wie möglich hinzu, um die Zeit für die Konfliktlösung zu verkürzen.\n\nHier ist eine Checkliste für Informationen, die Sie angeben sollten:\n\t● Wenn Sie der XMR-Käufer sind: Haben Sie die Traditional- oder Crypto-Überweisung gemacht? Wenn ja, haben Sie in der Anwendung auf die Schaltfläche "Zahlung gestartet" geklickt?\n\t● Wenn Sie der XMR-Verkäufer sind: Haben Sie die Traditional- oder Crypto-Zahlung erhalten? Wenn ja, haben Sie in der Anwendung auf die Schaltfläche "Zahlung erhalten" geklickt?\n\t● Welche Version von Haveno verwenden Sie?\n\t● Welches Betriebssystem verwenden Sie?\n\t● Wenn Sie ein Problem mit fehlgeschlagenen Transaktionen hatten, überlegen Sie bitte, in ein neues Datenverzeichnis zu wechseln.\n\t Manchmal wird das Datenverzeichnis beschädigt und führt zu seltsamen Fehlern. \n\t Siehe: https://docs.haveno.exchange/backup-recovery.html#switch-to-a-new-data-directory\n\nBitte machen Sie sich mit den Grundregeln für den Konfliktprozess vertraut:\n\t● Sie müssen auf die Anfragen der {0}' innerhalb von 2 Tagen antworten.\n\t● Mediatoren antworten innerhalb von 2 Tagen. Die Vermittler antworten innerhalb von 5 Werktagen.\n\t● Die maximale Frist für einen Konflikt beträgt 14 Tage.\n\t● Sie müssen mit den {1} zusammenarbeiten und die Informationen zur Verfügung stellen, die sie anfordern, um Ihren Fall zu bearbeiten.\n\t● Mit dem ersten Start der Anwendung haben Sie die Regeln des Konfliktdokuments in der Nutzervereinbarung akzeptiert.\n\nSie können mehr über den Konfliktprozess erfahren unter: {2} support.systemMsg=Systemnachricht: {0} support.youOpenedTicket=Sie haben eine Anfrage auf Support geöffnet.\n\n{0}\n\nHaveno-Version: {1} support.youOpenedDispute=Sie haben eine Anfrage für einen Konflikt geöffnet.\n\n{0}\n\nHaveno-version: {1} @@ -1115,19 +1110,19 @@ setting.about.subsystems.label=Version des Teilsystems setting.about.subsystems.val=Netzwerkversion: {0}; P2P-Nachrichtenversion: {1}; Lokale DB-Version: {2}; Version des Handelsprotokolls: {3} setting.about.shortcuts=Shortcuts -setting.about.shortcuts.ctrlOrAltOrCmd=''Strg + {0}'' oder ''Alt + {0}'' oder ''cmd + {0}'' +setting.about.shortcuts.ctrlOrAltOrCmd='Strg + {0}' oder 'Alt + {0}' oder 'cmd + {0}' setting.about.shortcuts.menuNav=Hauptmenü navigieren setting.about.shortcuts.menuNav.value=Um durch das Hauptmenü zu navigieren, drücken Sie: 'Strg' oder 'Alt' oder 'cmd' mit einer numerischen Taste zwischen '1-9' setting.about.shortcuts.close=Haveno beenden -setting.about.shortcuts.close.value=''Strg + {0}'' oder ''cmd + {0}'' bzw. ''Strg + {1}'' oder ''cmd + {1}'' +setting.about.shortcuts.close.value='Strg + {0}' oder 'cmd + {0}' bzw. 'Strg + {1}' oder 'cmd + {1}' setting.about.shortcuts.closePopup=Popup- oder Dialogfenster schließen setting.about.shortcuts.closePopup.value='ESCAPE' Taste setting.about.shortcuts.chatSendMsg=Trader eine Chat-Nachricht senden -setting.about.shortcuts.chatSendMsg.value=''Strg + ENTER'' oder ''Alt + ENTER'' oder ''cmd + ENTER'' +setting.about.shortcuts.chatSendMsg.value='Strg + ENTER' oder 'Alt + ENTER' oder 'cmd + ENTER' setting.about.shortcuts.openDispute=Streitfall eröffnen setting.about.shortcuts.openDispute.value=Wählen Sie den ausstehenden Trade und klicken Sie auf: {0} diff --git a/core/src/main/resources/i18n/displayStrings_es.properties b/core/src/main/resources/i18n/displayStrings_es.properties index b5754592dd..a1bb56e29d 100644 --- a/core/src/main/resources/i18n/displayStrings_es.properties +++ b/core/src/main/resources/i18n/displayStrings_es.properties @@ -4,11 +4,6 @@ # E.g.: [main-view].[component].[description] # In some cases we use enum values or constants to map to display strings -# A annoying issue with property files is that we need to use 2 single quotes in display string -# containing variables (e.g. {0}), otherwise the variable will not be resolved. -# In display string which do not use a variable a single quote is ok. -# E.g. Don''t .... {1} - # We use sometimes dynamic parts which are put together in the code and therefore sometimes use line breaks or spaces # at the end of the string. Please never remove any line breaks or spaces. They are there with a purpose! # To make longer strings with better readable you can make a line break with \ which does not result in a line break @@ -1116,19 +1111,19 @@ setting.about.subsystems.label=Versión de subsistemas: setting.about.subsystems.val=Versión de red: {0}; Versión de mensajes P2P: {1}; Versión de Base de Datos local: {2}; Versión de protocolo de intercambio {3} setting.about.shortcuts=Atajos -setting.about.shortcuts.ctrlOrAltOrCmd=''Ctrl + {0}'' o ''alt + {0}'' o ''cmd + {0}'' +setting.about.shortcuts.ctrlOrAltOrCmd='Ctrl + {0}' o 'alt + {0}' o 'cmd + {0}' setting.about.shortcuts.menuNav=Navegar menú principal setting.about.shortcuts.menuNav.value=Para navegar por el menú principal pulse: 'Ctrl' or 'alt' or 'cmd' con una tecla numérica entre el '1-9' setting.about.shortcuts.close=Cerrar Haveno -setting.about.shortcuts.close.value=''Ctrl + {0}'' o ''cmd + {0}'' o ''Ctrl + {1}'' o ''cmd + {1}'' +setting.about.shortcuts.close.value='Ctrl + {0}' o 'cmd + {0}' o 'Ctrl + {1}' o 'cmd + {1}' setting.about.shortcuts.closePopup=Cerrar la ventana emergente o ventana de diálogo setting.about.shortcuts.closePopup.value=Tecla 'ESCAPE' setting.about.shortcuts.chatSendMsg=Enviar mensaje en chat de intercambio -setting.about.shortcuts.chatSendMsg.value=''Ctrl + ENTER'' o ''alt + ENTER'' o ''cmd + ENTER'' +setting.about.shortcuts.chatSendMsg.value='Ctrl + ENTER' o 'alt + ENTER' o 'cmd + ENTER' setting.about.shortcuts.openDispute=Abrir disputa setting.about.shortcuts.openDispute.value=Seleccionar intercambios pendientes y pulsar: {0} diff --git a/core/src/main/resources/i18n/displayStrings_fa.properties b/core/src/main/resources/i18n/displayStrings_fa.properties index 44693d7e01..b6eb0cc2f4 100644 --- a/core/src/main/resources/i18n/displayStrings_fa.properties +++ b/core/src/main/resources/i18n/displayStrings_fa.properties @@ -4,11 +4,6 @@ # E.g.: [main-view].[component].[description] # In some cases we use enum values or constants to map to display strings -# A annoying issue with property files is that we need to use 2 single quotes in display string -# containing variables (e.g. {0}), otherwise the variable will not be resolved. -# In display string which do not use a variable a single quote is ok. -# E.g. Don''t .... {1} - # We use sometimes dynamic parts which are put together in the code and therefore sometimes use line breaks or spaces # at the end of the string. Please never remove any line breaks or spaces. They are there with a purpose! # To make longer strings with better readable you can make a line break with \ which does not result in a line break @@ -112,7 +107,7 @@ shared.belowInPercent= ٪ زیر قیمت بازار shared.aboveInPercent= ٪ بالای قیمت بازار shared.enterPercentageValue=ارزش ٪ را وارد کنید shared.OR=یا -shared.notEnoughFunds=You don''t have enough funds in your Haveno wallet for this transaction—{0} is needed but only {1} is available.\n\nPlease add funds from an external wallet, or fund your Haveno wallet at Funds > Receive Funds. +shared.notEnoughFunds=You don't have enough funds in your Haveno wallet for this transaction—{0} is needed but only {1} is available.\n\nPlease add funds from an external wallet, or fund your Haveno wallet at Funds > Receive Funds. shared.waitingForFunds=در انتظار دریافت وجه... shared.TheXMRBuyer=خریدار بیتکوین shared.You=شما @@ -359,7 +354,7 @@ offerbook.xmrAutoConf=Is auto-confirm enabled offerbook.buyXmrWith=با XMR خرید کنید: offerbook.sellXmrFor=فروش XMR برای: -offerbook.timeSinceSigning.help=When you successfully complete a trade with a peer who has a signed payment account, your payment account is signed.\n{0} days later, the initial limit of {1} is lifted and your account can sign other peers'' payment accounts. +offerbook.timeSinceSigning.help=When you successfully complete a trade with a peer who has a signed payment account, your payment account is signed.\n{0} days later, the initial limit of {1} is lifted and your account can sign other peers' payment accounts. offerbook.timeSinceSigning.notSigned=Not signed yet offerbook.timeSinceSigning.notSigned.ageDays={0} روز offerbook.timeSinceSigning.notSigned.noNeed=بدون پاسخ @@ -399,7 +394,7 @@ offerbook.warning.counterpartyTradeRestrictions=This offer cannot be taken due t offerbook.warning.newVersionAnnouncement=With this version of the software, trading peers can verify and sign each others' payment accounts to create a network of trusted payment accounts.\n\nAfter successfully trading with a peer with a verified payment account, your payment account will be signed and trading limits will be lifted after a certain time interval (length of this interval is based on the verification method).\n\nFor more information on account signing, please see the documentation at [HYPERLINK:https://docs.haveno.exchange/payment-methods#account-signing]. -popup.warning.tradeLimitDueAccountAgeRestriction.seller=The allowed trade amount is limited to {0} because of security restrictions based on the following criteria:\n- The buyer''s account has not been signed by an arbitrator or a peer\n- The time since signing of the buyer''s account is not at least 30 days\n- The payment method for this offer is considered risky for bank chargebacks\n\n{1} +popup.warning.tradeLimitDueAccountAgeRestriction.seller=The allowed trade amount is limited to {0} because of security restrictions based on the following criteria:\n- The buyer's account has not been signed by an arbitrator or a peer\n- The time since signing of the buyer's account is not at least 30 days\n- The payment method for this offer is considered risky for bank chargebacks\n\n{1} popup.warning.tradeLimitDueAccountAgeRestriction.buyer=The allowed trade amount is limited to {0} because of security restrictions based on the following criteria:\n- Your account has not been signed by an arbitrator or a peer\n- The time since signing of your account is not at least 30 days\n- The payment method for this offer is considered risky for bank chargebacks\n\n{1} popup.warning.tradeLimitDueAccountAgeRestriction.seller.releaseLimit=این روش پرداخت موقتاً تا {1} به {0} محدود شده است زیرا همه خریداران حساب‌های جدیدی دارند.\n\n{2} popup.warning.tradeLimitDueAccountAgeRestriction.seller.exceedsUnsignedBuyLimit=پیشنهاد شما تنها مختص خریدارانی خواهد بود که حساب‌هایی با امضا و سنین پیر دارند زیرا این مبلغ {0} را بیشتر می‌کند.\n\n{1} @@ -643,7 +638,7 @@ portfolio.pending.step2_buyer.postal=لطفاً {0} را توسط \"US Postal Mo # suppress inspection "TrailingSpacesInProperty" portfolio.pending.step2_buyer.payByMail=Please send {0} using \"Pay by Mail\" to the XMR seller. Specific instructions are in the trade contract, or if unclear you may ask questions via trader chat. See more details about Pay by Mail on the Haveno wiki [HYPERLINK:https://docs.haveno.exchange/the-project/payment_methods/Pay_By_Mail].\n\n # suppress inspection "TrailingSpacesInProperty" -portfolio.pending.step2_buyer.pay=Please pay {0} via the specified payment method to the XMR seller. You''ll find the seller's account details on the next screen.\n\n +portfolio.pending.step2_buyer.pay=Please pay {0} via the specified payment method to the XMR seller. You'll find the seller's account details on the next screen.\n\n # suppress inspection "TrailingSpacesInProperty" portfolio.pending.step2_buyer.f2f=لطفا با استفاده از راه‌های ارتباطی ارائه شده توسط فروشنده با وی تماس بگیرید و قرار ملاقاتی را برای پرداخت {0} تنظیم کنید.\n portfolio.pending.step2_buyer.startPaymentUsing=آغاز پرداخت با استفاده از {0} @@ -675,7 +670,7 @@ portfolio.pending.step2_seller.f2fInfo.headline=اطلاعات تماس خرید portfolio.pending.step2_seller.waitPayment.msg=تراکنش سپرده، حداقل یک تأییدیه بلاکچین دارد.شما\nباید تا آغاز پرداخت {0} از جانب خریدار بیتکوین، صبر نمایید. portfolio.pending.step2_seller.warn=خریدار بیت‌کوین هنوز پرداخت {0} را انجام نداده است.\nشما باید تا آغاز پرداخت از جانب او، صبر نمایید.\nاگر معامله تا {1} تکمیل نشد، داور بررسی خواهد کرد. portfolio.pending.step2_seller.openForDispute=The XMR buyer has not started their payment!\nThe max. allowed period for the trade has elapsed.\nYou can wait longer and give the trading peer more time or contact the mediator for assistance. -tradeChat.chatWindowTitle=Chat window for trade with ID ''{0}'' +tradeChat.chatWindowTitle=Chat window for trade with ID '{0}' tradeChat.openChat=Open chat window tradeChat.rules=You can communicate with your trade peer to resolve potential problems with this trade.\nIt is not mandatory to reply in the chat.\nIf a trader violates any of the rules below, open a dispute and report it to the mediator or arbitrator.\n\nChat rules:\n\t● Do not send any links (risk of malware). You can send the transaction ID and the name of a block explorer.\n\t● Do not send your seed words, private keys, passwords or other sensitive information!\n\t● Do not encourage trading outside of Haveno (no security).\n\t● Do not engage in any form of social engineering scam attempts.\n\t● If a peer is not responding and prefers to not communicate via chat, respect their decision.\n\t● Keep conversation scope limited to the trade. This chat is not a messenger replacement or troll-box.\n\t● Keep conversation friendly and respectful. @@ -715,7 +710,7 @@ portfolio.pending.step3_seller.westernUnion=خریدار باید MTCN (شمار portfolio.pending.step3_seller.halCash=خریدار باید کد HalCash را برای شما با پیامک بفرستد. علاوه‌ برآن شما از HalCash پیامی را محتوی اطلاعات موردنیاز برای برداشت EUR از خودپردازهای پشتیبان HalCash دریافت خواهید کرد.\n\nپس از اینکه پول را از دستگاه خودپرداز دریافت کردید، لطفا در اینجا رسید پرداخت را تایید کنید. portfolio.pending.step3_seller.amazonGiftCard=The buyer has sent you an Amazon eGift Card by email or by text message to your mobile phone. Please redeem now the Amazon eGift Card at your Amazon account and once accepted confirm the payment receipt. -portfolio.pending.step3_seller.bankCheck=\n\nPlease also verify that the name of the sender specified on the trade contract matches the name that appears on your bank statement:\nSender''s name, per trade contract: {0}\n\nIf the names are not exactly the same, {1} +portfolio.pending.step3_seller.bankCheck=\n\nPlease also verify that the name of the sender specified on the trade contract matches the name that appears on your bank statement:\nSender's name, per trade contract: {0}\n\nIf the names are not exactly the same, {1} # suppress inspection "TrailingSpacesInProperty" portfolio.pending.step3_seller.openDispute=don't confirm payment receipt. Instead, open a dispute by pressing \"alt + o\" or \"option + o\".\n\n portfolio.pending.step3_seller.confirmPaymentReceipt=تأیید رسید پرداخت @@ -737,7 +732,7 @@ portfolio.pending.step3_seller.openForDispute=You have not confirmed the receipt # suppress inspection "TrailingSpacesInProperty" portfolio.pending.step3_seller.onPaymentReceived.part1=آیا وجه {0} را از شریک معاملاتی خود دریافت کرده‌اید؟\n\n # suppress inspection "TrailingSpacesInProperty" -portfolio.pending.step3_seller.onPaymentReceived.name=Please also verify that the name of the sender specified on the trade contract matches the name that appears on your bank statement:\nSender''s name, per trade contract: {0}\n\nIf the names are not exactly the same, don''t confirm payment receipt. Instead, open a dispute by pressing \"alt + o\" or \"option + o\".\n\n +portfolio.pending.step3_seller.onPaymentReceived.name=Please also verify that the name of the sender specified on the trade contract matches the name that appears on your bank statement:\nSender's name, per trade contract: {0}\n\nIf the names are not exactly the same, don't confirm payment receipt. Instead, open a dispute by pressing \"alt + o\" or \"option + o\".\n\n # suppress inspection "TrailingSpacesInProperty" portfolio.pending.step3_seller.onPaymentReceived.note=Please note, that as soon you have confirmed the receipt, the locked trade amount will be released to the XMR buyer and the security deposit will be refunded.\n\n portfolio.pending.step3_seller.onPaymentReceived.confirm.headline=تأیید کنید که وجه را دریافت کرده‌اید @@ -811,9 +806,9 @@ portfolio.pending.mediationResult.info.selfAccepted=You have accepted the mediat portfolio.pending.mediationResult.info.peerAccepted=Your trade peer has accepted the mediator's suggestion. Do you accept as well? portfolio.pending.mediationResult.button=View proposed resolution portfolio.pending.mediationResult.popup.headline=Mediation result for trade with ID: {0} -portfolio.pending.mediationResult.popup.headline.peerAccepted=Your trade peer has accepted the mediator''s suggestion for trade {0} -portfolio.pending.mediationResult.popup.info=The mediator has suggested the following payout:\nYou receive: {0}\nYour trading peer receives: {1}\n\nYou can accept or reject this suggested payout.\n\nBy accepting, you sign the proposed payout transaction. If your trading peer also accepts and signs, the payout will be completed, and the trade will be closed.\n\nIf one or both of you reject the suggestion, you will have to wait until {2} (block {3}) to open a second-round dispute with an arbitrator who will investigate the case again and do a payout based on their findings.\n\nThe arbitrator may charge a small fee (fee maximum: the trader''s security deposit) as compensation for their work. Both traders agreeing to the mediator''s suggestion is the happy path—requesting arbitration is meant for exceptional circumstances, such as if a trader is sure the mediator did not make a fair payout suggestion (or if the other peer is unresponsive).\n\nMore details about the new arbitration model: [HYPERLINK:https://docs.haveno.exchange/trading-rules.html#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\nOnce 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\nYou can find more details about the arbitration model at:[HYPERLINK:https://docs.haveno.exchange/trading-rules.html#arbitration] +portfolio.pending.mediationResult.popup.headline.peerAccepted=Your trade peer has accepted the mediator's suggestion for trade {0} +portfolio.pending.mediationResult.popup.info=The mediator has suggested the following payout:\nYou receive: {0}\nYour trading peer receives: {1}\n\nYou can accept or reject this suggested payout.\n\nBy accepting, you sign the proposed payout transaction. If your trading peer also accepts and signs, the payout will be completed, and the trade will be closed.\n\nIf one or both of you reject the suggestion, you will have to wait until {2} (block {3}) to open a second-round dispute with an arbitrator who will investigate the case again and do a payout based on their findings.\n\nThe arbitrator may charge a small fee (fee maximum: the trader's security deposit) as compensation for their work. Both traders agreeing to the mediator's suggestion is the happy path—requesting arbitration is meant for exceptional circumstances, such as if a trader is sure the mediator did not make a fair payout suggestion (or if the other peer is unresponsive).\n\nMore details about the new arbitration model: [HYPERLINK:https://docs.haveno.exchange/trading-rules.html#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\nOnce 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\nYou can find more details about the arbitration model at:[HYPERLINK:https://docs.haveno.exchange/trading-rules.html#arbitration] portfolio.pending.mediationResult.popup.openArbitration=Reject and request arbitration portfolio.pending.mediationResult.popup.alreadyAccepted=You've already accepted @@ -977,7 +972,7 @@ support.sellerMaker=فروشنده/سفارش گذار بیتکوین support.buyerTaker=خریدار/پذیرنده‌ی بیتکوین support.sellerTaker=فروشنده/پذیرنده‌ی بیتکوین -support.initialInfo=Please enter a description of your problem in the text field below. Add as much information as possible to speed up dispute resolution time.\n\nHere is a check list for information you should provide:\n\t● If you are the XMR buyer: Did you make the Fiat or Crypto transfer? If so, did you click the 'payment started' button in the application?\n\t● If you are the XMR seller: Did you receive the Fiat or Crypto payment? If so, did you click the 'payment received' button in the application?\n\t● Which version of Haveno are you using?\n\t● Which operating system are you using?\n\t● If you encountered an issue with failed transactions please consider switching to a new data directory.\n\t Sometimes the data directory gets corrupted and leads to strange bugs. \n\t See: https://docs.haveno.exchange/backup-recovery.html#switch-to-a-new-data-directory\n\nPlease 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● Mediators respond in between 2 days. Arbitrators respond in between 5 business days.\n\t● The maximum period for a dispute is 14 days.\n\t● You need to cooperate with the {1} 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\nYou can read more about the dispute process at: {2} +support.initialInfo=Please enter a description of your problem in the text field below. Add as much information as possible to speed up dispute resolution time.\n\nHere is a check list for information you should provide:\n\t● If you are the XMR buyer: Did you make the Fiat or Crypto transfer? If so, did you click the 'payment started' button in the application?\n\t● If you are the XMR seller: Did you receive the Fiat or Crypto payment? If so, did you click the 'payment received' button in the application?\n\t● Which version of Haveno are you using?\n\t● Which operating system are you using?\n\t● If you encountered an issue with failed transactions please consider switching to a new data directory.\n\t Sometimes the data directory gets corrupted and leads to strange bugs. \n\t See: https://docs.haveno.exchange/backup-recovery.html#switch-to-a-new-data-directory\n\nPlease 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● Mediators respond in between 2 days. Arbitrators respond in between 5 business days.\n\t● The maximum period for a dispute is 14 days.\n\t● You need to cooperate with the {1} 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\nYou can read more about the dispute process at: {2} support.systemMsg=پیغام سیستم: {0} support.youOpenedTicket=شما یک درخواست برای پشتیبانی باز کردید.\n\n{0}\n\nنسخه Haveno شما: {1} support.youOpenedDispute=شما یک درخواست برای یک اختلاف باز کردید.\n\n{0}\n\nنسخه Haveno شما: {1} @@ -985,8 +980,8 @@ support.youOpenedDisputeForMediation=You requested mediation.\n\n{0}\n\nHaveno v support.peerOpenedTicket=Your trading peer has requested support due to technical problems.\n\n{0}\n\nHaveno version: {1} support.peerOpenedDispute=Your trading peer has requested a dispute.\n\n{0}\n\nHaveno version: {1} support.peerOpenedDisputeForMediation=Your trading peer has requested mediation.\n\n{0}\n\nHaveno version: {1} -support.mediatorsDisputeSummary=System message: Mediator''s dispute summary:\n{0} -support.mediatorsAddress=Mediator''s node address: {0} +support.mediatorsDisputeSummary=System message: Mediator's dispute summary:\n{0} +support.mediatorsAddress=Mediator's node address: {0} support.warning.disputesWithInvalidDonationAddress=The delayed payout transaction has used an invalid receiver address. It does not match any of the DAO parameter values for the valid donation addresses.\n\nThis might be a scam attempt. Please inform the developers about that incident and do not close that case before the situation is resolved!\n\nAddress used in the dispute: {0}\n\nAll DAO param donation addresses: {1}\n\nTrade ID: {2}{3} support.warning.disputesWithInvalidDonationAddress.mediator=\n\nDo you still want to close the dispute? support.warning.disputesWithInvalidDonationAddress.refundAgent=\n\nYou must not do the payout. @@ -1112,19 +1107,19 @@ setting.about.subsystems.label=نسخه‌های زیرسیستم‌ها setting.about.subsystems.val=نسخه ی شبکه: {0}; نسخه ی پیام همتا به همتا: {1}; نسخه ی Local DB: {2}; نسخه پروتکل معامله: {3} setting.about.shortcuts=Short cuts -setting.about.shortcuts.ctrlOrAltOrCmd=''Ctrl + {0}'' or ''alt + {0}'' or ''cmd + {0}'' +setting.about.shortcuts.ctrlOrAltOrCmd='Ctrl + {0}' or 'alt + {0}' or 'cmd + {0}' setting.about.shortcuts.menuNav=Navigate main menu setting.about.shortcuts.menuNav.value=To navigate the main menu press: 'Ctrl' or 'alt' or 'cmd' with a numeric key between '1-9' setting.about.shortcuts.close=Close Haveno -setting.about.shortcuts.close.value=''Ctrl + {0}'' or ''cmd + {0}'' or ''Ctrl + {1}'' or ''cmd + {1}'' +setting.about.shortcuts.close.value='Ctrl + {0}' or 'cmd + {0}' or 'Ctrl + {1}' or 'cmd + {1}' setting.about.shortcuts.closePopup=Close popup or dialog window setting.about.shortcuts.closePopup.value='ESCAPE' key setting.about.shortcuts.chatSendMsg=Send trader chat message -setting.about.shortcuts.chatSendMsg.value=''Ctrl + ENTER'' or ''alt + ENTER'' or ''cmd + ENTER'' +setting.about.shortcuts.chatSendMsg.value='Ctrl + ENTER' or 'alt + ENTER' or 'cmd + ENTER' setting.about.shortcuts.openDispute=Open dispute setting.about.shortcuts.openDispute.value=Select pending trade and click: {0} @@ -1199,7 +1194,7 @@ account.arbitratorRegistration.registerSuccess=You have successfully registered account.arbitratorRegistration.registerFailed=Could not complete registration.{0} account.crypto.yourCryptoAccounts=حساب‌های آلت‌کوین شما -account.crypto.popup.wallet.msg=Please be sure that you follow the requirements for the usage of {0} wallets as described on the {1} web page.\nUsing wallets from centralized exchanges where (a) you don''t control your keys or (b) which don''t use compatible wallet software is risky: it can lead to loss of the traded funds!\nThe mediator or arbitrator is not a {2} specialist and cannot help in such cases. +account.crypto.popup.wallet.msg=Please be sure that you follow the requirements for the usage of {0} wallets as described on the {1} web page.\nUsing wallets from centralized exchanges where (a) you don't control your keys or (b) which don't use compatible wallet software is risky: it can lead to loss of the traded funds!\nThe mediator or arbitrator is not a {2} specialist and cannot help in such cases. account.crypto.popup.wallet.confirm=من می فهمم و تأیید می کنم که می دانم از کدام کیف پول باید استفاده کنم. # suppress inspection "UnusedProperty" account.crypto.popup.upx.msg=Trading UPX on Haveno requires that you understand and fulfill the following requirements:\n\nFor sending UPX, you need to use either the official uPlexa GUI wallet or uPlexa CLI wallet with the store-tx-info flag enabled (default in new versions). Please be sure you can access the tx key as that would be required in case of a dispute.\nuplexa-wallet-cli (use the command get_tx_key)\nuplexa-wallet-gui (go to history tab and click on the (P) button for payment proof)\n\nAt normal block explorers the transfer is not verifiable.\n\nYou need to provide the arbitrator the following data in case of a dispute:\n- The tx private key\n- The transaction hash\n- The recipient's public address\n\nFailure to provide the above data, or if you used an incompatible wallet, will result in losing the dispute case. The UPX sender is responsible for providing verification of the UPX transfer to the arbitrator in case of a dispute.\n\nThere is no payment ID required, just the normal public address.\nIf you are not sure about that process visit uPlexa discord channel (https://discord.gg/vhdNSrV) or the uPlexa Telegram Chat (https://t.me/uplexaOfficial) to find more information. @@ -1314,7 +1309,7 @@ account.notifications.marketAlert.manageAlerts.header.offerType=نوع پیشن account.notifications.marketAlert.message.title=هشدار پیشنهاد account.notifications.marketAlert.message.msg.below=پایین account.notifications.marketAlert.message.msg.above=بالای -account.notifications.marketAlert.message.msg=پیشنهاد جدید ''{0} {1}'' با قیمت {2} ({3} {4} قیمت بازار) و روش پرداخت ''{5}'' در دفتر پیشنهادات Haveno منتشر شده است.\nشناسه پیشنهاد: {6}. +account.notifications.marketAlert.message.msg=پیشنهاد جدید '{0} {1}' با قیمت {2} ({3} {4} قیمت بازار) و روش پرداخت '{5}' در دفتر پیشنهادات Haveno منتشر شده است.\nشناسه پیشنهاد: {6}. account.notifications.priceAlert.message.title=هشدار قیمت برای {0} account.notifications.priceAlert.message.msg=هشدار قیمت شما فعال شده است. قیمت {0} فعلی {1} {2} است account.notifications.noWebCamFound.warning=دوبین پیدا نشد.\n\nلطفا از گزینه ایمیل برای ارسال توکن و کلید رمزنگاری از تلفن همراهتان به برنامه Haveno استفاده کنید. @@ -1630,7 +1625,7 @@ popup.warning.priceRelay=رله قیمت popup.warning.seed=دانه popup.warning.mandatoryUpdate.trading=Please update to the latest Haveno version. A mandatory update was released which disables trading for old versions. Please check out the Haveno Forum for more information. popup.warning.noFilter=ما شیء فیلتر را از گره‌های اولیه دریافت نکردیم. لطفاً به مدیران شبکه اطلاع دهید که یک شیء فیلتر ثبت کنند. -popup.warning.burnXMR=This transaction is not possible, as the mining fees of {0} would exceed the amount to transfer of {1}. Please wait until the mining fees are low again or until you''ve accumulated more XMR to transfer. +popup.warning.burnXMR=This transaction is not possible, as the mining fees of {0} would exceed the amount to transfer of {1}. Please wait until the mining fees are low again or until you've accumulated more XMR to transfer. popup.warning.openOffer.makerFeeTxRejected=The maker fee transaction for offer with ID {0} was rejected by the Monero network.\nTransaction ID={1}.\nThe offer has been removed to avoid further problems.\nPlease go to \"Settings/Network info\" and do a SPV resync.\nFor further help please contact the Haveno support channel at the Haveno Keybase team. @@ -1679,8 +1674,8 @@ popup.accountSigning.signAccounts.ECKey.error=Bad arbitrator ECKey popup.accountSigning.success.headline=Congratulations popup.accountSigning.success.description=All {0} payment accounts were successfully signed! popup.accountSigning.generalInformation=You'll find the signing state of all your accounts in the account section.\n\nFor further information, please visit [HYPERLINK:https://docs.haveno.exchange/payment-methods#account-signing]. -popup.accountSigning.signedByArbitrator=One of your payment accounts has been verified and signed by an arbitrator. Trading with this account will automatically sign your trading peer''s account after a successful trade.\n\n{0} -popup.accountSigning.signedByPeer=One of your payment accounts has been verified and signed by a trading peer. Your initial trading limit will be lifted and you''ll be able to sign other accounts in {0} days from now.\n\n{1} +popup.accountSigning.signedByArbitrator=One of your payment accounts has been verified and signed by an arbitrator. Trading with this account will automatically sign your trading peer's account after a successful trade.\n\n{0} +popup.accountSigning.signedByPeer=One of your payment accounts has been verified and signed by a trading peer. Your initial trading limit will be lifted and you'll be able to sign other accounts in {0} days from now.\n\n{1} popup.accountSigning.peerLimitLifted=The initial limit for one of your accounts has been lifted.\n\n{0} popup.accountSigning.peerSigner=One of your accounts is mature enough to sign other payment accounts and the initial limit for one of your accounts has been lifted.\n\n{0} @@ -1975,8 +1970,8 @@ payment.accountType=نوع حساب payment.checking=بررسی payment.savings=اندوخته ها payment.personalId=شناسه شخصی -payment.zelle.info=Zelle is a money transfer service that works best *through* another bank.\n\n1. Check this page to see if (and how) your bank works with Zelle: [HYPERLINK:https://www.zellepay.com/get-started]\n\n2. Take special note of your transfer limits—sending limits vary by bank, and banks often specify separate daily, weekly, and monthly limits.\n\n3. If your bank does not work with Zelle, you can still use it through the Zelle mobile app, but your transfer limits will be much lower.\n\n4. The name specified on your Haveno account MUST match the name on your Zelle/bank account. \n\nIf you cannot complete a Zelle transaction as specified in your trade contract, you may lose some (or all) of your security deposit.\n\nBecause of Zelle''s somewhat higher chargeback risk, sellers are advised to contact unsigned buyers through email or SMS to verify that the buyer really owns the Zelle account specified in Haveno. -payment.fasterPayments.newRequirements.info=Some banks have started verifying the receiver''s full name for Faster Payments transfers. Your current Faster Payments account does not specify a full name.\n\nPlease consider recreating your Faster Payments account in Haveno to provide future {0} buyers with a full name.\n\nWhen you recreate the account, make sure to copy the precise sort code, account number and account age verification salt values from your old account to your new account. This will ensure your existing account''s age and signing status are preserved. +payment.zelle.info=Zelle is a money transfer service that works best *through* another bank.\n\n1. Check this page to see if (and how) your bank works with Zelle: [HYPERLINK:https://www.zellepay.com/get-started]\n\n2. Take special note of your transfer limits—sending limits vary by bank, and banks often specify separate daily, weekly, and monthly limits.\n\n3. If your bank does not work with Zelle, you can still use it through the Zelle mobile app, but your transfer limits will be much lower.\n\n4. The name specified on your Haveno account MUST match the name on your Zelle/bank account. \n\nIf you cannot complete a Zelle transaction as specified in your trade contract, you may lose some (or all) of your security deposit.\n\nBecause of Zelle's somewhat higher chargeback risk, sellers are advised to contact unsigned buyers through email or SMS to verify that the buyer really owns the Zelle account specified in Haveno. +payment.fasterPayments.newRequirements.info=Some banks have started verifying the receiver's full name for Faster Payments transfers. Your current Faster Payments account does not specify a full name.\n\nPlease consider recreating your Faster Payments account in Haveno to provide future {0} buyers with a full name.\n\nWhen you recreate the account, make sure to copy the precise sort code, account number and account age verification salt values from your old account to your new account. This will ensure your existing account's age and signing status are preserved. payment.moneyGram.info=When using MoneyGram the XMR buyer has to send the Authorisation number and a photo of the receipt by email to the XMR seller. The receipt must clearly show the seller's full name, country, state and the amount. The seller's email will be displayed to the buyer during the trade process. payment.westernUnion.info=When using Western Union the XMR buyer has to send the MTCN (tracking number) and a photo of the receipt by email to the XMR seller. The receipt must clearly show the seller's full name, city, country and the amount. The seller's email will be displayed to the buyer during the trade process. payment.halCash.info=زمانی که از HalCash استفاده می‌کنید، خریدار باید کد HalCash را از طریق پیام کوتاه موبایل به فروشنده XMR ارسال کند.\n\nلطفا مطمئن شوید که از حداکثر میزانی که بانک شما برای انتقال از طریق HalCash مجاز می‌داند تجاوز نکرده‌اید. حداقل مقداردر هر برداشت معادل 10 یورو و حداکثر مقدار 600 یورو می‌باشد. این محدودیت برای برداشت‌های تکراری برای هر گیرنده در روز 3000 یورو و در ماه 6000 یورو می‌باشد. لطفا این محدودیت‌ها را با بانک خود مطابقت دهید و مطمئن شوید که آنها هم همین محدودی‌ها را دارند.\n\nمقدار برداشت باید شریبی از 10 یورو باشد چرا که مقادیر غیر از این را نمی‌توانید از طریق ATM برداشت کنید. رابط کاربری در صفحه ساخت پینشهاد و پذیرش پیشنهاد مقدار XMR را به گونه‌ای تنظیم می‌کنند که مقدار EUR درست باشد. شما نمی‌توانید از قیمت بر مبنای بازار استفاده کنید چون مقدار یورو با تغییر قیمت‌ها عوض خواهد شد.\n\nدر صورت بروز اختلاف خریدار XMR باید شواهد مربوط به ارسال یورو را ارائه دهد. @@ -1988,7 +1983,7 @@ payment.limits.info.withSigning=To limit chargeback risk, Haveno sets per-trade payment.cashDeposit.info=لطفا مطمئن شوید که بانک شما اجازه پرداخت سپرده نفد به حساب دیگر افراد را می‌دهد. برای مثال، Bank of America و Wells Fargo دیگر اجازه چنین پرداخت‌هایی را نمی‌دهند. payment.revolut.info=Revolut requires the 'Username' as account ID not the phone number or email as it was the case in the past. -payment.account.revolut.addUserNameInfo={0}\nYour existing Revolut account ({1}) does not have a ''Username''.\nPlease enter your Revolut ''Username'' to update your account data.\nThis will not affect your account age signing status. +payment.account.revolut.addUserNameInfo={0}\nYour existing Revolut account ({1}) does not have a 'Username'.\nPlease enter your Revolut 'Username' to update your account data.\nThis will not affect your account age signing status. payment.revolut.addUserNameInfo.headLine=Update Revolut account payment.cashapp.info=لطفاً توجه داشته باشید که Cash App ریسک بازپرداخت بالاتری نسبت به بیشتر انتقالات بانکی دارد. @@ -2022,7 +2017,7 @@ payment.japan.recipient=نام payment.australia.payid=PayID payment.payid=PayID linked to financial institution. Like email address or mobile phone. payment.payid.info=A PayID like a phone number, email address or an Australian Business Number (ABN), that you can securely link to your bank, credit union or building society account. You need to have already created a PayID with your Australian financial institution. Both sending and receiving financial institutions must support PayID. For more information please check [HYPERLINK:https://payid.com.au/faqs/] -payment.amazonGiftCard.info=To pay with Amazon eGift Card, you will need to send an Amazon eGift Card to the XMR seller via your Amazon account. \n\nHaveno will show the XMR seller''s email address or phone number where the gift card should be sent, and you must include the trade ID in the gift card''s message field. Please see the wiki [HYPERLINK:https://docs.haveno.exchange/the-project/payment_methods/Amazon_eGift_card] for further details and best practices. \n\nThree important notes:\n- try to send gift cards with amounts of 100 USD or smaller, as Amazon is known to flag larger gift cards as fraudulent\n- try to use creative, believable text for the gift card''s message (e.g., "Happy birthday Susan!") along with the trade ID (and use trader chat to tell your trading peer the reference text you picked so they can verify your payment)\n- Amazon eGift Cards can only be redeemed on the Amazon website they were purchased on (e.g., a gift card purchased on amazon.it can only be redeemed on amazon.it) +payment.amazonGiftCard.info=To pay with Amazon eGift Card, you will need to send an Amazon eGift Card to the XMR seller via your Amazon account. \n\nHaveno will show the XMR seller's email address or phone number where the gift card should be sent, and you must include the trade ID in the gift card's message field. Please see the wiki [HYPERLINK:https://docs.haveno.exchange/the-project/payment_methods/Amazon_eGift_card] for further details and best practices. \n\nThree important notes:\n- try to send gift cards with amounts of 100 USD or smaller, as Amazon is known to flag larger gift cards as fraudulent\n- try to use creative, believable text for the gift card's message (e.g., "Happy birthday Susan!") along with the trade ID (and use trader chat to tell your trading peer the reference text you picked so they can verify your payment)\n- Amazon eGift Cards can only be redeemed on the Amazon website they were purchased on (e.g., a gift card purchased on amazon.it can only be redeemed on amazon.it) payment.paysafe.info=برای حفاظت از شما، به شدت از استفاده از پین‌های Paysafecard برای پرداخت جلوگیری می‌کنیم.\n\n\ تراکنش‌های انجام شده از طریق پین‌ها نمی‌توانند به طور مستقل برای حل اختلاف تأیید شوند. اگر مشکلی پیش آید، بازیابی وجوه ممکن است غیرممکن باشد.\n\n\ برای اطمینان از امنیت تراکنش و حل اختلاف، همیشه از روش‌های پرداختی استفاده کنید که سوابق قابل تاییدی ارائه می‌دهند. diff --git a/core/src/main/resources/i18n/displayStrings_fr.properties b/core/src/main/resources/i18n/displayStrings_fr.properties index 478ab4c6c3..5d11a8a6fb 100644 --- a/core/src/main/resources/i18n/displayStrings_fr.properties +++ b/core/src/main/resources/i18n/displayStrings_fr.properties @@ -4,11 +4,6 @@ # E.g.: [main-view].[component].[description] # In some cases we use enum values or constants to map to display strings -# A annoying issue with property files is that we need to use 2 single quotes in display string -# containing variables (e.g. {0}), otherwise the variable will not be resolved. -# In display string which do not use a variable a single quote is ok. -# E.g. Don''t .... {1} - # We use sometimes dynamic parts which are put together in the code and therefore sometimes use line breaks or spaces # at the end of the string. Please never remove any line breaks or spaces. They are there with a purpose! # To make longer strings with better readable you can make a line break with \ which does not result in a line break @@ -146,7 +141,7 @@ shared.createNewAccountDescription=Les détails de votre compte sont stockés lo shared.saveNewAccount=Sauvegarder un nouveau compte shared.selectedAccount=Sélectionner un compte shared.deleteAccount=Supprimer le compte -shared.errorMessageInline=\nMessage d''erreur: {0} +shared.errorMessageInline=\nMessage d'erreur: {0} shared.errorMessage=Message d'erreur shared.information=Information shared.name=Nom @@ -171,14 +166,14 @@ shared.enterPrivKey=Entrer la clé privée pour déverrouiller shared.payoutTxId=ID du versement de la transaction shared.contractAsJson=Contrat au format JSON shared.viewContractAsJson=Voir le contrat en format JSON -shared.contract.title=Contrat pour la transaction avec l''ID : {0} +shared.contract.title=Contrat pour la transaction avec l'ID : {0} shared.paymentDetails=XMR {0} détails du paiement shared.securityDeposit=Dépôt de garantie shared.yourSecurityDeposit=Votre dépôt de garantie shared.contract=Contrat shared.messageArrived=Message reçu. shared.messageStoredInMailbox=Message stocké dans la boîte de réception. -shared.messageSendingFailed=Échec de l''envoi du message. Erreur: {0} +shared.messageSendingFailed=Échec de l'envoi du message. Erreur: {0} shared.unlock=Déverrouiller shared.toReceive=à recevoir shared.toSpend=à dépenser @@ -277,7 +272,7 @@ mainView.p2pNetworkWarnMsg.noNodesAvailable=Il n'y a pas de noeud de seed ou de mainView.p2pNetworkWarnMsg.connectionToP2PFailed=La connexion au réseau Haveno a échoué (erreur signalé: {0}).\nVeuillez vérifier votre connexion internet ou essayez de redémarrer l'application. mainView.walletServiceErrorMsg.timeout=La connexion au réseau Monero a échoué car le délai d'attente a expiré. -mainView.walletServiceErrorMsg.connectionError=La connexion au réseau Monero a échoué à cause d''une erreur: {0} +mainView.walletServiceErrorMsg.connectionError=La connexion au réseau Monero a échoué à cause d'une erreur: {0} mainView.walletServiceErrorMsg.rejectedTxException=Le réseau a rejeté une transaction.\n\n{0} @@ -331,8 +326,8 @@ market.trades.showVolumeInUSD=Afficher le volume en USD offerbook.createOffer=Créer un ordre offerbook.takeOffer=Accepter un ordre -offerbook.takeOfferToBuy=Accepter l''ordre d''achat {0} -offerbook.takeOfferToSell=Accepter l''ordre de vente {0} +offerbook.takeOfferToBuy=Accepter l'ordre d'achat {0} +offerbook.takeOfferToSell=Accepter l'ordre de vente {0} offerbook.takeOffer.enterChallenge=Entrez la phrase secrète de l'offre offerbook.trader=Échanger offerbook.offerersBankId=ID de la banque du maker (BIC/SWIFT): {0} @@ -359,7 +354,7 @@ offerbook.xmrAutoConf=Est-ce-que la confirmation automatique est activée offerbook.buyXmrWith=Acheter XMR avec : offerbook.sellXmrFor=Vendre XMR pour : -offerbook.timeSinceSigning.help=Lorsque vous effectuez avec succès une transaction avec un pair disposant d''un compte de paiement signé, votre compte de paiement est signé.\n{0} Jours plus tard, la limite initiale de {1} est levée et votre compte peut signer les comptes de paiement d''un autre pair. +offerbook.timeSinceSigning.help=Lorsque vous effectuez avec succès une transaction avec un pair disposant d'un compte de paiement signé, votre compte de paiement est signé.\n{0} Jours plus tard, la limite initiale de {1} est levée et votre compte peut signer les comptes de paiement d'un autre pair. offerbook.timeSinceSigning.notSigned=Pas encore signé offerbook.timeSinceSigning.notSigned.ageDays={0} jours offerbook.timeSinceSigning.notSigned.noNeed=N/A @@ -368,27 +363,27 @@ shared.notSigned.noNeed=Ce type de compte ne nécessite pas de signature shared.notSigned.noNeedDays=Ce type de compte ne nécessite pas de signature et a été créée il y'a {0} jours shared.notSigned.noNeedAlts=Les comptes pour crypto ne supportent pas la signature ou le vieillissement -offerbook.nrOffers=Nombre d''ordres: {0} +offerbook.nrOffers=Nombre d'ordres: {0} offerbook.volume={0} (min - max) offerbook.deposit=Déposer XMR (%) offerbook.deposit.help=Les deux parties à la transaction ont payé un dépôt pour assurer que la transaction se déroule normalement. Ce montant sera remboursé une fois la transaction terminée. offerbook.createNewOffer=Créer une offre à {0} {1} -offerbook.createOfferToBuy=Créer un nouvel ordre d''achat pour {0} +offerbook.createOfferToBuy=Créer un nouvel ordre d'achat pour {0} offerbook.createOfferToSell=Créer un nouvel ordre de vente pour {0} -offerbook.createOfferToBuy.withTraditional=Créer un nouvel ordre d''achat pour {0} avec {1} +offerbook.createOfferToBuy.withTraditional=Créer un nouvel ordre d'achat pour {0} avec {1} offerbook.createOfferToSell.forTraditional=Créer un nouvel ordre de vente pour {0} for {1} offerbook.createOfferToBuy.withCrypto=Créer un nouvel ordre de vente pour {0} (achat{1}) -offerbook.createOfferToSell.forCrypto=Créer un nouvel ordre d''achat pour {0} (vente{1}) +offerbook.createOfferToSell.forCrypto=Créer un nouvel ordre d'achat pour {0} (vente{1}) offerbook.takeOfferButton.tooltip=Accepter un ordre pour {0} offerbook.yesCreateOffer=Oui, créer un ordre offerbook.setupNewAccount=Configurer un nouveau compte de change offerbook.removeOffer.success=L'ordre a bien été retiré. -offerbook.removeOffer.failed=Le retrait de l''ordre a échoué:\n{0} -offerbook.deactivateOffer.failed=La désactivation de l''ordre a échoué:\n{0} -offerbook.activateOffer.failed=La publication de l''ordre a échoué:\n{0} -offerbook.withdrawFundsHint=Vous pouvez retirer les fonds investis depuis l''écran {0}. +offerbook.removeOffer.failed=Le retrait de l'ordre a échoué:\n{0} +offerbook.deactivateOffer.failed=La désactivation de l'ordre a échoué:\n{0} +offerbook.activateOffer.failed=La publication de l'ordre a échoué:\n{0} +offerbook.withdrawFundsHint=Vous pouvez retirer les fonds investis depuis l'écran {0}. offerbook.warning.noTradingAccountForCurrency.headline=Aucun compte de paiement pour la devise sélectionnée offerbook.warning.noTradingAccountForCurrency.msg=Vous n'avez pas de compte de paiement mis en place pour la devise sélectionnée.\n\nVoudriez-vous créer une offre pour une autre devise à la place? @@ -399,8 +394,8 @@ offerbook.warning.counterpartyTradeRestrictions=Cette offre ne peut être accept offerbook.warning.newVersionAnnouncement=Grâce à cette version du logiciel, les partenaires commerciaux peuvent confirmer et vérifier les comptes de paiement de chacun pour créer un réseau de comptes de paiement de confiance.\n\nUne fois la transaction réussie, votre compte de paiement sera vérifié et les restrictions de transaction seront levées après une certaine période de temps (cette durée est basée sur la méthode de vérification).\n\nPour plus d'informations sur la vérification de votre compte, veuillez consulter le document sur https://docs.haveno.exchange/payment-methods#account-signing -popup.warning.tradeLimitDueAccountAgeRestriction.seller=Le montant de transaction autorisé est limité à {0} en raison des restrictions de sécurité basées sur les critères suivants:\n- Le compte de l''acheteur n''a pas été signé par un arbitre ou par un pair\n- Le délai depuis la signature du compte de l''acheteur est inférieur à 30 jours\n- Le mode de paiement pour cette offre est considéré comme présentant un risque de rétrofacturation bancaire\n\n{1} -popup.warning.tradeLimitDueAccountAgeRestriction.buyer=Le montant de transaction autorisé est limité à {0} en raison des restrictions de sécurité basées sur les critères suivants:\n- Votre compte n''a pas été signé par un arbitre ou par un pair\n- Le délai depuis la signature de votre compte est inférieur à 30 jours\n- Le mode de paiement pour cette offre est considéré comme présentant un risque de rétrofacturation bancaire\n\n{1} +popup.warning.tradeLimitDueAccountAgeRestriction.seller=Le montant de transaction autorisé est limité à {0} en raison des restrictions de sécurité basées sur les critères suivants:\n- Le compte de l'acheteur n'a pas été signé par un arbitre ou par un pair\n- Le délai depuis la signature du compte de l'acheteur est inférieur à 30 jours\n- Le mode de paiement pour cette offre est considéré comme présentant un risque de rétrofacturation bancaire\n\n{1} +popup.warning.tradeLimitDueAccountAgeRestriction.buyer=Le montant de transaction autorisé est limité à {0} en raison des restrictions de sécurité basées sur les critères suivants:\n- Votre compte n'a pas été signé par un arbitre ou par un pair\n- Le délai depuis la signature de votre compte est inférieur à 30 jours\n- Le mode de paiement pour cette offre est considéré comme présentant un risque de rétrofacturation bancaire\n\n{1} popup.warning.tradeLimitDueAccountAgeRestriction.seller.releaseLimit=Ce mode de paiement est temporairement limité à {0} jusqu'à {1} car tous les acheteurs ont de nouveaux comptes.\n\n{2} popup.warning.tradeLimitDueAccountAgeRestriction.seller.exceedsUnsignedBuyLimit=Votre offre sera limitée aux acheteurs avec des comptes signés et anciens car elle dépasse {0}.\n\n{1} @@ -421,7 +416,7 @@ offerbook.info.sellAboveMarketPrice=Vous obtiendrez {0} de plus que le prix actu offerbook.info.buyBelowMarketPrice=Vous paierez {0} de moins que le prix actuel du marché (mis à jour chaque minute). offerbook.info.buyAtFixedPrice=Vous achèterez à ce prix déterminé. offerbook.info.sellAtFixedPrice=Vous vendrez à ce prix déterminé. -offerbook.info.noArbitrationInUserLanguage=En cas de litige, veuillez noter que l''arbitrage de cet ordre sera traité par {0}. La langue est actuellement définie sur {1}. +offerbook.info.noArbitrationInUserLanguage=En cas de litige, veuillez noter que l'arbitrage de cet ordre sera traité par {0}. La langue est actuellement définie sur {1}. offerbook.info.roundedFiatVolume=Le montant a été arrondi pour accroître la confidentialité de votre transaction. #################################################################### @@ -440,7 +435,7 @@ createOffer.fundsBox.title=Financer votre ordre createOffer.fundsBox.offerFee=Frais de transaction createOffer.fundsBox.networkFee=Frais de minage createOffer.fundsBox.placeOfferSpinnerInfo=Publication de l'ordre en cours ... -createOffer.fundsBox.paymentLabel=Transaction Haveno avec l''ID {0} +createOffer.fundsBox.paymentLabel=Transaction Haveno avec l'ID {0} createOffer.fundsBox.fundsStructure=({0} dépôt de garantie, {1} frais de transaction, {2} frais de minage) createOffer.success.headline=Votre offre a été créée createOffer.success.info=Vous pouvez gérer vos ordres en cours dans \"Portfolio/Mes ordres\". @@ -472,7 +467,7 @@ createOffer.createOfferFundWalletInfo.msg=Vous devez déposer {0} à cette offre - Frais de transaction : {3} # only first part "An error occurred when placing the offer:" has been used before. We added now the rest (need update in existing translations!) -createOffer.amountPriceBox.error.message=Une erreur s''est produite lors du placement de cet ordre:\n\n{0}\n\nAucun fonds n''a été prélevé sur votre portefeuille pour le moment.\nVeuillez redémarrer l''application et vérifier votre connexion réseau. +createOffer.amountPriceBox.error.message=Une erreur s'est produite lors du placement de cet ordre:\n\n{0}\n\nAucun fonds n'a été prélevé sur votre portefeuille pour le moment.\nVeuillez redémarrer l'application et vérifier votre connexion réseau. createOffer.setAmountPrice=Définir le montant et le prix createOffer.warnCancelOffer=Vous avez déjà financé cet ordre.\nSi vous annulez maintenant, vos fonds seront envoyés dans votre portefeuille haveno local et seront disponible pour retrait dans l'onglet \"Fonds/Envoyer des fonds\".\nÊtes-vous certain de vouloir annuler ? createOffer.timeoutAtPublishing=Un timeout est survenu au moment de la publication de l'ordre. @@ -482,7 +477,7 @@ createOffer.tooLowSecDeposit.makerIsSeller=Ceci vous donne moins de protection d createOffer.tooLowSecDeposit.makerIsBuyer=cela offre moins de protection pour le pair que de suivre le protocole de trading car vous avez moins de dépôt à risque. D'autres utilisateurs préféreront peut-être accepter d'autres ordres que le vôtre. createOffer.resetToDefault=Non, revenir à la valeur par défaut createOffer.useLowerValue=Oui, utiliser ma valeur la plus basse -createOffer.priceOutSideOfDeviation=Le prix que vous avez fixé est en dehors de l''écart max. du prix du marché autorisé\nL''écart maximum autorisé est {0} et peut être ajusté dans les préférences. +createOffer.priceOutSideOfDeviation=Le prix que vous avez fixé est en dehors de l'écart max. du prix du marché autorisé\nL'écart maximum autorisé est {0} et peut être ajusté dans les préférences. createOffer.changePrice=Modifier le prix createOffer.tac=En plaçant cet ordre vous acceptez d'effectuer des transactions avec n'importe quel trader remplissant les conditions affichées à l'écran. createOffer.currencyForFee=Frais de transaction @@ -490,7 +485,7 @@ createOffer.setDeposit=Etablir le dépôt de garantie de l'acheteur (%) createOffer.setDepositAsBuyer=Définir mon dépôt de garantie en tant qu'acheteur (%) createOffer.setDepositForBothTraders=Établissez le dépôt de sécurité des deux traders (%) createOffer.securityDepositInfo=Le dépôt de garantie de votre acheteur sera de {0} -createOffer.securityDepositInfoAsBuyer=Votre dépôt de garantie en tant qu''acheteur sera de {0} +createOffer.securityDepositInfoAsBuyer=Votre dépôt de garantie en tant qu'acheteur sera de {0} createOffer.minSecurityDepositUsed=Le dépôt de sécurité minimum est utilisé createOffer.buyerAsTakerWithoutDeposit=Aucun dépôt requis de la part de l'acheteur (protégé par un mot de passe) createOffer.myDeposit=Mon dépôt de garantie (%) @@ -516,16 +511,16 @@ takeOffer.fundsBox.tradeAmount=Montant à vendre takeOffer.fundsBox.offerFee=Frais de transaction du trade takeOffer.fundsBox.networkFee=Total des frais de minage takeOffer.fundsBox.takeOfferSpinnerInfo=Acceptation de l'offre : {0} -takeOffer.fundsBox.paymentLabel=Transaction Haveno avec l''ID {0} +takeOffer.fundsBox.paymentLabel=Transaction Haveno avec l'ID {0} takeOffer.fundsBox.fundsStructure=({0} dépôt de garantie, {1} frais de transaction, {2} frais de minage) takeOffer.fundsBox.noFundingRequiredTitle=Aucun financement requis takeOffer.fundsBox.noFundingRequiredDescription=Obtenez la phrase secrète de l'offre auprès du vendeur en dehors de Haveno pour accepter cette offre. takeOffer.success.headline=Vous avez accepté un ordre avec succès. takeOffer.success.info=Vous pouvez voir vos transactions dans \"Portfolio/Échanges en cours\". -takeOffer.error.message=Une erreur s''est produite pendant l’'acceptation de l''ordre.\n\n{0} +takeOffer.error.message=Une erreur s'est produite pendant l’'acceptation de l'ordre.\n\n{0} # new entries -takeOffer.takeOfferButton=Vérifier: Accepter l''ordre de {0} Monero +takeOffer.takeOfferButton=Vérifier: Accepter l'ordre de {0} Monero takeOffer.noPriceFeedAvailable=Vous ne pouvez pas accepter cet ordre, car celui-ci utilise un prix en pourcentage basé sur le prix du marché, mais il n'y a pas de prix de référence de disponible. takeOffer.takeOfferFundWalletInfo.headline=Provisionner votre trade # suppress inspection "TrailingSpacesInProperty" @@ -561,7 +556,7 @@ openOffer.triggered=Cette offre a été désactivée car le prix du marché a at editOffer.setPrice=Définir le prix editOffer.confirmEdit=Confirmation: Modification de l'ordre editOffer.publishOffer=Publication de votre ordre. -editOffer.failed=Échec de la modification de l''ordre:\n{0} +editOffer.failed=Échec de la modification de l'ordre:\n{0} editOffer.success=Votre ordre a été modifié avec succès. editOffer.invalidDeposit=Le dépôt de garantie de l'acheteur ne respecte pas le cadre des contraintes définies par Haveno DAO et ne peut plus être modifié. @@ -633,20 +628,20 @@ portfolio.pending.step2_buyer.crypto=Veuillez transférer à partir de votre por portfolio.pending.step2_buyer.cash=Veuillez vous rendre dans une banque et payer {0} au vendeur de XMR.\n portfolio.pending.step2_buyer.cash.extra=CONDITIONS REQUISES: \nAprès avoir effectué le paiement veuillez écrire sur le reçu papier : PAS DE REMBOURSEMENT.\nPuis déchirer le en 2, prenez en une photo et envoyer le à l'adresse email du vendeur de XMR. # suppress inspection "TrailingSpacesInProperty" -portfolio.pending.step2_buyer.moneyGram=Veuillez s''il vous plaît payer {0} au vendeur de XMR en utilisant MoneyGram.\n\n -portfolio.pending.step2_buyer.moneyGram.extra=CONDITIONS REQUISES:\nAprès avoir effectué le paiement envoyez le numéro d''autorisation et une photo du reçu par e-mail au vendeur de XMR.\nLe reçu doit faire clairement figurer le nom complet du vendeur, son pays, l''état et le montant. Le mail du vendeur est: {0}. +portfolio.pending.step2_buyer.moneyGram=Veuillez s'il vous plaît payer {0} au vendeur de XMR en utilisant MoneyGram.\n\n +portfolio.pending.step2_buyer.moneyGram.extra=CONDITIONS REQUISES:\nAprès avoir effectué le paiement envoyez le numéro d'autorisation et une photo du reçu par e-mail au vendeur de XMR.\nLe reçu doit faire clairement figurer le nom complet du vendeur, son pays, l'état et le montant. Le mail du vendeur est: {0}. # suppress inspection "TrailingSpacesInProperty" -portfolio.pending.step2_buyer.westernUnion=Veuillez s''il vous plaît payer {0} au vendeur de XMR en utilisant Western Union.\n\n -portfolio.pending.step2_buyer.westernUnion.extra=CONDITIONS REQUISES:\nAprès avoir effectué le paiement envoyez le MTCN (numéro de suivi) et une photo du reçu par e-mail au vendeur de XMR.\nLe reçu doit faire clairement figurer le nom complet du vendeur, son pays, l''état et le montant. Le mail du vendeur est: {0}. +portfolio.pending.step2_buyer.westernUnion=Veuillez s'il vous plaît payer {0} au vendeur de XMR en utilisant Western Union.\n\n +portfolio.pending.step2_buyer.westernUnion.extra=CONDITIONS REQUISES:\nAprès avoir effectué le paiement envoyez le MTCN (numéro de suivi) et une photo du reçu par e-mail au vendeur de XMR.\nLe reçu doit faire clairement figurer le nom complet du vendeur, son pays, l'état et le montant. Le mail du vendeur est: {0}. # suppress inspection "TrailingSpacesInProperty" -portfolio.pending.step2_buyer.postal=Merci d''envoyer {0} par \"US Postal Money Order\" au vendeur de XMR.\n\n +portfolio.pending.step2_buyer.postal=Merci d'envoyer {0} par \"US Postal Money Order\" au vendeur de XMR.\n\n # suppress inspection "TrailingSpacesInProperty" portfolio.pending.step2_buyer.payByMail=Veuillez envoyer {0} en utlisant \"Pay by Mail\" au vendeur de XMR. Les instructions spécifiques sont dans le contrat de trade, ou si ce n'est pas clair, vous pouvez poser des questions via le chat des trader. Pour plus de détails sur Pay by Mail, allez sur le wiki Haveno \n[LIEN:https://docs.haveno.exchange/the-project/payment_methods/Pay_By_Mail]\n # suppress inspection "TrailingSpacesInProperty" portfolio.pending.step2_buyer.pay=Veuillez payer {0} via la méthode de paiement spécifiée par le vendeur de XMR. Vous trouverez les informations du compte du vendeur à l'écran suivant.\n\n # suppress inspection "TrailingSpacesInProperty" -portfolio.pending.step2_buyer.f2f=Veuillez s''il vous plaît contacter le vendeur de XMR via le contact fourni, et planifiez un rendez-vous pour effectuer le paiement {0}.\n\n +portfolio.pending.step2_buyer.f2f=Veuillez s'il vous plaît contacter le vendeur de XMR via le contact fourni, et planifiez un rendez-vous pour effectuer le paiement {0}.\n\n portfolio.pending.step2_buyer.startPaymentUsing=Initier le paiement en utilisant {0} portfolio.pending.step2_buyer.recipientsAccountData=Destinataires {0} portfolio.pending.step2_buyer.amountToTransfer=Montant à transférer @@ -654,16 +649,16 @@ portfolio.pending.step2_buyer.sellersAddress=Adresse {0} du vendeur portfolio.pending.step2_buyer.buyerAccount=Votre compte de paiement à utiliser portfolio.pending.step2_buyer.paymentSent=Paiement initié portfolio.pending.step2_buyer.fillInBsqWallet=Payer depuis le portefeuille BSQ -portfolio.pending.step2_buyer.warn=Vous n''avez toujours pas effectué votre {0} paiement !\nVeuillez noter que l''échange doit être achevé avant {1}. +portfolio.pending.step2_buyer.warn=Vous n'avez toujours pas effectué votre {0} paiement !\nVeuillez noter que l'échange doit être achevé avant {1}. portfolio.pending.step2_buyer.openForDispute=Vous n'avez pas effectué votre paiement !\nLe délai maximal alloué pour l'échange est écoulé, veuillez contacter le médiateur pour obtenir de l'aide. portfolio.pending.step2_buyer.paperReceipt.headline=Avez-vous envoyé le reçu papier au vendeur de XMR? portfolio.pending.step2_buyer.paperReceipt.msg=Rappelez-vous: \nVous devez écrire sur le reçu papier: PAS DE REMBOURSEMENT.\nEnsuite, veuillez le déchirer en 2, faire une photo et l'envoyer à l'adresse email du vendeur. portfolio.pending.step2_buyer.moneyGramMTCNInfo.headline=Envoyer le numéro d'autorisation ainsi que le reçu -portfolio.pending.step2_buyer.moneyGramMTCNInfo.msg=Vous devez envoyez le numéro d''autorisation et une photo du reçu par email au vendeur de XMR.\nLe reçu doit faire clairement figurer le nom complet du vendeur, son pays, l''état, et le montant. Le mail du vendeur est: {0}.\n\nAvez-vous envoyé le numéro d''autorisation et le contrat au vendeur ? +portfolio.pending.step2_buyer.moneyGramMTCNInfo.msg=Vous devez envoyez le numéro d'autorisation et une photo du reçu par email au vendeur de XMR.\nLe reçu doit faire clairement figurer le nom complet du vendeur, son pays, l'état, et le montant. Le mail du vendeur est: {0}.\n\nAvez-vous envoyé le numéro d'autorisation et le contrat au vendeur ? portfolio.pending.step2_buyer.westernUnionMTCNInfo.headline=Envoyer le MTCN et le reçu -portfolio.pending.step2_buyer.westernUnionMTCNInfo.msg=Vous devez envoyez le MTCN (numéro de suivi) et une photo du reçu par email au vendeur de XMR.\nLe reçu doit clairement faire figurer le nom complet du vendeur, son pays, l''état et le montant. Le mail du vendeur est: {0}.\n\nAvez-vous envoyé le MTCN et le contrat au vendeur ? +portfolio.pending.step2_buyer.westernUnionMTCNInfo.msg=Vous devez envoyez le MTCN (numéro de suivi) et une photo du reçu par email au vendeur de XMR.\nLe reçu doit clairement faire figurer le nom complet du vendeur, son pays, l'état et le montant. Le mail du vendeur est: {0}.\n\nAvez-vous envoyé le MTCN et le contrat au vendeur ? portfolio.pending.step2_buyer.halCashInfo.headline=Envoyer le code HalCash -portfolio.pending.step2_buyer.halCashInfo.msg=Vous devez envoyez un message au format texte SMS avec le code HalCash ainsi que l''ID de la transaction ({0}) au vendeur de XMR.\nLe numéro de mobile du vendeur est {1}.\n\nAvez-vous envoyé le code au vendeur ? +portfolio.pending.step2_buyer.halCashInfo.msg=Vous devez envoyez un message au format texte SMS avec le code HalCash ainsi que l'ID de la transaction ({0}) au vendeur de XMR.\nLe numéro de mobile du vendeur est {1}.\n\nAvez-vous envoyé le code au vendeur ? portfolio.pending.step2_buyer.fasterPaymentsHolderNameInfo=Certaines banques pourraient vérifier le nom du receveur. Des comptes de paiement plus rapides créés dans des clients Haveno plus anciens ne fournissent pas le nom du receveur, veuillez donc utiliser le chat de trade pour l'obtenir (si nécessaire). portfolio.pending.step2_buyer.confirmStart.headline=Confirmez que vous avez initié le paiement portfolio.pending.step2_buyer.confirmStart.msg=Avez-vous initié le {0} paiement auprès de votre partenaire de trading? @@ -674,10 +669,10 @@ portfolio.pending.step2_buyer.confirmStart.proof.invalidInput=La sasie n'est pas portfolio.pending.step2_buyer.confirmStart.warningButton=Ignorer et continuer tout de même portfolio.pending.step2_seller.waitPayment.headline=En attende du paiement portfolio.pending.step2_seller.f2fInfo.headline=Coordonnées de l'acheteur -portfolio.pending.step2_seller.waitPayment.msg=La transaction de dépôt a été vérifiée au moins une fois sur la blockchain\nVous devez attendre que l''acheteur de XMR lance le {0} payment. -portfolio.pending.step2_seller.warn=L''acheteur de XMR n''a toujours pas effectué le paiement {0}.\nVeuillez attendre qu''il effectue celui-ci.\nSi la transaction n''est pas effectuée le {1}, un arbitre enquêtera. +portfolio.pending.step2_seller.waitPayment.msg=La transaction de dépôt a été vérifiée au moins une fois sur la blockchain\nVous devez attendre que l'acheteur de XMR lance le {0} payment. +portfolio.pending.step2_seller.warn=L'acheteur de XMR n'a toujours pas effectué le paiement {0}.\nVeuillez attendre qu'il effectue celui-ci.\nSi la transaction n'est pas effectuée le {1}, un arbitre enquêtera. portfolio.pending.step2_seller.openForDispute=L'acheteur de XMR n'a pas initié son paiement !\nLa période maximale autorisée pour ce trade est écoulée.\nVous pouvez attendre plus longtemps et accorder plus de temps à votre pair de trading ou contacter le médiateur pour obtenir de l'aide. -tradeChat.chatWindowTitle=Fenêtre de discussion pour la transaction avec l''ID ''{0}'' +tradeChat.chatWindowTitle=Fenêtre de discussion pour la transaction avec l'ID '{0}' tradeChat.openChat=Ouvrir une fenêtre de discussion tradeChat.rules=Vous pouvez communiquer avec votre pair de trading pour résoudre les problèmes potentiels liés à cet échange.\nIl n'est pas obligatoire de répondre sur le chat.\nSi un trader enfreint l'une des règles ci-dessous, ouvrez un litige et signalez-le au médiateur ou à l'arbitre.\n\nRègles sur le chat:\n\t● N'envoyez pas de liens (risque de malware). Vous pouvez envoyer l'ID de transaction et le nom d'un explorateur de blocs.\n\t● N'envoyez pas les mots de votre seed, clés privées, mots de passe ou autre information sensible !\n\t● N'encouragez pas le trading en dehors de Haveno (non sécurisé).\n\t● Ne vous engagez dans aucune forme d'escroquerie d'ingénierie sociale.\n\t● Si un pair ne répond pas et préfère ne pas communiquer par chat, respectez sa décision.\n\t● Limitez la portée de la conversation à l'échange en cours. Ce chat n'est pas une alternative à messenger ou une troll-box.\n\t● Entretenez une conversation amicale et respectueuse. @@ -699,25 +694,25 @@ portfolio.pending.step3_buyer.wait.info=En attente de la confirmation du vendeur portfolio.pending.step3_buyer.wait.msgStateInfo.label=État du message de lancement du paiement portfolio.pending.step3_buyer.warn.part1a=sur la {0} blockchain portfolio.pending.step3_buyer.warn.part1b=chez votre prestataire de paiement (par ex. banque) -portfolio.pending.step3_buyer.warn.part2=Le vendeur de XMR n''a toujours pas confirmé votre paiement. . Veuillez vérifier {0} si l''envoi du paiement a bien fonctionné. +portfolio.pending.step3_buyer.warn.part2=Le vendeur de XMR n'a toujours pas confirmé votre paiement. . Veuillez vérifier {0} si l'envoi du paiement a bien fonctionné. portfolio.pending.step3_buyer.openForDispute=Le vendeur de XMR n'a pas confirmé votre paiement ! Le délai maximal alloué pour ce trade est écoulé. Vous pouvez attendre plus longtemps et accorder plus de temps à votre pair de trading ou contacter le médiateur pour obtenir de l'aide. # suppress inspection "TrailingSpacesInProperty" -portfolio.pending.step3_seller.part=Votre partenaire de trading a confirmé qu''il a initié le paiement {0}.\n +portfolio.pending.step3_seller.part=Votre partenaire de trading a confirmé qu'il a initié le paiement {0}.\n portfolio.pending.step3_seller.crypto.explorer=Sur votre explorateur blockchain {0} favori portfolio.pending.step3_seller.crypto.wallet=Dans votre portefeuille {0} -portfolio.pending.step3_seller.crypto={0}Veuillez s''il vous plaît vérifier {1} que la transaction vers votre adresse de réception\n{2}\ndispose de suffisamment de confirmations sur la blockchain.\nLe montant du paiement doit être {3}\n\nVous pouvez copier & coller votre adresse {4} à partir de l''écran principal après avoir fermé ce popup. +portfolio.pending.step3_seller.crypto={0}Veuillez s'il vous plaît vérifier {1} que la transaction vers votre adresse de réception\n{2}\ndispose de suffisamment de confirmations sur la blockchain.\nLe montant du paiement doit être {3}\n\nVous pouvez copier & coller votre adresse {4} à partir de l'écran principal après avoir fermé ce popup. portfolio.pending.step3_seller.postal={0}Veuillez vérifier si vous avez reçu {1} avec \"US Postal Money Order\" de la part de l'acheteur de XMR. # suppress inspection "TrailingSpacesInProperty" portfolio.pending.step3_seller.payByMail={0}Veuillez vérifier si vous avez reçu {1} avec \"Pay by Mail\" de la part de l'acheteur de XMR # suppress inspection "TrailingSpacesInProperty" portfolio.pending.step3_seller.bank=Votre partenaire de trading a confirmé qu'il a initié le {0} paiement.\n\nVeuillez vous rendre sur votre banque en ligne et vérifier si vous avez reçu {1} de la part de l'acheteur de XMR. -portfolio.pending.step3_seller.cash=Du fait que le paiement est réalisé via Cash Deposit l''acheteur de XMR doit inscrire \"NO REFUND\" sur le reçu papier, le déchirer en 2 et vous envoyer une photo par email.\n\nPour éviter un risque de rétrofacturation, ne confirmez que si vous recevez le mail et que vous êtes sûr que le reçu papier est valide.\nSi vous n''êtes pas sûr, {0} +portfolio.pending.step3_seller.cash=Du fait que le paiement est réalisé via Cash Deposit l'acheteur de XMR doit inscrire \"NO REFUND\" sur le reçu papier, le déchirer en 2 et vous envoyer une photo par email.\n\nPour éviter un risque de rétrofacturation, ne confirmez que si vous recevez le mail et que vous êtes sûr que le reçu papier est valide.\nSi vous n'êtes pas sûr, {0} portfolio.pending.step3_seller.moneyGram=L'acheteur doit vous envoyer le numéro d'autorisation et une photo du reçu par e-mail .\nLe reçu doit faire clairement figurer votre nom complet, votre pays, l'état et le montant. Veuillez s'il vous plaît vérifier que vous avez bien reçu par e-mail le numéro d'autorisation.\n\nAprès avoir fermé ce popup vous verrez le nom de l'acheteur de XMR et l'adresse où retirer l'argent depuis MoneyGram.\n\nN'accusez réception qu'après avoir retiré l'argent avec succès! portfolio.pending.step3_seller.westernUnion=L'acheteur doit vous envoyer le MTCN (numéro de suivi) et une photo du reçu par e-mail .\nLe reçu doit faire clairement figurer votre nom complet, votre pays, l'état et le montant. Veuillez s'il vous plaît vérifier si vous avez reçu par e-mail le MTCN.\n\nAprès avoir fermé ce popup vous verrez le nom de l'acheteur de XMR et l'adresse où retirer l'argent depuis Western Union.\n\nN'accusez réception qu'après avoir retiré l'argent avec succès! portfolio.pending.step3_seller.halCash=L'acheteur doit vous envoyer le code HalCash par message texte SMS. Par ailleurs, vous recevrez un message de la part d'HalCash avec les informations nécessaires pour retirer les EUR depuis un DAB Bancaire supportant HalCash.\n\nAprès avoir retiré l'argent au DAB, veuillez confirmer ici la réception du paiement ! portfolio.pending.step3_seller.amazonGiftCard=L'acheteur vous a envoyé une e-carte cadeau Amazon via email ou SMS vers votre téléphone. Veuillez récupérer maintenant la carte cadeau sur votre compte Amazon, et une fois activée, confirmez le reçu de paiement. -portfolio.pending.step3_seller.bankCheck=\n\nVeuillez également vérifier que le nom de l''expéditeur indiqué sur le contrat de l''échange correspond au nom qui apparaît sur votre relevé bancaire:\nNom de l''expéditeur, associé au contrat de l''échange: {0}\n\nSi les noms ne sont pas exactement identiques, {1} +portfolio.pending.step3_seller.bankCheck=\n\nVeuillez également vérifier que le nom de l'expéditeur indiqué sur le contrat de l'échange correspond au nom qui apparaît sur votre relevé bancaire:\nNom de l'expéditeur, associé au contrat de l'échange: {0}\n\nSi les noms ne sont pas exactement identiques, {1} # suppress inspection "TrailingSpacesInProperty" portfolio.pending.step3_seller.openDispute=ne confirmez pas la réception du paiement. Au lieu de cela, ouvrez un litige en appuyant sur \"alt + o\" ou \"option + o\".\n\n portfolio.pending.step3_seller.confirmPaymentReceipt=Confirmer la réception du paiement @@ -729,17 +724,17 @@ portfolio.pending.step3_seller.xmrTxHash=ID de la transaction portfolio.pending.step3_seller.xmrTxKey=Clé de Transaction portfolio.pending.step3_seller.buyersAccount=Données du compte de l'acheteur portfolio.pending.step3_seller.confirmReceipt=Confirmer la réception du paiement -portfolio.pending.step3_seller.buyerStartedPayment=L''acheteur XMR a commencé le {0} paiement.\n{1} +portfolio.pending.step3_seller.buyerStartedPayment=L'acheteur XMR a commencé le {0} paiement.\n{1} portfolio.pending.step3_seller.buyerStartedPayment.crypto=Vérifiez la présence de confirmations par la blockchain dans votre portefeuille crypto ou sur un explorateur de blocs et confirmez le paiement lorsque vous aurez suffisamment de confirmations sur la blockchain. portfolio.pending.step3_seller.buyerStartedPayment.traditional=Vérifiez sur votre compte de trading (par ex. compte bancaire) et confirmez quand vous avez reçu le paiement. portfolio.pending.step3_seller.warn.part1a=sur la {0} blockchain portfolio.pending.step3_seller.warn.part1b=Auprès de votre prestataire de paiement (par ex. banque) -portfolio.pending.step3_seller.warn.part2=Vous n''avez toujours pas confirmé la réception du paiement. Veuillez vérifier {0} si vous avez reçu le paiement. +portfolio.pending.step3_seller.warn.part2=Vous n'avez toujours pas confirmé la réception du paiement. Veuillez vérifier {0} si vous avez reçu le paiement. portfolio.pending.step3_seller.openForDispute=Vous n'avez pas confirmé la réception du paiement !\nLe délai maximal alloué pour ce trade est écoulé.\nVeuillez confirmer ou demander l'aide du médiateur. # suppress inspection "TrailingSpacesInProperty" portfolio.pending.step3_seller.onPaymentReceived.part1=Avez-vous reçu le paiement {0} de votre partenaire de trading?\n\n # suppress inspection "TrailingSpacesInProperty" -portfolio.pending.step3_seller.onPaymentReceived.name=Veuillez également vérifier que le nom de l''expéditeur indiqué sur le contrat de l''échange correspond au nom qui apparaît sur votre relevé bancaire:\nNom de l''expéditeur, avec le contrat de l''échange: {0}\n\nSi les noms ne sont pas exactement identiques, ne confirmez pas la réception du paiement. Au lieu de cela, ouvrez un litige en appuyant sur \"alt + o\" ou \"option + o\".\n\n +portfolio.pending.step3_seller.onPaymentReceived.name=Veuillez également vérifier que le nom de l'expéditeur indiqué sur le contrat de l'échange correspond au nom qui apparaît sur votre relevé bancaire:\nNom de l'expéditeur, avec le contrat de l'échange: {0}\n\nSi les noms ne sont pas exactement identiques, ne confirmez pas la réception du paiement. Au lieu de cela, ouvrez un litige en appuyant sur \"alt + o\" ou \"option + o\".\n\n # suppress inspection "TrailingSpacesInProperty" portfolio.pending.step3_seller.onPaymentReceived.note=Veuillez noter que dès que vous aurez confirmé la réception, le montant verrouillé pour l'échange sera remis à l'acheteur de XMR et le dépôt de garantie vous sera remboursé.\n portfolio.pending.step3_seller.onPaymentReceived.confirm.headline=Confirmez que vous avez bien reçu le paiement @@ -778,14 +773,14 @@ portfolio.pending.remainingTime=Temps restant portfolio.pending.remainingTimeDetail={0} (jusqu'’à {1}) portfolio.pending.tradePeriodInfo=Après la première confirmation de la blockchain, la période de trade commence. En fonction de la méthode de paiement utilisée, une période maximale allouée pour la transaction sera appliquée. portfolio.pending.tradePeriodWarning=Si le délai est dépassé, l'es deux participants du trade peuvent ouvrir un litige. -portfolio.pending.tradeNotCompleted=Trade inachevé dans le temps imparti (jusqu''à {0}) +portfolio.pending.tradeNotCompleted=Trade inachevé dans le temps imparti (jusqu'à {0}) portfolio.pending.tradeProcess=Processus de transaction portfolio.pending.openAgainDispute.msg=Si vous n'êtes pas certain que le message addressé au médiateur ou à l'arbitre soit arrivé (par exemple si vous n'avez pas reçu de réponse dans un délai de 1 jour), n'hésitez pas à réouvrir un litige avec Cmd/ctrl+O. Vous pouvez aussi demander de l'aide en complément sur le forum haveno à [LIEN:https://haveno.community]. portfolio.pending.openAgainDispute.button=Ouvrir à nouveau le litige portfolio.pending.openSupportTicket.headline=Ouvrir un ticket d'assistance portfolio.pending.openSupportTicket.msg=S'il vous plaît n'utilisez seulement cette fonction qu'en cas d'urgence si vous ne pouvez pas voir le bouton \"Open support\" ou \"Ouvrir un litige\.\n\nLorsque vous ouvrez un ticket de support, l'échange sera interrompu et pris en charge par le médiateur ou par l'arbitre. -portfolio.pending.timeLockNotOver=Vous devez patienter jusqu''au ≈{0} ({1} blocs de plus) avant de pouvoir ouvrir ouvrir un arbitrage pour le litige. +portfolio.pending.timeLockNotOver=Vous devez patienter jusqu'au ≈{0} ({1} blocs de plus) avant de pouvoir ouvrir ouvrir un arbitrage pour le litige. portfolio.pending.error.depositTxNull=La transaction de dépôt est nulle. Vous ne pouvez pas ouvrir un litige sans une transaction de dépôt valide. Allez dans \"Paramètres/Info sur le réseau\" et faites une resynchronisation SPV.\n\nPour obtenir de l'aide, le canal support de l'équipe Haveno est disponible sur Keybase. portfolio.pending.mediationResult.error.depositTxNull=La transaction de dépôt est nulle. Vous pouvez déplacer le trade vers les trades n'ayant pas réussi. portfolio.pending.mediationResult.error.delayedPayoutTxNull=Le paiement de la transaction différée est nul. Vous pouvez déplacer le trade vers les trades échoués. @@ -812,7 +807,7 @@ portfolio.pending.mediationResult.info.noneAccepted=Terminez la transaction en a portfolio.pending.mediationResult.info.selfAccepted=Vous avez accepté la suggestion du médiateur. En attente que le pair l'accepte également. portfolio.pending.mediationResult.info.peerAccepted=Votre pair de trading a accepté la suggestion du médiateur. L'acceptez-vous également ? portfolio.pending.mediationResult.button=Voir la résolution proposée -portfolio.pending.mediationResult.popup.headline=Résultat de la médiation pour la transaction avec l''ID: {0} +portfolio.pending.mediationResult.popup.headline=Résultat de la médiation pour la transaction avec l'ID: {0} portfolio.pending.mediationResult.popup.headline.peerAccepted=Votre pair de trading a accepté la suggestion du médiateur pour la transaction {0} portfolio.pending.mediationResult.popup.info=Les frais recommandés par le médiateur sont les suivants: \nVous paierez: {0} \nVotre partenaire commercial paiera: {1} \n\nVous pouvez accepter ou refuser ces frais de médiation. \n\nEn acceptant, vous avez vérifié l'opération de paiement du contrat. Si votre partenaire commercial accepte et vérifie également, le paiement sera effectué et la transaction sera clôturée. \n\nSi l'un de vous ou les deux refusent la proposition, vous devrez attendre le {2} (bloc {3}) pour commencer le deuxième tour de discussion sur le différend avec l'arbitre, et ce dernier étudiera à nouveau le cas. Le paiement sera fait en fonction de ses résultats. \n\nL'arbitre peut facturer une somme modique (la limite supérieure des honoraires: la marge de la transaction) en compensation de son travail. Les deux commerçants conviennent que la suggestion du médiateur est une voie agréable. La demande d'arbitrage concerne des circonstances particulières, par exemple si un professionnel est convaincu que le médiateur n'a pas fait une recommandation de d'indemnisation équitable (ou si l'autre partenaire n'a pas répondu). \n\nPlus de détails sur le nouveau modèle d'arbitrage: [HYPERLINK:https://docs.haveno.exchange/trading-rules.html#arbitration] portfolio.pending.mediationResult.popup.selfAccepted.lockTimeOver=Vous avez accepté la proposition de paiement du médiateur, mais il semble que votre contrepartie ne l'ait pas acceptée. \n\nUne fois que le temps de verrouillage atteint {0} (bloc {1}), vous pouvez ouvrir le second tour de litige pour que l'arbitre réétudie le cas et prend une nouvelle décision de dépenses. \n\nVous pouvez trouver plus d'informations sur le modèle d'arbitrage sur:[HYPERLINK:https://docs.haveno.exchange/trading-rules.html#arbitration] @@ -892,10 +887,10 @@ funds.withdrawal.warn.noSourceAddressSelected=Vous devez sélectionner une adres funds.withdrawal.warn.amountExceeds=Vous ne disposez pas de fonds suffisants provenant de l'adresse sélectionnée.\nEnvisagez de sélectionner plusieurs adresses dans le champ ci-dessus ou changez les frais pour inclure les frais du mineur. funds.reserved.noFunds=Aucun fonds n'est réservé pour les ordres en cours -funds.reserved.reserved=Réversé dans votre portefeuille local pour l''ordre avec l''ID: {0} +funds.reserved.reserved=Réversé dans votre portefeuille local pour l'ordre avec l'ID: {0} funds.locked.noFunds=Aucun fonds n'est verrouillé dans les trades -funds.locked.locked=Vérouillé en multisig pour le trade avec l''ID: {0} +funds.locked.locked=Vérouillé en multisig pour le trade avec l'ID: {0} funds.tx.direction.sentTo=Envoyer à: funds.tx.direction.receivedWith=Reçu depuis: @@ -908,7 +903,7 @@ funds.tx.disputePayout=Versement du litige: {0} funds.tx.disputeLost=Cas de litige perdu: {0} funds.tx.collateralForRefund=Remboursement du dépôt de garantie: {0} funds.tx.timeLockedPayoutTx=Tx de paiement verrouillée dans le temps: {0} -funds.tx.refund=Remboursement venant de l''arbitrage: {0} +funds.tx.refund=Remboursement venant de l'arbitrage: {0} funds.tx.unknown=Raison inconnue: {0} funds.tx.noFundsFromDispute=Aucun remboursement en cas de litige funds.tx.receivedFunds=Fonds reçus @@ -948,9 +943,9 @@ support.fullReportButton.label=Tous les litiges support.noTickets=Il n'y a pas de tickets ouverts support.sendingMessage=Envoi du message... support.receiverNotOnline=Le destinataire n'est pas en ligne. Le message est enregistré dans leur boîte mail. -support.sendMessageError=Échec de l''envoi du message. Erreur: {0} +support.sendMessageError=Échec de l'envoi du message. Erreur: {0} support.receiverNotKnown=Destinataire inconnu -support.wrongVersion=L''ordre relatif au litige en question a été créé avec une ancienne version de Haveno.\nVous ne pouvez pas clore ce litige avec votre version de l''application.\n\nVeuillez utiliser une version plus ancienne avec la version du protocole {0} +support.wrongVersion=L'ordre relatif au litige en question a été créé avec une ancienne version de Haveno.\nVous ne pouvez pas clore ce litige avec votre version de l'application.\n\nVeuillez utiliser une version plus ancienne avec la version du protocole {0} support.openFile=Ouvrir le fichier à joindre (taille max. du fichier : {0} kb) support.attachmentTooLarge=La taille totale de vos pièces jointes est de {0} ko ce qui dépasse la taille maximale autorisée de {1} ko pour les messages. support.maxSize=La taille maximale autorisée pour le fichier est {0} kB. @@ -966,7 +961,7 @@ support.attachments=Pièces jointes: support.savedInMailbox=Message sauvegardé dans la boîte mail du destinataire support.arrived=Message reçu par le destinataire support.acknowledged=Réception du message confirmée par le destinataire -support.error=Le destinataire n''a pas pu traiter le message. Erreur : {0} +support.error=Le destinataire n'a pas pu traiter le message. Erreur : {0} support.buyerAddress=Adresse de l'acheteur XMR support.sellerAddress=Adresse du vendeur XMR support.role=Rôle @@ -982,7 +977,7 @@ support.buyerTaker=Acheteur XMR/Taker support.sellerTaker=Vendeur XMR/Taker support.backgroundInfo=Haveno n'est pas une entreprise, donc il gère les litiges différemment.\n\nLes traders peuvent communiquer au sein de l'application via une discussion sécurisée sur l'écran des transactions ouvertes pour tenter de résoudre les litiges eux-mêmes. Si cela n'est pas suffisant, un médiateur évaluera la situation et décidera d'un paiement des fonds de transaction. -support.initialInfo=Veuillez entrer une description de votre problème dans le champ texte ci-dessous. Ajoutez autant d''informations que possible pour accélérer le temps de résolution du litige.\n\nVoici une check list des informations que vous devez fournir :\n● Si vous êtes l''acheteur XMR : Avez-vous effectué le paiement Fiat ou Crypto ? Si oui, avez-vous cliqué sur le bouton "paiement commencé" dans l''application ?\n● Si vous êtes le vendeur XMR : Avez-vous reçu le paiement Fiat ou Crypto ? Si oui, avez-vous cliqué sur le bouton "paiement reçu" dans l''application ?\n● Quelle version de Haveno utilisez-vous ?\n● Quel système d''exploitation utilisez-vous ?\n● Si vous avez rencontré un problème avec des transactions qui ont échoué, veuillez envisager de passer à un nouveau répertoire de données.\nParfois, le répertoire de données est corrompu et conduit à des bogues étranges. \nVoir : https://docs.haveno.exchange/backup-recovery.html#switch-to-a-new-data-directory\n\nVeuillez vous familiariser avec les règles de base du processus de règlement des litiges :\n● Vous devez répondre aux demandes des {0} dans les 2 jours.\n● Les médiateurs répondent dans un délai de 2 jours. Les arbitres répondent dans un délai de 5 jours ouvrables.\n● Le délai maximum pour un litige est de 14 jours.\n● Vous devez coopérer avec les {1} et fournir les renseignements qu''ils demandent pour faire valoir votre cause.\n● Vous avez accepté les règles décrites dans le document de litige dans l''accord d''utilisation lorsque vous avez lancé l''application pour la première fois.\n\nVous pouvez en apprendre davantage sur le processus de litige à l''adresse suivante {2} +support.initialInfo=Veuillez entrer une description de votre problème dans le champ texte ci-dessous. Ajoutez autant d'informations que possible pour accélérer le temps de résolution du litige.\n\nVoici une check list des informations que vous devez fournir :\n● Si vous êtes l'acheteur XMR : Avez-vous effectué le paiement Fiat ou Crypto ? Si oui, avez-vous cliqué sur le bouton "paiement commencé" dans l'application ?\n● Si vous êtes le vendeur XMR : Avez-vous reçu le paiement Fiat ou Crypto ? Si oui, avez-vous cliqué sur le bouton "paiement reçu" dans l'application ?\n● Quelle version de Haveno utilisez-vous ?\n● Quel système d'exploitation utilisez-vous ?\n● Si vous avez rencontré un problème avec des transactions qui ont échoué, veuillez envisager de passer à un nouveau répertoire de données.\nParfois, le répertoire de données est corrompu et conduit à des bogues étranges. \nVoir : https://docs.haveno.exchange/backup-recovery.html#switch-to-a-new-data-directory\n\nVeuillez vous familiariser avec les règles de base du processus de règlement des litiges :\n● Vous devez répondre aux demandes des {0} dans les 2 jours.\n● Les médiateurs répondent dans un délai de 2 jours. Les arbitres répondent dans un délai de 5 jours ouvrables.\n● Le délai maximum pour un litige est de 14 jours.\n● Vous devez coopérer avec les {1} et fournir les renseignements qu'ils demandent pour faire valoir votre cause.\n● Vous avez accepté les règles décrites dans le document de litige dans l'accord d'utilisation lorsque vous avez lancé l'application pour la première fois.\n\nVous pouvez en apprendre davantage sur le processus de litige à l'adresse suivante {2} support.systemMsg=Message du système: {0} support.youOpenedTicket=Vous avez ouvert une demande de support.\n\n{0}\n\nHaveno version: {1} support.youOpenedDispute=Vous avez ouvert une demande de litige.\n\n{0}\n\nHaveno version: {1} @@ -1117,22 +1112,22 @@ setting.about.subsystems.label=Versions des sous-systèmes setting.about.subsystems.val=Version du réseau: {0}; version des messages P2P: {1}; Version DB Locale: {2}; Version du protocole de trading: {3} setting.about.shortcuts=Raccourcis -setting.about.shortcuts.ctrlOrAltOrCmd=''Ctrl + {0}'' ou ''alt + {0}'' ou ''cmd + {0}'' +setting.about.shortcuts.ctrlOrAltOrCmd='Ctrl + {0}' ou 'alt + {0}' ou 'cmd + {0}' setting.about.shortcuts.menuNav=Naviguer dans le menu principal setting.about.shortcuts.menuNav.value=Pour naviguer dans le menu principal, appuyez sur: 'Ctrl' ou 'alt' ou 'cmd' avec une touche numérique entre '1-9'. setting.about.shortcuts.close=Fermer Haveno -setting.about.shortcuts.close.value=''Ctrl + {0}'' ou ''cmd + {0}'' ou ''Ctrl + {1}'' ou ''cmd + {1}'' +setting.about.shortcuts.close.value='Ctrl + {0}' ou 'cmd + {0}' ou 'Ctrl + {1}' ou 'cmd + {1}' setting.about.shortcuts.closePopup=Fermer le popup ou la fenêtre de dialogue setting.about.shortcuts.closePopup.value=Touche 'ECHAP' setting.about.shortcuts.chatSendMsg=Envoyer un message chat au trader -setting.about.shortcuts.chatSendMsg.value=''Ctrl + ENTRÉE'' ou ''alt + ENTREE'' ou ''cmd + ENTRÉE'' +setting.about.shortcuts.chatSendMsg.value='Ctrl + ENTRÉE' ou 'alt + ENTREE' ou 'cmd + ENTRÉE' setting.about.shortcuts.openDispute=Ouvrir un litige -setting.about.shortcuts.openDispute.value=Sélectionnez l''échange en cours et cliquez sur: {0} +setting.about.shortcuts.openDispute.value=Sélectionnez l'échange en cours et cliquez sur: {0} setting.about.shortcuts.walletDetails=Ouvrir la fenêtre avec les détails sur le portefeuille @@ -1152,7 +1147,7 @@ setting.about.shortcuts.registerMediator=Inscrire le médiateur (médiateur/arbi setting.about.shortcuts.registerMediator.value=Naviguez jusqu'au compte et appuyez sur: {0} setting.about.shortcuts.openSignPaymentAccountsWindow=Ouvrir la fenêtre pour la signature de l'âge du compte (anciens arbitres seulement) -setting.about.shortcuts.openSignPaymentAccountsWindow.value=Naviguer vers l''ancienne vue de l''arbitre et appuyer sur: {0} +setting.about.shortcuts.openSignPaymentAccountsWindow.value=Naviguer vers l'ancienne vue de l'arbitre et appuyer sur: {0} setting.about.shortcuts.sendAlertMsg=Envoyer un message d'alerte ou de mise à jour (activité privilégiée) @@ -1196,15 +1191,15 @@ account.arbitratorRegistration.pubKey=Clé publique account.arbitratorRegistration.register=S'inscrire account.arbitratorRegistration.registration={0} Enregistrement account.arbitratorRegistration.revoke=Révoquer -account.arbitratorRegistration.info.msg=Veuillez noter que vous devez rester disponible pendant 15 jours après la révocation, car il se peut que des échanges vous impliquent comme {0}. Le délai d''échange maximal autorisé est de 8 jours et la procédure de contestation peut prendre jusqu''à 7 jours. +account.arbitratorRegistration.info.msg=Veuillez noter que vous devez rester disponible pendant 15 jours après la révocation, car il se peut que des échanges vous impliquent comme {0}. Le délai d'échange maximal autorisé est de 8 jours et la procédure de contestation peut prendre jusqu'à 7 jours. account.arbitratorRegistration.warn.min1Language=Vous devez définir au moins 1 langue.\nNous avons ajouté la langue par défaut pour vous. account.arbitratorRegistration.removedSuccess=Vous avez supprimé votre inscription au réseau Haveno avec succès. -account.arbitratorRegistration.removedFailed=Impossible de supprimer l''enregistrement.{0} +account.arbitratorRegistration.removedFailed=Impossible de supprimer l'enregistrement.{0} account.arbitratorRegistration.registerSuccess=Vous vous êtes inscrit au réseau Haveno avec succès. -account.arbitratorRegistration.registerFailed=Impossible de terminer l''enregistrement.{0} +account.arbitratorRegistration.registerFailed=Impossible de terminer l'enregistrement.{0} account.crypto.yourCryptoAccounts=Vos comptes crypto -account.crypto.popup.wallet.msg=Veuillez vous assurer que vous respectez les exigences relatives à l''utilisation des {0} portefeuilles, selon les conditions présentées sur la page {1} du site.\nL''utilisation des portefeuilles provenant de plateformes de trading centralisées où (a) vous ne contrôlez pas vos clés ou (b) qui ne disposent pas d''un portefeuille compatible est risquée : cela peut entraîner la perte des fonds échangés!\nLe médiateur et l''arbitre ne sont pas des spécialistes {2} et ne pourront pas intervenir dans ce cas. +account.crypto.popup.wallet.msg=Veuillez vous assurer que vous respectez les exigences relatives à l'utilisation des {0} portefeuilles, selon les conditions présentées sur la page {1} du site.\nL'utilisation des portefeuilles provenant de plateformes de trading centralisées où (a) vous ne contrôlez pas vos clés ou (b) qui ne disposent pas d'un portefeuille compatible est risquée : cela peut entraîner la perte des fonds échangés!\nLe médiateur et l'arbitre ne sont pas des spécialistes {2} et ne pourront pas intervenir dans ce cas. account.crypto.popup.wallet.confirm=Je comprends et confirme que je sais quel portefeuille je dois utiliser. # suppress inspection "UnusedProperty" account.crypto.popup.upx.msg=Pour échanger UPX sur Haveno, vous devez comprendre et respecter les exigences suivantes: \n\nPour envoyer UPX, vous devez utiliser le portefeuille officiel UPXmA GUI ou le portefeuille UPXmA CLI avec le logo store-tx-info activé (valeur par défaut dans la nouvelle version) . Assurez-vous d'avoir accès à la clé tx, car elle est nécessaire dans l'état du litige. monero-wallet-cli (à l'aide de la commande get_Tx_key) monero-wallet-gui: sur la page Avancé> Preuve / Vérification. \n\nCes transactions ne sont pas vérifiables dans le navigateur blockchain ordinaire. \n\nEn cas de litige, vous devez fournir à l'arbitre les informations suivantes: \n\n- Clé privée Tx- hachage de transaction- adresse publique du destinataire \n\nSi vous ne fournissez pas les informations ci-dessus ou si vous utilisez un portefeuille incompatible, vous perdrez le litige. En cas de litige, l'expéditeur UPX est responsable de fournir la vérification du transfert UPX à l'arbitre. \n\nAucun paiement d'identité n'est requis, juste une adresse publique commune. \n\nSi vous n'êtes pas sûr du processus, veuillez visiter le canal UPXmA Discord (https://discord.gg/vhdNSrV) ou le groupe d'échanges Telegram (https://t.me/uplexaOfficial) pour plus d'informations. @@ -1250,8 +1245,8 @@ account.backup.backupNow=Sauvegarder maintenant (la sauvegarde n'est pas crypté account.backup.appDir=Répertoire des données de l'application account.backup.openDirectory=Ouvrir le répertoire account.backup.openLogFile=Ouvrir le fichier de log -account.backup.success=Sauvegarder réussite vers l''emplacement:\n{0} -account.backup.directoryNotAccessible=Le répertoire que vous avez choisi n''est pas accessible. {0} +account.backup.success=Sauvegarder réussite vers l'emplacement:\n{0} +account.backup.directoryNotAccessible=Le répertoire que vous avez choisi n'est pas accessible. {0} account.password.removePw.button=Supprimer le mot de passe account.password.removePw.headline=Supprimer la protection par mot de passe du portefeuille @@ -1295,13 +1290,13 @@ account.notifications.priceAlert.low.label=Me prévenir si le prix du XMR est in account.notifications.priceAlert.setButton=Définir l'alerte de prix account.notifications.priceAlert.removeButton=Retirer l'alerte de prix account.notifications.trade.message.title=L'état du trade a été modifié. -account.notifications.trade.message.msg.conf=La transaction de dépôt pour l''échange avec ID {0} est confirmée. Veuillez ouvrir votre application Haveno et initier le paiement. -account.notifications.trade.message.msg.started=L''acheteur de XMR a initié le paiement pour la transaction avec ID {0}. -account.notifications.trade.message.msg.completed=La transaction avec l''ID {0} est terminée. +account.notifications.trade.message.msg.conf=La transaction de dépôt pour l'échange avec ID {0} est confirmée. Veuillez ouvrir votre application Haveno et initier le paiement. +account.notifications.trade.message.msg.started=L'acheteur de XMR a initié le paiement pour la transaction avec ID {0}. +account.notifications.trade.message.msg.completed=La transaction avec l'ID {0} est terminée. account.notifications.offer.message.title=Votre ordre a été accepté -account.notifications.offer.message.msg=Votre ordre avec l''ID {0} a été accepté +account.notifications.offer.message.msg=Votre ordre avec l'ID {0} a été accepté account.notifications.dispute.message.title=Nouveau message de litige -account.notifications.dispute.message.msg=Vous avez reçu un message de contestation pour le trade avec l''ID {0} +account.notifications.dispute.message.msg=Vous avez reçu un message de contestation pour le trade avec l'ID {0} account.notifications.marketAlert.title=Alertes sur les ordres account.notifications.marketAlert.selectPaymentAccount=Ordres correspondants au compte de paiement @@ -1320,9 +1315,9 @@ account.notifications.marketAlert.manageAlerts.header.offerType=Type d'ordre account.notifications.marketAlert.message.title=Alerte d'ordre account.notifications.marketAlert.message.msg.below=en dessous de account.notifications.marketAlert.message.msg.above=au dessus de -account.notifications.marketAlert.message.msg=Un nouvel ordre ''{0} {1}''' avec le prix {2} ({3} {4} prix de marché) avec le moyen de paiement ''{5}'' a été publiée dans le livre des ordres de Haveno.\nID de l''ordre: {6}. +account.notifications.marketAlert.message.msg=Un nouvel ordre '{0} {1}'' avec le prix {2} ({3} {4} prix de marché) avec le moyen de paiement '{5}' a été publiée dans le livre des ordres de Haveno.\nID de l'ordre: {6}. account.notifications.priceAlert.message.title=Alerte de prix pour {0} -account.notifications.priceAlert.message.msg=Votre alerte de prix a été déclenchée. l''actuel {0} le prix est {1}. {2} +account.notifications.priceAlert.message.msg=Votre alerte de prix a été déclenchée. l'actuel {0} le prix est {1}. {2} account.notifications.noWebCamFound.warning=Aucune webcam n'a été trouvée.\n\nUtilisez l'option mail pour envoyer le jeton et la clé de cryptage depuis votre téléphone portable vers l'application Haveno. account.notifications.priceAlert.warning.highPriceTooLow=Le prix le plus élevé doit être supérieur au prix le plus bas. account.notifications.priceAlert.warning.lowerPriceTooHigh=Le prix le plus bas doit être inférieur au prix le plus élevé. @@ -1418,9 +1413,9 @@ disputeSummaryWindow.close.nextStepsForRefundAgentArbitration=\n\nÉtape suivant disputeSummaryWindow.close.closePeer=Vous devez également clore le ticket des pairs de trading ! disputeSummaryWindow.close.txDetails.headline=Publier la transaction de remboursement # suppress inspection "TrailingSpacesInProperty" -disputeSummaryWindow.close.txDetails.buyer=L''acheteur reçoit {0} à l''adresse: {1}\n +disputeSummaryWindow.close.txDetails.buyer=L'acheteur reçoit {0} à l'adresse: {1}\n # suppress inspection "TrailingSpacesInProperty" -disputeSummaryWindow.close.txDetails.seller=Le vendeur reçoit {0} à l''adresse: {1}\n +disputeSummaryWindow.close.txDetails.seller=Le vendeur reçoit {0} à l'adresse: {1}\n disputeSummaryWindow.close.txDetails=Dépenser: {0}\n{1}{2}Frais de transaction: {3}\n\nÊtes-vous sûr de vouloir publier cette transaction ? disputeSummaryWindow.close.noPayout.headline=Fermé sans paiement @@ -1474,7 +1469,7 @@ offerDetailsWindow.commitment=Engagement offerDetailsWindow.agree=J'accepte offerDetailsWindow.tac=Conditions d'utilisation offerDetailsWindow.confirm.maker=Confirmer: Placer un ordre de {0} monero -offerDetailsWindow.confirm.taker=Confirmer: Acceptez l''ordre de {0} monero +offerDetailsWindow.confirm.taker=Confirmer: Acceptez l'ordre de {0} monero offerDetailsWindow.creationDate=Date de création offerDetailsWindow.makersOnion=Adresse onion du maker offerDetailsWindow.challenge=Phrase secrète de l'offre @@ -1592,29 +1587,29 @@ popup.headline.error=Erreur popup.doNotShowAgain=Ne plus montrer popup.reportError.log=Ouvrir le dossier de log popup.reportError.gitHub=Signaler au Tracker de problème GitHub -popup.reportError={0}\n\nAfin de nous aider à améliorer le logiciel, veuillez signaler ce bug en ouvrant un nouveau ticket de support sur https://github.com/haveno-dex/haveno/issues.\nLe message d''erreur ci-dessus sera copié dans le presse-papier lorsque vous cliquerez sur l''un des boutons ci-dessous.\nCela facilitera le dépannage si vous incluez le fichier haveno.log en appuyant sur "ouvrir le fichier de log", en sauvegardant une copie, et en l''attachant à votre rapport de bug. +popup.reportError={0}\n\nAfin de nous aider à améliorer le logiciel, veuillez signaler ce bug en ouvrant un nouveau ticket de support sur https://github.com/haveno-dex/haveno/issues.\nLe message d'erreur ci-dessus sera copié dans le presse-papier lorsque vous cliquerez sur l'un des boutons ci-dessous.\nCela facilitera le dépannage si vous incluez le fichier haveno.log en appuyant sur "ouvrir le fichier de log", en sauvegardant une copie, et en l'attachant à votre rapport de bug. popup.error.tryRestart=Veuillez essayer de redémarrer votre application et vérifier votre connexion réseau pour voir si vous pouvez résoudre ce problème. -popup.error.takeOfferRequestFailed=Une erreur est survenue pendant que quelqu''un essayait d''accepter l''un de vos ordres:\n{0} +popup.error.takeOfferRequestFailed=Une erreur est survenue pendant que quelqu'un essayait d'accepter l'un de vos ordres:\n{0} -error.spvFileCorrupted=Une erreur est survenue pendant la lecture du fichier de la chaîne SPV.\nIl se peut que le fichier de la chaîne SPV soit corrompu.\n\nMessage d''erreur: {0}\n\nVoulez-vous l''effacer et lancer une resynchronisation? +error.spvFileCorrupted=Une erreur est survenue pendant la lecture du fichier de la chaîne SPV.\nIl se peut que le fichier de la chaîne SPV soit corrompu.\n\nMessage d'erreur: {0}\n\nVoulez-vous l'effacer et lancer une resynchronisation? error.deleteAddressEntryListFailed=Impossible de supprimer le dossier AddressEntryList.\nErreur: {0}. -error.closedTradeWithUnconfirmedDepositTx=La transaction de dépôt de l''échange fermé avec l''ID d''échange {0} n'est pas encore confirmée.\n\nVeuillez effectuer une resynchronisation SPV à \"Paramètres/Info sur le réseau\" pour voir si la transaction est valide. -error.closedTradeWithNoDepositTx=La transaction de dépôt de l'échange fermé avec l''ID d'échange {0} est nulle.\n\nVeuillez redémarrer l''application pour nettoyer la liste des transactions fermées. +error.closedTradeWithUnconfirmedDepositTx=La transaction de dépôt de l'échange fermé avec l'ID d'échange {0} n'est pas encore confirmée.\n\nVeuillez effectuer une resynchronisation SPV à \"Paramètres/Info sur le réseau\" pour voir si la transaction est valide. +error.closedTradeWithNoDepositTx=La transaction de dépôt de l'échange fermé avec l'ID d'échange {0} est nulle.\n\nVeuillez redémarrer l'application pour nettoyer la liste des transactions fermées. popup.warning.walletNotInitialized=Le portefeuille n'est pas encore initialisé popup.warning.osxKeyLoggerWarning=En raison de mesures de sécurité plus strictes dans MacOS 10.14 et dans la version supérieure, le lancement d'une application Java (Haveno utilise Java) provoquera un avertissement pop-up dans MacOS (« Haveno souhaite recevoir les frappes de toute application »). \n\nPour éviter ce problème, veuillez ouvrir «Paramètres MacOS», puis allez dans «Sécurité et confidentialité» -> «Confidentialité» -> «Surveillance des entrées», puis supprimez «Haveno» de la liste de droite. \n\nUne fois les limitations techniques résolues (le packager Java de la version Java requise n'a pas été livré), Haveno effectuera une mise à niveau vers la nouvelle version Java pour éviter ce problème. -popup.warning.wrongVersion=Vous avez probablement une mauvaise version de Haveno sur cet ordinateur.\nL''architecture de votre ordinateur est: {0}.\nLa binary Haveno que vous avez installé est: {1}.\nVeuillez éteindre et réinstaller une bonne version ({2}). +popup.warning.wrongVersion=Vous avez probablement une mauvaise version de Haveno sur cet ordinateur.\nL'architecture de votre ordinateur est: {0}.\nLa binary Haveno que vous avez installé est: {1}.\nVeuillez éteindre et réinstaller une bonne version ({2}). popup.warning.incompatibleDB=Nous avons détecté un fichier de base de données incompatible!\n\nCes fichiers de base de données ne sont pas compatibles avec notre base de code actuelle: {0}\n\nNous avons sauvegardé les fichiers endommagés et appliqué les valeurs par défaut à la nouvelle version de la base de données.\n\nLa sauvegarde se trouve dans: \n\n{1} / db / backup_of_corrupted_data. \n\nVeuillez vérifier si vous avez installé la dernière version de Haveno. \n\nVous pouvez télécharger: \n\n[HYPERLINK:https://haveno.exchange/downloads] \n\nVeuillez redémarrer l'application. popup.warning.startupFailed.twoInstances=Haveno est déjà lancé. Vous ne pouvez pas lancer deux instances de haveno. -popup.warning.tradePeriod.halfReached=Votre transaction avec ID {0} a atteint la moitié de la période de trading maximale autorisée et n''est toujours pas terminée.\n\nLa période de trade se termine le {1}.\n\nVeuillez vérifier l''état de votre transaction dans \"Portfolio/échanges en cours\" pour obtenir de plus amples informations. -popup.warning.tradePeriod.ended=Votre échange avec l''ID {0} a atteint la période de trading maximale autorisée et n''est pas terminé.\n\nLa période d''échange s''est terminée le {1}.\n\nVeuillez vérifier votre transaction sur \"Portfolio/Echanges en cours\" pour contacter le médiateur. +popup.warning.tradePeriod.halfReached=Votre transaction avec ID {0} a atteint la moitié de la période de trading maximale autorisée et n'est toujours pas terminée.\n\nLa période de trade se termine le {1}.\n\nVeuillez vérifier l'état de votre transaction dans \"Portfolio/échanges en cours\" pour obtenir de plus amples informations. +popup.warning.tradePeriod.ended=Votre échange avec l'ID {0} a atteint la période de trading maximale autorisée et n'est pas terminé.\n\nLa période d'échange s'est terminée le {1}.\n\nVeuillez vérifier votre transaction sur \"Portfolio/Echanges en cours\" pour contacter le médiateur. popup.warning.noTradingAccountSetup.headline=Vous n'avez pas configuré de compte de trading popup.warning.noTradingAccountSetup.msg=Vous devez configurer une devise nationale ou un compte crypto avant de pouvoir créer un ordre.\nVoulez-vous configurer un compte ? popup.warning.noArbitratorsAvailable=Les arbitres ne sont pas disponibles. popup.warning.noMediatorsAvailable=Il n'y a pas de médiateurs disponibles. popup.warning.notFullyConnected=Vous devez attendre d'être complètement connecté au réseau.\nCela peut prendre jusqu'à 2 minutes au démarrage. -popup.warning.notSufficientConnectionsToXmrNetwork=Vous devez attendre d''avoir au minimum {0} connexions au réseau Monero. +popup.warning.notSufficientConnectionsToXmrNetwork=Vous devez attendre d'avoir au minimum {0} connexions au réseau Monero. popup.warning.downloadNotComplete=Vous devez attendre que le téléchargement des blocs Monero manquants soit terminé. popup.warning.walletNotSynced=Le portefeuille Haveno n'est pas synchronisé avec la hauteur la plus récente de la blockchain. Veuillez patienter jusqu'à ce que le portefeuille soit synchronisé ou vérifiez votre connexion. popup.warning.removeOffer=Vous êtes certain de vouloir retirer cet ordre? @@ -1623,7 +1618,7 @@ popup.warning.examplePercentageValue=Merci de saisir un nombre sous la forme d'u popup.warning.noPriceFeedAvailable=Il n'y a pas de flux pour le prix de disponible pour cette devise. Vous ne pouvez pas utiliser un prix basé sur un pourcentage.\nVeuillez sélectionner le prix fixé. popup.warning.sendMsgFailed=L'envoi du message à votre partenaire d'échange a échoué.\nMerci d'essayer de nouveau et si l'échec persiste merci de reporter le bug. popup.warning.messageTooLong=Votre message dépasse la taille maximale autorisée. Veuillez l'envoyer en plusieurs parties ou le télécharger depuis un service comme https://pastebin.com. -popup.warning.lockedUpFunds=Vous avez des fonds bloqués d''une transaction qui a échoué.\nSolde bloqué: {0}\nAdresse de la tx de dépôt: {1}\nID de l''échange: {2}.\n\nVeuillez ouvrir un ticket de support en sélectionnant la transaction dans l'écran des transactions ouvertes et en appuyant sur \"alt + o\" ou \"option + o\". +popup.warning.lockedUpFunds=Vous avez des fonds bloqués d'une transaction qui a échoué.\nSolde bloqué: {0}\nAdresse de la tx de dépôt: {1}\nID de l'échange: {2}.\n\nVeuillez ouvrir un ticket de support en sélectionnant la transaction dans l'écran des transactions ouvertes et en appuyant sur \"alt + o\" ou \"option + o\". popup.warning.makerTxInvalid=Cette offre n'est pas valide. Veuillez choisir une autre offre.\n\n takeOffer.cancelButton=Annuler la prise de l'offre @@ -1636,19 +1631,19 @@ popup.warning.priceRelay=Relais de prix popup.warning.seed=seed popup.warning.mandatoryUpdate.trading=Veuillez faire une mise à jour vers la dernière version de Haveno. Une mise à jour obligatoire a été publiée, laquelle désactive le trading sur les anciennes versions. Veuillez consulter le Forum Haveno pour obtenir plus d'informations. popup.warning.noFilter=Nous n'avons pas reçu d'objet de filtre des nœuds de seed. Veuillez informer les administrateurs du réseau d'enregistrer un objet de filtre. -popup.warning.burnXMR=Cette transaction n''est pas possible, car les frais de minage de {0} dépasseraient le montant à transférer de {1}. Veuillez patienter jusqu''à ce que les frais de minage soient de nouveau bas ou jusqu''à ce que vous ayez accumulé plus de XMR à transférer. +popup.warning.burnXMR=Cette transaction n'est pas possible, car les frais de minage de {0} dépasseraient le montant à transférer de {1}. Veuillez patienter jusqu'à ce que les frais de minage soient de nouveau bas ou jusqu'à ce que vous ayez accumulé plus de XMR à transférer. -popup.warning.openOffer.makerFeeTxRejected=La transaction de frais de maker pour l''offre avec ID {0} a été rejetée par le réseau Monero.\nID de transaction={1}.\nL''offre a été retirée pour éviter d''autres problèmes.\nAllez dans \"Paramètres/Info sur le réseau réseau\" et faites une resynchronisation SPV.\nPour obtenir de l''aide, le canal support de l''équipe Haveno disposible sur Keybase. +popup.warning.openOffer.makerFeeTxRejected=La transaction de frais de maker pour l'offre avec ID {0} a été rejetée par le réseau Monero.\nID de transaction={1}.\nL'offre a été retirée pour éviter d'autres problèmes.\nAllez dans \"Paramètres/Info sur le réseau réseau\" et faites une resynchronisation SPV.\nPour obtenir de l'aide, le canal support de l'équipe Haveno disposible sur Keybase. popup.warning.trade.txRejected.tradeFee=frais de transaction popup.warning.trade.txRejected.deposit=dépôt -popup.warning.trade.txRejected=La transaction {0} pour le trade qui a pour ID {1} a été rejetée par le réseau Monero.\nID de transaction={2}.\nLe trade a été déplacé vers les échanges échoués.\nAllez dans \"Paramètres/Info sur le réseau\" et effectuez une resynchronisation SPV.\nPour obtenir de l''aide, le canal support de l'équipe Haveno est disponible sur Keybase. +popup.warning.trade.txRejected=La transaction {0} pour le trade qui a pour ID {1} a été rejetée par le réseau Monero.\nID de transaction={2}.\nLe trade a été déplacé vers les échanges échoués.\nAllez dans \"Paramètres/Info sur le réseau\" et effectuez une resynchronisation SPV.\nPour obtenir de l'aide, le canal support de l'équipe Haveno est disponible sur Keybase. -popup.warning.openOfferWithInvalidMakerFeeTx=La transaction de frais de maker pour l''offre avec ID {0} n''est pas valide.\nID de transaction={1}.\nAllez dans \"Paramètres/Info sur le réseau réseau\" et faites une resynchronisation SPV.\nPour obtenir de l''aide, le canal support de l''équipe Haveno est disponible sur Keybase. +popup.warning.openOfferWithInvalidMakerFeeTx=La transaction de frais de maker pour l'offre avec ID {0} n'est pas valide.\nID de transaction={1}.\nAllez dans \"Paramètres/Info sur le réseau réseau\" et faites une resynchronisation SPV.\nPour obtenir de l'aide, le canal support de l'équipe Haveno est disponible sur Keybase. popup.info.securityDepositInfo=Afin de s'assurer que les deux traders suivent le protocole de trading, les deux traders doivent payer un dépôt de garantie.\n\nCe dépôt est conservé dans votre portefeuille d'échange jusqu'à ce que votre transaction soit terminée avec succès, et ensuite il vous sera restitué.\n\nRemarque : si vous créez un nouvel ordre, Haveno doit être en cours d'exécution pour qu'un autre trader puisse l'accepter. Pour garder vos ordres en ligne, laissez Haveno en marche et assurez-vous que cet ordinateur reste en ligne aussi (pour cela, assurez-vous qu'il ne passe pas en mode veille....le mode veille du moniteur ne pose aucun problème). -popup.info.cashDepositInfo=Veuillez vous assurer d''avoir une succursale de l''établissement bancaire dans votre région afin de pouvoir effectuer le dépôt en espèces.\nL''identifiant bancaire (BIC/SWIFT) de la banque du vendeur est: {0}. +popup.info.cashDepositInfo=Veuillez vous assurer d'avoir une succursale de l'établissement bancaire dans votre région afin de pouvoir effectuer le dépôt en espèces.\nL'identifiant bancaire (BIC/SWIFT) de la banque du vendeur est: {0}. popup.info.cashDepositInfo.confirm=Je confirme que je peux effectuer le dépôt. popup.info.shutDownWithOpenOffers=Haveno est en cours de fermeture, mais des ordres sont en attente.\n\nCes ordres ne seront pas disponibles sur le réseau P2P si Haveno est éteint, mais ils seront republiés sur le réseau P2P la prochaine fois que vous lancerez Haveno.\n\nPour garder vos ordres en ligne, laissez Haveno en marche et assurez-vous que cet ordinateur reste aussi en ligne (pour cela, assurez-vous qu'il ne passe pas en mode veille...la veille du moniteur ne pose aucun problème). popup.info.qubesOSSetupInfo=Il semble que vous exécutez Haveno sous Qubes OS.\n\nVeuillez vous assurer que votre Haveno qube est mis en place de la manière expliquée dans notre guide [LIEN:https://haveno.exchange/wiki/Running_Haveno_on_Qubes]. @@ -1664,7 +1659,7 @@ popup.xmrLocalNode.msg=Haveno a détecté un nœud Monero en cours d'exécution popup.shutDownInProgress.headline=Fermeture en cours popup.shutDownInProgress.msg=La fermeture de l'application nécessite quelques secondes.\nVeuillez ne pas interrompre ce processus. -popup.attention.forTradeWithId=Attention requise la transaction avec l''ID {0} +popup.attention.forTradeWithId=Attention requise la transaction avec l'ID {0} popup.attention.reasonForPaymentRuleChange=La version 1.5.5 introduit un changement critique de règle de trade concernant le champ \"raison du paiement\" dans les transferts banquaires. Veuillez laisser ce champ vide -- N'UTILISEZ PAS l'ID de trade comme \"raison de paiement\". popup.info.multiplePaymentAccounts.headline=Comptes de paiement multiples disponibles @@ -1688,8 +1683,8 @@ popup.accountSigning.success.headline=Félicitations popup.accountSigning.success.description=Tous les {0} comptes de paiement ont été signés avec succès ! popup.accountSigning.generalInformation=Vous trouverez l'état de signature de tous vos comptes dans la section compte.\n\nPour plus d'informations, veuillez consulter [LIEN:https://docs.haveno.exchange/payment-methods#account-signing]. popup.accountSigning.signedByArbitrator=Un de vos comptes de paiement a été vérifié et signé par un arbitre. Echanger avec ce compte signera automatiquement le compte de votre pair de trading après un échange réussi.\n\n{0} -popup.accountSigning.signedByPeer=Un de vos comptes de paiement a été vérifié et signé par un pair de trading. Votre limite de trading initiale sera levée et vous pourrez signer d''autres comptes dans les {0} jours à venir.\n\n{1} -popup.accountSigning.peerLimitLifted=La limite initiale pour l''un de vos comptes a été levée.\n\n{0} +popup.accountSigning.signedByPeer=Un de vos comptes de paiement a été vérifié et signé par un pair de trading. Votre limite de trading initiale sera levée et vous pourrez signer d'autres comptes dans les {0} jours à venir.\n\n{1} +popup.accountSigning.peerLimitLifted=La limite initiale pour l'un de vos comptes a été levée.\n\n{0} popup.accountSigning.peerSigner=Un de vos comptes est suffisamment mature pour signer d'autres comptes de paiement et la limite initiale pour un de vos comptes a été levée.\n\n{0} popup.accountSigning.singleAccountSelect.headline=Importer le témoin non-signé de l'âge du compte @@ -1712,8 +1707,8 @@ popup.info.buyerAsTakerWithoutDeposit=Votre offre ne nécessitera pas de dépôt # Notifications #################################################################### -notification.trade.headline=Notification pour la transaction avec l''ID {0} -notification.ticket.headline=Ticket de support pour l''échange avec l''ID {0} +notification.trade.headline=Notification pour la transaction avec l'ID {0} +notification.ticket.headline=Ticket de support pour l'échange avec l'ID {0} notification.trade.completed=La transaction est maintenant terminée et vous pouvez retirer vos fonds. notification.trade.accepted=Votre ordre a été accepté par un XMR {0}. notification.trade.unlocked=Votre échange avait au moins une confirmation sur la blockchain.\nVous pouvez effectuer le paiement maintenant. @@ -1723,7 +1718,7 @@ notification.trade.peerOpenedDispute=Votre pair de trading a ouvert un {0}. notification.trade.disputeClosed=Le {0} a été fermé notification.walletUpdate.headline=Mise à jour du portefeuille de trading notification.walletUpdate.msg=Votre portefeuille de trading est suffisamment approvisionné.\nMontant: {0} -notification.takeOffer.walletUpdate.msg=Votre portefeuille de trading était déjà suffisamment approvisionné à la suite d''une précédente tentative d''achat de l'ordre.\nMontant: {0} +notification.takeOffer.walletUpdate.msg=Votre portefeuille de trading était déjà suffisamment approvisionné à la suite d'une précédente tentative d'achat de l'ordre.\nMontant: {0} notification.tradeCompleted.headline=Le trade est terminé notification.tradeCompleted.msg=Vous pouvez retirer vos fonds vers un portefeuille Monero externe ou les conserver dans votre portefeuille Haveno. @@ -1736,25 +1731,25 @@ systemTray.show=Montrer la fenêtre de l'application systemTray.hide=Cacher la fenêtre de l'application systemTray.info=Informations au sujet de Haveno systemTray.exit=Sortir -systemTray.tooltip=Haveno: Une plateforme d''échange décentralisée sur le réseau monero +systemTray.tooltip=Haveno: Une plateforme d'échange décentralisée sur le réseau monero #################################################################### # GUI Util #################################################################### -guiUtil.accountExport.savedToPath=Les comptes de trading sont sauvegardés vers l''arborescence:\n{0} +guiUtil.accountExport.savedToPath=Les comptes de trading sont sauvegardés vers l'arborescence:\n{0} guiUtil.accountExport.noAccountSetup=Vous n'avez pas de comptes de trading configurés pour exportation. -guiUtil.accountExport.selectPath=Sélectionner l''arborescence vers {0} +guiUtil.accountExport.selectPath=Sélectionner l'arborescence vers {0} # suppress inspection "TrailingSpacesInProperty" -guiUtil.accountExport.tradingAccount=Compte de trading avec l''ID {0}\n +guiUtil.accountExport.tradingAccount=Compte de trading avec l'ID {0}\n # suppress inspection "TrailingSpacesInProperty" -guiUtil.accountImport.noImport=Nous n''avons pas importé de compte de trading avec l''id {0} car il existe déjà.\n -guiUtil.accountExport.exportFailed=Echec de l''export à CSV à cause d'une erreur.\nErreur = {0} +guiUtil.accountImport.noImport=Nous n'avons pas importé de compte de trading avec l'id {0} car il existe déjà.\n +guiUtil.accountExport.exportFailed=Echec de l'export à CSV à cause d'une erreur.\nErreur = {0} guiUtil.accountExport.selectExportPath=Sélectionner l'arborescence d'export -guiUtil.accountImport.imported=Compte de trading importé depuis l''arborescence:\n{0}\n\nComptes importés:\n{1} -guiUtil.accountImport.noAccountsFound=Aucun compte de trading exporté n''a été trouvé sur l''arborescence {0}.\nLe nom du fichier est {1}." -guiUtil.openWebBrowser.warning=Vous allez ouvrir une page Web dans le navigateur Web de votre système.\nVoulez-vous ouvrir la page web maintenant ?\n\nSi vous n''utilisez pas le \"Navigateur Tor\" comme navigateur web par défaut, vous vous connecterez à la page web en clair.\n\nURL: \"{0}\" +guiUtil.accountImport.imported=Compte de trading importé depuis l'arborescence:\n{0}\n\nComptes importés:\n{1} +guiUtil.accountImport.noAccountsFound=Aucun compte de trading exporté n'a été trouvé sur l'arborescence {0}.\nLe nom du fichier est {1}." +guiUtil.openWebBrowser.warning=Vous allez ouvrir une page Web dans le navigateur Web de votre système.\nVoulez-vous ouvrir la page web maintenant ?\n\nSi vous n'utilisez pas le \"Navigateur Tor\" comme navigateur web par défaut, vous vous connecterez à la page web en clair.\n\nURL: \"{0}\" guiUtil.openWebBrowser.doOpen=Ouvrir la page web et ne plus me le demander guiUtil.openWebBrowser.copyUrl=Copier l'URL et annuler guiUtil.ofTradeAmount=du montant du trade @@ -1776,13 +1771,13 @@ table.placeholder.processingData=Traitement des données en cours... peerInfoIcon.tooltip.tradePeer=Du pair de trading peerInfoIcon.tooltip.maker=du maker peerInfoIcon.tooltip.trade.traded={0} adresse onion: {1}\nVous avez déjà échangé a {2} reprise(s) avec ce pair\n{3} -peerInfoIcon.tooltip.trade.notTraded={0} adresse onion: {1}\nvous n''avez pas échangé avec ce pair jusqu''à présent.\n{2} +peerInfoIcon.tooltip.trade.notTraded={0} adresse onion: {1}\nvous n'avez pas échangé avec ce pair jusqu'à présent.\n{2} peerInfoIcon.tooltip.age=Compte de paiement créé il y a {0}. peerInfoIcon.tooltip.unknownAge=Ancienneté du compte de paiement inconnue. tooltip.openPopupForDetails=Ouvrir le popup pour obtenir des détails tooltip.invalidTradeState.warning=Le trade est dans un état invalide. Ouvrez la fenêtre des détails pour plus d'informations -tooltip.openBlockchainForAddress=Ouvrir un explorateur de blockchain externe pour l''adresse: {0} +tooltip.openBlockchainForAddress=Ouvrir un explorateur de blockchain externe pour l'adresse: {0} tooltip.openBlockchainForTx=Ouvrir un explorateur de blockchain externe pour la transaction: {0} confidence.unknown=Statut de transaction inconnu @@ -1966,7 +1961,7 @@ payment.accountNr=Numéro de compte payment.emailOrMobile=Email ou N° de portable payment.useCustomAccountName=Utiliser un nom de compte personnalisé payment.maxPeriod=Durée d'échange max. autorisée -payment.maxPeriodAndLimit=Durée maximale de l''échange : {0} / Achat maximum : {1} / Vente maximum : {2} / Âge du compte : {3} +payment.maxPeriodAndLimit=Durée maximale de l'échange : {0} / Achat maximum : {1} / Vente maximum : {2} / Âge du compte : {3} payment.maxPeriodAndLimitCrypto=Durée maximale de trade: {0} / Limite maximale de trading {1} payment.currencyWithSymbol=Devise: {0} payment.nameOfAcceptedBank=Nom de la banque acceptée @@ -1993,7 +1988,7 @@ payment.halCash.info=Lors de l'utilisation de HalCash, l'acheteur de XMR doit en # suppress inspection "UnusedMessageFormatParameter" payment.limits.info=Sachez que tous les virements bancaires comportent un certain risque de rétrofacturation. Pour mitiger ce risque, Haveno fixe des limites par trade en fonction du niveau estimé de risque de rétrofacturation pour la méthode de paiement utilisée.\n\nPour cette méthode de paiement, votre limite de trading pour l'achat et la vente est de {2}.\n\nCette limite ne s'applique qu'à la taille d'une seule transaction. Vous pouvez effectuer autant de transactions que vous le souhaitez.\n\nVous trouverez plus de détails sur le wiki [HYPERLINK:https://docs.haveno.exchange/the-project/account_limits]. # suppress inspection "UnusedProperty" -payment.limits.info.withSigning=Afin de limiter le risque de rétrofacturation des achats, Haveno fixe des limites d'achat par transaction pour ce compte de paiement basé sur les 2 facteurs suivants :\n\n1. Risque de rétrofacturation pour le mode de paiement\n2. Statut de signature du compte\n\nCe compte de paiement n'est pas encore signé, il est donc limité à l'achat de {0} par trade. Après sa signature, les limites d'achat augmenteront comme suit :\n\n● Avant la signature, et jusqu'à 30 jours après la signature, votre limite d'achat par trade sera de {0}\n● 30 jours après la signature, votre limite d'achat par trade sera de {1}\n● 60 jours après la signature, votre limite d'achat par trade sera de {2}\n\nLes limites de vente ne sont pas affectées par la signature du compte. Vous pouvez vendre {2} en un seul trade immédiatement.\n\nCes limites s'appliquent uniquement à la taille d'un seul trade-vous pouvez placer autant de trades que vous voulez.\n\n Pour plus d''nformations, rendez vous à [LIEN:https://docs.haveno.exchange/the-project/account_limits]. +payment.limits.info.withSigning=Afin de limiter le risque de rétrofacturation des achats, Haveno fixe des limites d'achat par transaction pour ce compte de paiement basé sur les 2 facteurs suivants :\n\n1. Risque de rétrofacturation pour le mode de paiement\n2. Statut de signature du compte\n\nCe compte de paiement n'est pas encore signé, il est donc limité à l'achat de {0} par trade. Après sa signature, les limites d'achat augmenteront comme suit :\n\n● Avant la signature, et jusqu'à 30 jours après la signature, votre limite d'achat par trade sera de {0}\n● 30 jours après la signature, votre limite d'achat par trade sera de {1}\n● 60 jours après la signature, votre limite d'achat par trade sera de {2}\n\nLes limites de vente ne sont pas affectées par la signature du compte. Vous pouvez vendre {2} en un seul trade immédiatement.\n\nCes limites s'appliquent uniquement à la taille d'un seul trade-vous pouvez placer autant de trades que vous voulez.\n\n Pour plus d'nformations, rendez vous à [LIEN:https://docs.haveno.exchange/the-project/account_limits]. payment.cashDeposit.info=Veuillez confirmer que votre banque vous permet d'envoyer des dépôts en espèces sur le compte d'autres personnes. Par exemple, Bank of America et Wells Fargo n'autorisent plus de tels dépôts. @@ -2216,8 +2211,8 @@ validation.negative=Une valeur négative n'est pas autorisée. validation.traditional.tooSmall=La saisie d'une valeur plus petite que le montant minimal possible n'est pas autorisée. validation.traditional.tooLarge=La saisie d'une valeur supérieure au montant maximal possible n'est pas autorisée. validation.xmr.fraction=L'entrée résultera dans une valeur monero plus petite qu'1 satoshi -validation.xmr.tooLarge=La saisie d''une valeur supérieure à {0} n''est pas autorisée. -validation.xmr.tooSmall=La saisie d''une valeur inférieure à {0} n''est pas autorisée. +validation.xmr.tooLarge=La saisie d'une valeur supérieure à {0} n'est pas autorisée. +validation.xmr.tooSmall=La saisie d'une valeur inférieure à {0} n'est pas autorisée. validation.passwordTooShort=Le mot de passe que vous avez saisi est trop court. Il doit comporter un minimum de 8 caractères. validation.passwordTooLong=Le mot de passe que vous avez saisi est trop long. Il ne doit pas contenir plus de 50 caractères. validation.sortCodeNumber={0} doit être composer de {1} chiffres. @@ -2225,17 +2220,17 @@ validation.sortCodeChars={0} doit être composer de {1} caractères. validation.bankIdNumber={0} doit être composer de {1} chiffres. validation.accountNr=Le numéro du compte doit comporter {0} chiffres. validation.accountNrChars=Le numéro du compte doit comporter {0} caractères. -validation.xmr.invalidAddress=L''adresse n''est pas correcte. Veuillez vérifier le format de l''adresse. +validation.xmr.invalidAddress=L'adresse n'est pas correcte. Veuillez vérifier le format de l'adresse. validation.integerOnly=Veuillez seulement entrer des nombres entiers. validation.inputError=Votre saisie a causé une erreur:\n{0} -validation.xmr.exceedsMaxTradeLimit=Votre seuil maximum d''échange est {0}. +validation.xmr.exceedsMaxTradeLimit=Votre seuil maximum d'échange est {0}. validation.nationalAccountId={0} doit être composé de {1} nombres. #new validation.invalidInput=La valeur saisie est invalide: {0} validation.accountNrFormat=Le numéro du compte doit être au format: {0} # suppress inspection "UnusedProperty" -validation.crypto.wrongStructure=La validation de l''adresse a échoué car elle ne concorde pas avec la structure d''une adresse {0}. +validation.crypto.wrongStructure=La validation de l'adresse a échoué car elle ne concorde pas avec la structure d'une adresse {0}. # suppress inspection "UnusedProperty" validation.crypto.ltz.zAddressesNotSupported=L'adresse LTZ doit commencer par L. Les adresses commençant par z ne sont pas supportées. # suppress inspection "UnusedProperty" diff --git a/core/src/main/resources/i18n/displayStrings_it.properties b/core/src/main/resources/i18n/displayStrings_it.properties index 784b65234f..1e7119606a 100644 --- a/core/src/main/resources/i18n/displayStrings_it.properties +++ b/core/src/main/resources/i18n/displayStrings_it.properties @@ -4,11 +4,6 @@ # E.g.: [main-view].[component].[description] # In some cases we use enum values or constants to map to display strings -# A annoying issue with property files is that we need to use 2 single quotes in display string -# containing variables (e.g. {0}), otherwise the variable will not be resolved. -# In display string which do not use a variable a single quote is ok. -# E.g. Don''t .... {1} - # We use sometimes dynamic parts which are put together in the code and therefore sometimes use line breaks or spaces # at the end of the string. Please never remove any line breaks or spaces. They are there with a purpose! # To make longer strings with better readable you can make a line break with \ which does not result in a line break @@ -112,7 +107,7 @@ shared.belowInPercent=Sotto % del prezzo di mercato shared.aboveInPercent=Sopra % del prezzo di mercato shared.enterPercentageValue=Immetti il valore % shared.OR=OPPURE -shared.notEnoughFunds=You don''t have enough funds in your Haveno wallet for this transaction—{0} is needed but only {1} is available.\n\nPlease add funds from an external wallet, or fund your Haveno wallet at Funds > Receive Funds. +shared.notEnoughFunds=You don't have enough funds in your Haveno wallet for this transaction—{0} is needed but only {1} is available.\n\nPlease add funds from an external wallet, or fund your Haveno wallet at Funds > Receive Funds. shared.waitingForFunds=In attesa dei fondi... shared.TheXMRBuyer=L'acquirente di XMR shared.You=Tu @@ -643,7 +638,7 @@ portfolio.pending.step2_buyer.postal=Invia {0} tramite \"Vaglia Postale Statunit # suppress inspection "TrailingSpacesInProperty" portfolio.pending.step2_buyer.payByMail=Please send {0} using \"Pay by Mail\" to the XMR seller. Specific instructions are in the trade contract, or if unclear you may ask questions via trader chat. See more details about Pay by Mail on the Haveno wiki [HYPERLINK:https://docs.haveno.exchange/the-project/payment_methods/Pay_By_Mail].\n\n # suppress inspection "TrailingSpacesInProperty" -portfolio.pending.step2_buyer.pay=Please pay {0} via the specified payment method to the XMR seller. You''ll find the seller's account details on the next screen.\n\n +portfolio.pending.step2_buyer.pay=Please pay {0} via the specified payment method to the XMR seller. You'll find the seller's account details on the next screen.\n\n # suppress inspection "TrailingSpacesInProperty" portfolio.pending.step2_buyer.f2f=Contatta il venditore XMR tramite il contatto fornito e organizza un incontro per pagare {0}.\n\n portfolio.pending.step2_buyer.startPaymentUsing=Inizia il pagamento utilizzando {0} @@ -675,7 +670,7 @@ portfolio.pending.step2_seller.f2fInfo.headline=Informazioni di contatto dell'ac portfolio.pending.step2_seller.waitPayment.msg=La transazione di deposito necessita di almeno una conferma blockchain.\nDevi attendere fino a quando l'acquirente XMR invia il pagamento {0}. portfolio.pending.step2_seller.warn=L'acquirente XMR non ha ancora effettuato il pagamento {0}.\nDevi aspettare fino a quando non invia il pagamento.\nSe lo scambio non sarà completato il {1}, l'arbitro comincierà ad indagare. portfolio.pending.step2_seller.openForDispute=L'acquirente XMR non ha ancora inviato il pagamento!\nIl periodo massimo consentito per lo scambio è trascorso.\nPuoi aspettare più a lungo e dare più tempo al partner di scambio oppure puoi contattare il mediatore per ricevere assistenza. -tradeChat.chatWindowTitle=Finestra di chat per scambi con ID '' {0} '' +tradeChat.chatWindowTitle=Finestra di chat per scambi con ID ' {0} ' tradeChat.openChat=Apri la finestra di chat tradeChat.rules=Puoi comunicare con il tuo peer di trading per risolvere potenziali problemi con questo scambio.\nNon è obbligatorio rispondere nella chat.\nSe un trader viola una delle seguenti regole, apri una controversia ed effettua una segnalazione al mediatore o all'arbitro.\n\nRegole della chat:\n● Non inviare nessun link (rischio di malware). È possibile inviare l'ID transazione e il nome di un block explorer.\n● Non inviare parole del seed, chiavi private, password o altre informazioni sensibili!\n● Non incoraggiare il trading al di fuori di Haveno (non garantisce nessuna sicurezza).\n● Non intraprendere alcuna forma di tentativo di frode di ingegneria sociale.\n● Se un peer non risponde e preferisce non comunicare tramite chat, rispettane la decisione.\n● Limita l'ambito della conversazione allo scambio. Questa chat non è una sostituzione di messenger o un troll-box.\n● Mantieni la conversazione amichevole e rispettosa.\n  @@ -812,8 +807,8 @@ portfolio.pending.mediationResult.info.peerAccepted=Il tuo pari commerciale ha a portfolio.pending.mediationResult.button=Visualizza la risoluzione proposta portfolio.pending.mediationResult.popup.headline=Risultato della mediazione per gli scambi con ID: {0} portfolio.pending.mediationResult.popup.headline.peerAccepted=Il tuo pari commerciale ha accettato il suggerimento del mediatore per lo scambio {0} -portfolio.pending.mediationResult.popup.info=The mediator has suggested the following payout:\nYou receive: {0}\nYour trading peer receives: {1}\n\nYou can accept or reject this suggested payout.\n\nBy accepting, you sign the proposed payout transaction. If your trading peer also accepts and signs, the payout will be completed, and the trade will be closed.\n\nIf one or both of you reject the suggestion, you will have to wait until {2} (block {3}) to open a second-round dispute with an arbitrator who will investigate the case again and do a payout based on their findings.\n\nThe arbitrator may charge a small fee (fee maximum: the trader''s security deposit) as compensation for their work. Both traders agreeing to the mediator''s suggestion is the happy path—requesting arbitration is meant for exceptional circumstances, such as if a trader is sure the mediator did not make a fair payout suggestion (or if the other peer is unresponsive).\n\nMore details about the new arbitration model: [HYPERLINK:https://docs.haveno.exchange/trading-rules.html#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\nOnce 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\nYou can find more details about the arbitration model at:[HYPERLINK:https://docs.haveno.exchange/trading-rules.html#arbitration] +portfolio.pending.mediationResult.popup.info=The mediator has suggested the following payout:\nYou receive: {0}\nYour trading peer receives: {1}\n\nYou can accept or reject this suggested payout.\n\nBy accepting, you sign the proposed payout transaction. If your trading peer also accepts and signs, the payout will be completed, and the trade will be closed.\n\nIf one or both of you reject the suggestion, you will have to wait until {2} (block {3}) to open a second-round dispute with an arbitrator who will investigate the case again and do a payout based on their findings.\n\nThe arbitrator may charge a small fee (fee maximum: the trader's security deposit) as compensation for their work. Both traders agreeing to the mediator's suggestion is the happy path—requesting arbitration is meant for exceptional circumstances, such as if a trader is sure the mediator did not make a fair payout suggestion (or if the other peer is unresponsive).\n\nMore details about the new arbitration model: [HYPERLINK:https://docs.haveno.exchange/trading-rules.html#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\nOnce 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\nYou can find more details about the arbitration model at:[HYPERLINK:https://docs.haveno.exchange/trading-rules.html#arbitration] portfolio.pending.mediationResult.popup.openArbitration=Rifiuta e richiedi l'arbitrato portfolio.pending.mediationResult.popup.alreadyAccepted=Hai già accettato @@ -987,7 +982,7 @@ support.youOpenedDisputeForMediation=Hai richiesto la mediazione.\n\n{0}\n\nVers support.peerOpenedTicket=Il tuo peer di trading ha richiesto supporto a causa di problemi tecnici.\n\n{0}\n\nVersione Haveno: {1} support.peerOpenedDispute=Il tuo peer di trading ha richiesto una controversia.\n\n{0}\n\nVersione Haveno: {1} support.peerOpenedDisputeForMediation=Il tuo peer di trading ha richiesto la mediazione.\n\n{0}\n\nVersione Haveno: {1} -support.mediatorsDisputeSummary=System message: Mediator''s dispute summary:\n{0} +support.mediatorsDisputeSummary=System message: Mediator's dispute summary:\n{0} support.mediatorsAddress=Indirizzo nodo del mediatore: {0} support.warning.disputesWithInvalidDonationAddress=The delayed payout transaction has used an invalid receiver address. It does not match any of the DAO parameter values for the valid donation addresses.\n\nThis might be a scam attempt. Please inform the developers about that incident and do not close that case before the situation is resolved!\n\nAddress used in the dispute: {0}\n\nAll DAO param donation addresses: {1}\n\nTrade ID: {2}{3} support.warning.disputesWithInvalidDonationAddress.mediator=\n\nDo you still want to close the dispute? @@ -1114,19 +1109,19 @@ setting.about.subsystems.label=Versioni di sottosistemi setting.about.subsystems.val=Versione di rete: {0}; Versione del messaggio P2P: {1}; Versione DB locale: {2}; Versione del protocollo di scambio: {3} setting.about.shortcuts=Scorciatoie -setting.about.shortcuts.ctrlOrAltOrCmd=''Ctrl + {0}'' o ''alt + {0}'' o ''cmd + {0}'' +setting.about.shortcuts.ctrlOrAltOrCmd='Ctrl + {0}' o 'alt + {0}' o 'cmd + {0}' setting.about.shortcuts.menuNav=Naviga il menu principale setting.about.shortcuts.menuNav.value=Per navigare nel menu principale premere: 'Ctrl' o 'alt' o 'cmd' con un tasto numerico tra '1-9' setting.about.shortcuts.close=Chiudi Haveno -setting.about.shortcuts.close.value=''Ctrl + {0}'' o ''cmd + {0}'' o ''Ctrl + {1}'' o ''cmd + {1}'' +setting.about.shortcuts.close.value='Ctrl + {0}' o 'cmd + {0}' o 'Ctrl + {1}' o 'cmd + {1}' setting.about.shortcuts.closePopup=Chiudi popup o finestra di dialogo setting.about.shortcuts.closePopup.value=Tasto 'ESC' setting.about.shortcuts.chatSendMsg=Invia messaggio chat al trader -setting.about.shortcuts.chatSendMsg.value=''Ctrl + ENTER'' o ''alt + ENTER'' o ''cmd + ENTER'' +setting.about.shortcuts.chatSendMsg.value='Ctrl + ENTER' o 'alt + ENTER' o 'cmd + ENTER' setting.about.shortcuts.openDispute=Apri disputa setting.about.shortcuts.openDispute.value=Seleziona lo scambio in sospeso e fai clic: {0} @@ -1317,7 +1312,7 @@ account.notifications.marketAlert.manageAlerts.header.offerType=Tipo di offerta account.notifications.marketAlert.message.title=Avviso di offerta account.notifications.marketAlert.message.msg.below=sotto account.notifications.marketAlert.message.msg.above=sopra -account.notifications.marketAlert.message.msg=Una nuova ''{0} {1}'' offerta con prezzo {2} ({3} {4} prezzo di mercato) e metodo di pagamento ''{5}'' è stata pubblicata sulla pagina delle offerte Haveno.\nID offerta: {6}. +account.notifications.marketAlert.message.msg=Una nuova '{0} {1}' offerta con prezzo {2} ({3} {4} prezzo di mercato) e metodo di pagamento '{5}' è stata pubblicata sulla pagina delle offerte Haveno.\nID offerta: {6}. account.notifications.priceAlert.message.title=Avviso di prezzo per {0} account.notifications.priceAlert.message.msg=Il tuo avviso di prezzo è stato attivato. L'attuale prezzo {0} è {1} {2} account.notifications.noWebCamFound.warning=Nessuna webcam trovata.\n\nUtilizzare l'opzione e-mail per inviare il token e la chiave di crittografia dal telefono cellulare all'applicazione Haveno. @@ -1978,7 +1973,7 @@ payment.accountType=Tipologia conto payment.checking=Verifica payment.savings=Risparmi payment.personalId=ID personale -payment.zelle.info=Zelle is a money transfer service that works best *through* another bank.\n\n1. Check this page to see if (and how) your bank works with Zelle: [HYPERLINK:https://www.zellepay.com/get-started]\n\n2. Take special note of your transfer limits—sending limits vary by bank, and banks often specify separate daily, weekly, and monthly limits.\n\n3. If your bank does not work with Zelle, you can still use it through the Zelle mobile app, but your transfer limits will be much lower.\n\n4. The name specified on your Haveno account MUST match the name on your Zelle/bank account. \n\nIf you cannot complete a Zelle transaction as specified in your trade contract, you may lose some (or all) of your security deposit.\n\nBecause of Zelle''s somewhat higher chargeback risk, sellers are advised to contact unsigned buyers through email or SMS to verify that the buyer really owns the Zelle account specified in Haveno. +payment.zelle.info=Zelle is a money transfer service that works best *through* another bank.\n\n1. Check this page to see if (and how) your bank works with Zelle: [HYPERLINK:https://www.zellepay.com/get-started]\n\n2. Take special note of your transfer limits—sending limits vary by bank, and banks often specify separate daily, weekly, and monthly limits.\n\n3. If your bank does not work with Zelle, you can still use it through the Zelle mobile app, but your transfer limits will be much lower.\n\n4. The name specified on your Haveno account MUST match the name on your Zelle/bank account. \n\nIf you cannot complete a Zelle transaction as specified in your trade contract, you may lose some (or all) of your security deposit.\n\nBecause of Zelle's somewhat higher chargeback risk, sellers are advised to contact unsigned buyers through email or SMS to verify that the buyer really owns the Zelle account specified in Haveno. payment.fasterPayments.newRequirements.info=Alcune banche hanno iniziato a verificare il nome completo del destinatario per i trasferimenti di Faster Payments (UK). Il tuo attuale account Faster Payments non specifica un nome completo.\n\nTi consigliamo di ricreare il tuo account Faster Payments in Haveno per fornire ai futuri acquirenti {0} un nome completo.\n\nQuando si ricrea l'account, assicurarsi di copiare il codice di ordinamento preciso, il numero di account e i valori salt della verifica dell'età dal vecchio account al nuovo account. Ciò garantirà il mantenimento dell'età del tuo account esistente e lo stato della firma.\n  payment.moneyGram.info=When using MoneyGram the XMR buyer has to send the Authorisation number and a photo of the receipt by email to the XMR seller. The receipt must clearly show the seller's full name, country, state and the amount. The seller's email will be displayed to the buyer during the trade process. payment.westernUnion.info=When using Western Union the XMR buyer has to send the MTCN (tracking number) and a photo of the receipt by email to the XMR seller. The receipt must clearly show the seller's full name, city, country and the amount. The seller's email will be displayed to the buyer during the trade process. @@ -1991,7 +1986,7 @@ payment.limits.info.withSigning=To limit chargeback risk, Haveno sets per-trade payment.cashDeposit.info=Conferma che la tua banca ti consente di inviare depositi in contanti su conti di altre persone. Ad esempio, Bank of America e Wells Fargo non consentono più tali depositi. payment.revolut.info=Revolut requires the 'Username' as account ID not the phone number or email as it was the case in the past. -payment.account.revolut.addUserNameInfo={0}\nYour existing Revolut account ({1}) does not have a ''Username''.\nPlease enter your Revolut ''Username'' to update your account data.\nThis will not affect your account age signing status. +payment.account.revolut.addUserNameInfo={0}\nYour existing Revolut account ({1}) does not have a 'Username'.\nPlease enter your Revolut 'Username' to update your account data.\nThis will not affect your account age signing status. payment.revolut.addUserNameInfo.headLine=Update Revolut account payment.cashapp.info=Si prega di notare che Cash App ha un rischio di chargeback più elevato rispetto alla maggior parte dei bonifici bancari. @@ -2025,7 +2020,7 @@ payment.japan.recipient=Nome payment.australia.payid=PayID payment.payid=PayID linked to financial institution. Like email address or mobile phone. payment.payid.info=A PayID like a phone number, email address or an Australian Business Number (ABN), that you can securely link to your bank, credit union or building society account. You need to have already created a PayID with your Australian financial institution. Both sending and receiving financial institutions must support PayID. For more information please check [HYPERLINK:https://payid.com.au/faqs/] -payment.amazonGiftCard.info=To pay with Amazon eGift Card, you will need to send an Amazon eGift Card to the XMR seller via your Amazon account. \n\nHaveno will show the XMR seller''s email address or phone number where the gift card should be sent, and you must include the trade ID in the gift card''s message field. Please see the wiki [HYPERLINK:https://docs.haveno.exchange/the-project/payment_methods/Amazon_eGift_card] for further details and best practices. \n\nThree important notes:\n- try to send gift cards with amounts of 100 USD or smaller, as Amazon is known to flag larger gift cards as fraudulent\n- try to use creative, believable text for the gift card''s message (e.g., "Happy birthday Susan!") along with the trade ID (and use trader chat to tell your trading peer the reference text you picked so they can verify your payment)\n- Amazon eGift Cards can only be redeemed on the Amazon website they were purchased on (e.g., a gift card purchased on amazon.it can only be redeemed on amazon.it) +payment.amazonGiftCard.info=To pay with Amazon eGift Card, you will need to send an Amazon eGift Card to the XMR seller via your Amazon account. \n\nHaveno will show the XMR seller's email address or phone number where the gift card should be sent, and you must include the trade ID in the gift card's message field. Please see the wiki [HYPERLINK:https://docs.haveno.exchange/the-project/payment_methods/Amazon_eGift_card] for further details and best practices. \n\nThree important notes:\n- try to send gift cards with amounts of 100 USD or smaller, as Amazon is known to flag larger gift cards as fraudulent\n- try to use creative, believable text for the gift card's message (e.g., "Happy birthday Susan!") along with the trade ID (and use trader chat to tell your trading peer the reference text you picked so they can verify your payment)\n- Amazon eGift Cards can only be redeemed on the Amazon website they were purchased on (e.g., a gift card purchased on amazon.it can only be redeemed on amazon.it) payment.paysafe.info=Per la tua protezione, sconsigliamo vivamente di utilizzare i PIN di Paysafecard per i pagamenti.\n\n\ Le transazioni effettuate tramite PIN non possono essere verificate in modo indipendente per la risoluzione delle controversie. Se si verifica un problema, il recupero dei fondi potrebbe non essere possibile.\n\n\ Per garantire la sicurezza delle transazioni con risoluzione delle controversie, utilizza sempre metodi di pagamento che forniscono registrazioni verificabili. diff --git a/core/src/main/resources/i18n/displayStrings_ja.properties b/core/src/main/resources/i18n/displayStrings_ja.properties index ccc24ab657..045417f7c4 100644 --- a/core/src/main/resources/i18n/displayStrings_ja.properties +++ b/core/src/main/resources/i18n/displayStrings_ja.properties @@ -4,11 +4,6 @@ # E.g.: [main-view].[component].[description] # In some cases we use enum values or constants to map to display strings -# A annoying issue with property files is that we need to use 2 single quotes in display string -# containing variables (e.g. {0}), otherwise the variable will not be resolved. -# In display string which do not use a variable a single quote is ok. -# E.g. Don''t .... {1} - # We use sometimes dynamic parts which are put together in the code and therefore sometimes use line breaks or spaces # at the end of the string. Please never remove any line breaks or spaces. They are there with a purpose! # To make longer strings with better readable you can make a line break with \ which does not result in a line break @@ -676,7 +671,7 @@ portfolio.pending.step2_seller.f2fInfo.headline=買い手の連絡先 portfolio.pending.step2_seller.waitPayment.msg=デポジットトランザクションには、少なくとも1つのブロックチェーン承認があります。\nXMRの買い手が{0}の支払いを開始するまで待つ必要があります。 portfolio.pending.step2_seller.warn=XMRの買い手はまだ{0}の支払いを行っていません。\n支払いが開始されるまで待つ必要があります。\n取引が{1}で完了していない場合は、調停人が調査します。 portfolio.pending.step2_seller.openForDispute=XMRの買い手は支払いを開始していません!\nトレードの許可された最大期間が経過しました。\nもっと長く待ってトレードピアにもっと時間を与えるか、助けを求めるために調停者に連絡することができます。 -tradeChat.chatWindowTitle=トレードID '{0}'' のチャットウィンドウ +tradeChat.chatWindowTitle=トレードID '{0}' のチャットウィンドウ tradeChat.openChat=チャットウィンドウを開く tradeChat.rules=このトレードに対する潜在的な問題を解決するため、トレードピアと連絡できます。\nチャットに返事する義務はありません。\n取引者が以下のルールを破ると、係争を開始して調停者や調停人に報告して下さい。\n\nチャット・ルール:\n\t●リンクを送らないこと(マルウェアの危険性)。トランザクションIDとブロックチェーンエクスプローラの名前を送ることができます。\n\t●シードワード、プライベートキー、パスワードなどの機密な情報を送らないこと。\n\t●Haveno外のトレードを助長しないこと(セキュリティーがありません)。\n\t●ソーシャル・エンジニアリングや詐欺の行為に参加しないこと。\n\t●チャットで返事されない場合、それともチャットでの連絡が断られる場合、ピアの決断を尊重すること。\n\t●チャットの範囲をトレードに集中しておくこと。チャットはメッセンジャーの代わりや釣りをする場所ではありません。\n\t●礼儀正しく丁寧に話すこと。 diff --git a/core/src/main/resources/i18n/displayStrings_pt-br.properties b/core/src/main/resources/i18n/displayStrings_pt-br.properties index c28a530456..59b24342db 100644 --- a/core/src/main/resources/i18n/displayStrings_pt-br.properties +++ b/core/src/main/resources/i18n/displayStrings_pt-br.properties @@ -4,11 +4,6 @@ # E.g.: [main-view].[component].[description] # In some cases we use enum values or constants to map to display strings -# A annoying issue with property files is that we need to use 2 single quotes in display string -# containing variables (e.g. {0}), otherwise the variable will not be resolved. -# In display string which do not use a variable a single quote is ok. -# E.g. Don''t .... {1} - # We use sometimes dynamic parts which are put together in the code and therefore sometimes use line breaks or spaces # at the end of the string. Please never remove any line breaks or spaces. They are there with a purpose! # To make longer strings with better readable you can make a line break with \ which does not result in a line break @@ -112,7 +107,7 @@ shared.belowInPercent=% abaixo do preço de mercado shared.aboveInPercent=% acima do preço de mercado shared.enterPercentageValue=Insira a % shared.OR=OU -shared.notEnoughFunds=You don''t have enough funds in your Haveno wallet for this transaction—{0} is needed but only {1} is available.\n\nPlease add funds from an external wallet, or fund your Haveno wallet at Funds > Receive Funds. +shared.notEnoughFunds=You don't have enough funds in your Haveno wallet for this transaction—{0} is needed but only {1} is available.\n\nPlease add funds from an external wallet, or fund your Haveno wallet at Funds > Receive Funds. shared.waitingForFunds=Aguardando pagamento... shared.TheXMRBuyer=O comprador de XMR shared.You=Você @@ -646,7 +641,7 @@ portfolio.pending.step2_buyer.postal=Envie {0} através de \"US Postal Money Ord # suppress inspection "TrailingSpacesInProperty" portfolio.pending.step2_buyer.payByMail=Please send {0} using \"Pay by Mail\" to the XMR seller. Specific instructions are in the trade contract, or if unclear you may ask questions via trader chat. See more details about Pay by Mail on the Haveno wiki [HYPERLINK:https://docs.haveno.exchange/the-project/payment_methods/Pay_By_Mail].\n\n # suppress inspection "TrailingSpacesInProperty" -portfolio.pending.step2_buyer.pay=Please pay {0} via the specified payment method to the XMR seller. You''ll find the seller's account details on the next screen.\n\n +portfolio.pending.step2_buyer.pay=Please pay {0} via the specified payment method to the XMR seller. You'll find the seller's account details on the next screen.\n\n # suppress inspection "TrailingSpacesInProperty" portfolio.pending.step2_buyer.f2f=Por favor, entre em contato com o vendedor de XMR através do contato fornecido e combine um encontro para pagá-lo {0}.\n\n portfolio.pending.step2_buyer.startPaymentUsing=Iniciar pagamento usando {0} @@ -815,8 +810,8 @@ portfolio.pending.mediationResult.info.peerAccepted=O seu parceiro de negociaç portfolio.pending.mediationResult.button=Ver solução proposta portfolio.pending.mediationResult.popup.headline=Resultado da mediação para a negociação com ID: {0} portfolio.pending.mediationResult.popup.headline.peerAccepted=O seu parceiro de negociação aceitou a sugestão do mediador para a negociação {0} -portfolio.pending.mediationResult.popup.info=The mediator has suggested the following payout:\nYou receive: {0}\nYour trading peer receives: {1}\n\nYou can accept or reject this suggested payout.\n\nBy accepting, you sign the proposed payout transaction. If your trading peer also accepts and signs, the payout will be completed, and the trade will be closed.\n\nIf one or both of you reject the suggestion, you will have to wait until {2} (block {3}) to open a second-round dispute with an arbitrator who will investigate the case again and do a payout based on their findings.\n\nThe arbitrator may charge a small fee (fee maximum: the trader''s security deposit) as compensation for their work. Both traders agreeing to the mediator''s suggestion is the happy path—requesting arbitration is meant for exceptional circumstances, such as if a trader is sure the mediator did not make a fair payout suggestion (or if the other peer is unresponsive).\n\nMore details about the new arbitration model: [HYPERLINK:https://docs.haveno.exchange/trading-rules.html#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\nOnce 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\nYou can find more details about the arbitration model at:[HYPERLINK:https://docs.haveno.exchange/trading-rules.html#arbitration] +portfolio.pending.mediationResult.popup.info=The mediator has suggested the following payout:\nYou receive: {0}\nYour trading peer receives: {1}\n\nYou can accept or reject this suggested payout.\n\nBy accepting, you sign the proposed payout transaction. If your trading peer also accepts and signs, the payout will be completed, and the trade will be closed.\n\nIf one or both of you reject the suggestion, you will have to wait until {2} (block {3}) to open a second-round dispute with an arbitrator who will investigate the case again and do a payout based on their findings.\n\nThe arbitrator may charge a small fee (fee maximum: the trader's security deposit) as compensation for their work. Both traders agreeing to the mediator's suggestion is the happy path—requesting arbitration is meant for exceptional circumstances, such as if a trader is sure the mediator did not make a fair payout suggestion (or if the other peer is unresponsive).\n\nMore details about the new arbitration model: [HYPERLINK:https://docs.haveno.exchange/trading-rules.html#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\nOnce 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\nYou can find more details about the arbitration model at:[HYPERLINK:https://docs.haveno.exchange/trading-rules.html#arbitration] portfolio.pending.mediationResult.popup.openArbitration=Rejeitar e solicitar arbitramento portfolio.pending.mediationResult.popup.alreadyAccepted=Você já aceitou @@ -989,7 +984,7 @@ support.youOpenedDisputeForMediation=Você solicitou mediação.\n\n{0}\n\nVers support.peerOpenedTicket=O seu parceiro de negociação solicitou suporte devido a problemas técnicos.\n\n{0}\n\nVersão do Haveno: {1} support.peerOpenedDispute=O seu parceiro de negociação solicitou uma disputa.\n\n{0}\n\nVersão do Haveno: {1} support.peerOpenedDisputeForMediation=O seu parceiro de negociação solicitou mediação.\n\n{0}\n\nVersão do Haveno: {1} -support.mediatorsDisputeSummary=System message: Mediator''s dispute summary:\n{0} +support.mediatorsDisputeSummary=System message: Mediator's dispute summary:\n{0} support.mediatorsAddress=Endereço do nó do mediador: {0} support.warning.disputesWithInvalidDonationAddress=The delayed payout transaction has used an invalid receiver address. It does not match any of the DAO parameter values for the valid donation addresses.\n\nThis might be a scam attempt. Please inform the developers about that incident and do not close that case before the situation is resolved!\n\nAddress used in the dispute: {0}\n\nAll DAO param donation addresses: {1}\n\nTrade ID: {2}{3} support.warning.disputesWithInvalidDonationAddress.mediator=\n\nDo you still want to close the dispute? @@ -1116,19 +1111,19 @@ setting.about.subsystems.label=Versões dos subsistemas setting.about.subsystems.val=Versão da rede: {0}; Versão de mensagens P2P: {1}; Versão do banco de dados local: {2}; Versão do protocolo de negociação: {3} setting.about.shortcuts=atalhos -setting.about.shortcuts.ctrlOrAltOrCmd=''Ctrl + {0}'' ou ''alt + {0}'' ou ''cmd + {0}'' +setting.about.shortcuts.ctrlOrAltOrCmd='Ctrl + {0}' ou 'alt + {0}' ou 'cmd + {0}' setting.about.shortcuts.menuNav=Navegar para o menu principal setting.about.shortcuts.menuNav.value=Para ir ao menu principal, pressione: "ctr" ou "alt" ou "cmd" com um botão numérico de 1 a 9 setting.about.shortcuts.close=Fechar Haveno -setting.about.shortcuts.close.value=''Ctrl + {0}'' ou ''cmd + {0}'' ou ''Ctrl + {1}'' ou ''cmd + {1}'' +setting.about.shortcuts.close.value='Ctrl + {0}' ou 'cmd + {0}' ou 'Ctrl + {1}' ou 'cmd + {1}' setting.about.shortcuts.closePopup=Fechar popup ou janela de diálogo setting.about.shortcuts.closePopup.value=botão "Esc" setting.about.shortcuts.chatSendMsg=Enviar mensagem de chat ao negociador -setting.about.shortcuts.chatSendMsg.value=''Ctrl + ENTER'' ou ''alt + ENTER'' ou ''cmd + ENTER'' +setting.about.shortcuts.chatSendMsg.value='Ctrl + ENTER' ou 'alt + ENTER' ou 'cmd + ENTER' setting.about.shortcuts.openDispute=Abrir disputa setting.about.shortcuts.openDispute.value=Selecione negociação pendente e clique: {0} @@ -1319,7 +1314,7 @@ account.notifications.marketAlert.manageAlerts.header.offerType=Tipo de oferta account.notifications.marketAlert.message.title=Alerta de oferta account.notifications.marketAlert.message.msg.below=abaixo account.notifications.marketAlert.message.msg.above=acima -account.notifications.marketAlert.message.msg=Uma nova oferta ''{0} {1}'' com preço {2} ({3} {4} preço de mercado) e com o método de pagamento ''{5}'' foi publicada no livro de ofertas do Haveno.\nID da oferta: {6}. +account.notifications.marketAlert.message.msg=Uma nova oferta '{0} {1}' com preço {2} ({3} {4} preço de mercado) e com o método de pagamento '{5}' foi publicada no livro de ofertas do Haveno.\nID da oferta: {6}. account.notifications.priceAlert.message.title=Alerta de preço para {0} account.notifications.priceAlert.message.msg=O seu preço de alerta foi atingido. O preço atual da {0} é {1} {2} account.notifications.noWebCamFound.warning=Nenhuma webcam foi encontrada.\n\nPor favor, use a opção e-mail para enviar o token e a chave de criptografia do seu celular para o Haveno @@ -1985,8 +1980,8 @@ payment.accountType=Tipo de conta payment.checking=Conta Corrente payment.savings=Poupança payment.personalId=Identificação pessoal -payment.zelle.info=Zelle is a money transfer service that works best *through* another bank.\n\n1. Check this page to see if (and how) your bank works with Zelle: [HYPERLINK:https://www.zellepay.com/get-started]\n\n2. Take special note of your transfer limits—sending limits vary by bank, and banks often specify separate daily, weekly, and monthly limits.\n\n3. If your bank does not work with Zelle, you can still use it through the Zelle mobile app, but your transfer limits will be much lower.\n\n4. The name specified on your Haveno account MUST match the name on your Zelle/bank account. \n\nIf you cannot complete a Zelle transaction as specified in your trade contract, you may lose some (or all) of your security deposit.\n\nBecause of Zelle''s somewhat higher chargeback risk, sellers are advised to contact unsigned buyers through email or SMS to verify that the buyer really owns the Zelle account specified in Haveno. -payment.fasterPayments.newRequirements.info=Some banks have started verifying the receiver''s full name for Faster Payments transfers. Your current Faster Payments account does not specify a full name.\n\nPlease consider recreating your Faster Payments account in Haveno to provide future {0} buyers with a full name.\n\nWhen you recreate the account, make sure to copy the precise sort code, account number and account age verification salt values from your old account to your new account. This will ensure your existing account''s age and signing status are preserved. +payment.zelle.info=Zelle is a money transfer service that works best *through* another bank.\n\n1. Check this page to see if (and how) your bank works with Zelle: [HYPERLINK:https://www.zellepay.com/get-started]\n\n2. Take special note of your transfer limits—sending limits vary by bank, and banks often specify separate daily, weekly, and monthly limits.\n\n3. If your bank does not work with Zelle, you can still use it through the Zelle mobile app, but your transfer limits will be much lower.\n\n4. The name specified on your Haveno account MUST match the name on your Zelle/bank account. \n\nIf you cannot complete a Zelle transaction as specified in your trade contract, you may lose some (or all) of your security deposit.\n\nBecause of Zelle's somewhat higher chargeback risk, sellers are advised to contact unsigned buyers through email or SMS to verify that the buyer really owns the Zelle account specified in Haveno. +payment.fasterPayments.newRequirements.info=Some banks have started verifying the receiver's full name for Faster Payments transfers. Your current Faster Payments account does not specify a full name.\n\nPlease consider recreating your Faster Payments account in Haveno to provide future {0} buyers with a full name.\n\nWhen you recreate the account, make sure to copy the precise sort code, account number and account age verification salt values from your old account to your new account. This will ensure your existing account's age and signing status are preserved. payment.moneyGram.info=When using MoneyGram the XMR buyer has to send the Authorisation number and a photo of the receipt by email to the XMR seller. The receipt must clearly show the seller's full name, country, state and the amount. The seller's email will be displayed to the buyer during the trade process. payment.westernUnion.info=When using Western Union the XMR buyer has to send the MTCN (tracking number) and a photo of the receipt by email to the XMR seller. The receipt must clearly show the seller's full name, city, country and the amount. The seller's email will be displayed to the buyer during the trade process. payment.halCash.info=Ao usar o HalCash, o comprador de XMR precisa enviar ao vendedor de XMR o código HalCash através de uma mensagem de texto do seu telefone.\n\nPor favor, certifique-se de não exceder a quantia máxima que seu banco lhe permite enviar com o HalCash. O valor mínimo de saque é de 10 euros e valor máximo é de 600 EUR. Para saques repetidos é de 3000 euros por destinatário por dia e 6000 euros por destinatário por mês. Por favor confirme esses limites com seu banco para ter certeza de que eles usam os mesmos limites mencionados aqui.\n\nO valor de saque deve ser um múltiplo de 10 euros, pois você não pode sacar notas diferentes de uma ATM. Esse valor em XMR será ajustado na telas de criar e aceitar ofertas para que a quantia de EUR esteja correta. Você não pode usar o preço com base no mercado, pois o valor do EUR estaria mudando com a variação dos preços.\n\nEm caso de disputa, o comprador de XMR precisa fornecer a prova de que enviou o EUR. @@ -1998,7 +1993,7 @@ payment.limits.info.withSigning=To limit chargeback risk, Haveno sets per-trade payment.cashDeposit.info=Certifique-se de que o seu banco permite a realização de depósitos em espécie na conta de terceiros. payment.revolut.info=Revolut requires the 'Username' as account ID not the phone number or email as it was the case in the past. -payment.account.revolut.addUserNameInfo={0}\nYour existing Revolut account ({1}) does not have a ''Username''.\nPlease enter your Revolut ''Username'' to update your account data.\nThis will not affect your account age signing status. +payment.account.revolut.addUserNameInfo={0}\nYour existing Revolut account ({1}) does not have a 'Username'.\nPlease enter your Revolut 'Username' to update your account data.\nThis will not affect your account age signing status. payment.revolut.addUserNameInfo.headLine=Update Revolut account payment.cashapp.info=Por favor, esteja ciente de que o Cash App tem um risco maior de estorno do que a maioria das transferências bancárias. @@ -2032,7 +2027,7 @@ payment.japan.recipient=Nome payment.australia.payid=PayID payment.payid=PayID linked to financial institution. Like email address or mobile phone. payment.payid.info=A PayID like a phone number, email address or an Australian Business Number (ABN), that you can securely link to your bank, credit union or building society account. You need to have already created a PayID with your Australian financial institution. Both sending and receiving financial institutions must support PayID. For more information please check [HYPERLINK:https://payid.com.au/faqs/] -payment.amazonGiftCard.info=To pay with Amazon eGift Card, you will need to send an Amazon eGift Card to the XMR seller via your Amazon account. \n\nHaveno will show the XMR seller''s email address or phone number where the gift card should be sent, and you must include the trade ID in the gift card''s message field. Please see the wiki [HYPERLINK:https://docs.haveno.exchange/the-project/payment_methods/Amazon_eGift_card] for further details and best practices. \n\nThree important notes:\n- try to send gift cards with amounts of 100 USD or smaller, as Amazon is known to flag larger gift cards as fraudulent\n- try to use creative, believable text for the gift card''s message (e.g., "Happy birthday Susan!") along with the trade ID (and use trader chat to tell your trading peer the reference text you picked so they can verify your payment)\n- Amazon eGift Cards can only be redeemed on the Amazon website they were purchased on (e.g., a gift card purchased on amazon.it can only be redeemed on amazon.it) +payment.amazonGiftCard.info=To pay with Amazon eGift Card, you will need to send an Amazon eGift Card to the XMR seller via your Amazon account. \n\nHaveno will show the XMR seller's email address or phone number where the gift card should be sent, and you must include the trade ID in the gift card's message field. Please see the wiki [HYPERLINK:https://docs.haveno.exchange/the-project/payment_methods/Amazon_eGift_card] for further details and best practices. \n\nThree important notes:\n- try to send gift cards with amounts of 100 USD or smaller, as Amazon is known to flag larger gift cards as fraudulent\n- try to use creative, believable text for the gift card's message (e.g., "Happy birthday Susan!") along with the trade ID (and use trader chat to tell your trading peer the reference text you picked so they can verify your payment)\n- Amazon eGift Cards can only be redeemed on the Amazon website they were purchased on (e.g., a gift card purchased on amazon.it can only be redeemed on amazon.it) payment.paysafe.info=Para sua proteção, desaconselhamos fortemente o uso de PINs do Paysafecard para pagamento.\n\n\ Transações feitas por PINs não podem ser verificadas de forma independente para resolução de disputas. Se ocorrer um problema, a recuperação de fundos pode não ser possível.\n\n\ Para garantir a segurança das transações com resolução de disputas, sempre utilize métodos de pagamento que forneçam registros verificáveis. diff --git a/core/src/main/resources/i18n/displayStrings_pt.properties b/core/src/main/resources/i18n/displayStrings_pt.properties index d14b41668c..682b825894 100644 --- a/core/src/main/resources/i18n/displayStrings_pt.properties +++ b/core/src/main/resources/i18n/displayStrings_pt.properties @@ -4,11 +4,6 @@ # E.g.: [main-view].[component].[description] # In some cases we use enum values or constants to map to display strings -# A annoying issue with property files is that we need to use 2 single quotes in display string -# containing variables (e.g. {0}), otherwise the variable will not be resolved. -# In display string which do not use a variable a single quote is ok. -# E.g. Don''t .... {1} - # We use sometimes dynamic parts which are put together in the code and therefore sometimes use line breaks or spaces # at the end of the string. Please never remove any line breaks or spaces. They are there with a purpose! # To make longer strings with better readable you can make a line break with \ which does not result in a line break @@ -112,7 +107,7 @@ shared.belowInPercent=Abaixo % do preço de mercado shared.aboveInPercent=Acima % do preço de mercado shared.enterPercentageValue=Insira % do valor shared.OR=OU -shared.notEnoughFunds=You don''t have enough funds in your Haveno wallet for this transaction—{0} is needed but only {1} is available.\n\nPlease add funds from an external wallet, or fund your Haveno wallet at Funds > Receive Funds. +shared.notEnoughFunds=You don't have enough funds in your Haveno wallet for this transaction—{0} is needed but only {1} is available.\n\nPlease add funds from an external wallet, or fund your Haveno wallet at Funds > Receive Funds. shared.waitingForFunds=Esperando pelos fundos... shared.TheXMRBuyer=O comprador de XMR shared.You=Você @@ -643,7 +638,7 @@ portfolio.pending.step2_buyer.postal=Por favor envie {0} por \"US Postal Money O # suppress inspection "TrailingSpacesInProperty" portfolio.pending.step2_buyer.payByMail=Please send {0} using \"Pay by Mail\" to the XMR seller. Specific instructions are in the trade contract, or if unclear you may ask questions via trader chat. See more details about Pay by Mail on the Haveno wiki [HYPERLINK:https://docs.haveno.exchange/the-project/payment_methods/Pay_By_Mail].\n\n # suppress inspection "TrailingSpacesInProperty" -portfolio.pending.step2_buyer.pay=Please pay {0} via the specified payment method to the XMR seller. You''ll find the seller's account details on the next screen.\n\n +portfolio.pending.step2_buyer.pay=Please pay {0} via the specified payment method to the XMR seller. You'll find the seller's account details on the next screen.\n\n # suppress inspection "TrailingSpacesInProperty" portfolio.pending.step2_buyer.f2f=Por favor contacte o vendedor de XMR pelo contacto fornecido e marque um encontro para pagar {0}.\n\n portfolio.pending.step2_buyer.startPaymentUsing=Iniciar pagamento usando {0} @@ -675,7 +670,7 @@ portfolio.pending.step2_seller.f2fInfo.headline=Informação do contacto do comp portfolio.pending.step2_seller.waitPayment.msg=A transação de depósito tem pelo menos uma confirmação da blockchain.\nVocê precisa esperar até que o comprador de XMR inicie o pagamento {0}. portfolio.pending.step2_seller.warn=O comprador do XMR ainda não efetuou o pagamento de {0}.\nVocê precisa esperar até que eles tenham iniciado o pagamento.\nSe o negócio não for concluído em {1}, o árbitro irá investigar. portfolio.pending.step2_seller.openForDispute=O comprador de XMR não iniciou o seu pagamento!\nO período máx. permitido para o negócio acabou.\nVocê pode esperar e dar mais tempo ao seu par de negociação ou entrar em contacto com o mediador para assistência. -tradeChat.chatWindowTitle=Janela de chat para o negócio com o ID ''{0}'' +tradeChat.chatWindowTitle=Janela de chat para o negócio com o ID '{0}' tradeChat.openChat=Abrir janela de chat tradeChat.rules=Você pode comunicar com o seu par de negociação para resolver problemas com este negócio.\nNão é obrigatório responder no chat.\nSe algum negociante infringir alguma das regras abaixo, abra uma disputa e reporte-o ao mediador ou ao árbitro.\n\nRegras do chat:\n\t● Não envie nenhum link (risco de malware). Você pode enviar o ID da transação e o nome de um explorador de blocos.\n\t● Não envie as suas palavras-semente, chaves privadas, senhas ou outra informação sensitiva!\n\t● Não encoraje negócios fora do Haveno (sem segurança).\n\t● Não engaje em nenhuma forma de scams de engenharia social.\n\t● Se um par não responde e prefere não comunicar pelo chat, respeite a sua decisão.\n\t● Mantenha o âmbito da conversa limitado ao negócio. Este chat não é um substituto para o messenger ou uma caixa para trolls.\n\t● Mantenha a conversa amigável e respeitosa. @@ -812,8 +807,8 @@ portfolio.pending.mediationResult.info.peerAccepted=O seu par de negócio aceito portfolio.pending.mediationResult.button=Ver a resolução proposta portfolio.pending.mediationResult.popup.headline=Resultado da mediação para o negócio com o ID: {0} portfolio.pending.mediationResult.popup.headline.peerAccepted=O seu par de negócio aceitou a sugestão do mediador para o negócio {0} -portfolio.pending.mediationResult.popup.info=The mediator has suggested the following payout:\nYou receive: {0}\nYour trading peer receives: {1}\n\nYou can accept or reject this suggested payout.\n\nBy accepting, you sign the proposed payout transaction. If your trading peer also accepts and signs, the payout will be completed, and the trade will be closed.\n\nIf one or both of you reject the suggestion, you will have to wait until {2} (block {3}) to open a second-round dispute with an arbitrator who will investigate the case again and do a payout based on their findings.\n\nThe arbitrator may charge a small fee (fee maximum: the trader''s security deposit) as compensation for their work. Both traders agreeing to the mediator''s suggestion is the happy path—requesting arbitration is meant for exceptional circumstances, such as if a trader is sure the mediator did not make a fair payout suggestion (or if the other peer is unresponsive).\n\nMore details about the new arbitration model: [HYPERLINK:https://docs.haveno.exchange/trading-rules.html#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\nOnce 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\nYou can find more details about the arbitration model at:[HYPERLINK:https://docs.haveno.exchange/trading-rules.html#arbitration] +portfolio.pending.mediationResult.popup.info=The mediator has suggested the following payout:\nYou receive: {0}\nYour trading peer receives: {1}\n\nYou can accept or reject this suggested payout.\n\nBy accepting, you sign the proposed payout transaction. If your trading peer also accepts and signs, the payout will be completed, and the trade will be closed.\n\nIf one or both of you reject the suggestion, you will have to wait until {2} (block {3}) to open a second-round dispute with an arbitrator who will investigate the case again and do a payout based on their findings.\n\nThe arbitrator may charge a small fee (fee maximum: the trader's security deposit) as compensation for their work. Both traders agreeing to the mediator's suggestion is the happy path—requesting arbitration is meant for exceptional circumstances, such as if a trader is sure the mediator did not make a fair payout suggestion (or if the other peer is unresponsive).\n\nMore details about the new arbitration model: [HYPERLINK:https://docs.haveno.exchange/trading-rules.html#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\nOnce 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\nYou can find more details about the arbitration model at:[HYPERLINK:https://docs.haveno.exchange/trading-rules.html#arbitration] portfolio.pending.mediationResult.popup.openArbitration=Rejeitar e solicitar arbitragem portfolio.pending.mediationResult.popup.alreadyAccepted=Você já aceitou @@ -986,7 +981,7 @@ support.youOpenedDisputeForMediation=Você solicitou mediação.\n\n{0}\n\nVers support.peerOpenedTicket=O seu par de negociação solicitou suporte devido a problemas técnicos.\n\n{0}\n\nVersão Haveno: {1} support.peerOpenedDispute=O seu par de negociação solicitou uma disputa.\n\n{0}\n\nVersão Haveno: {1} support.peerOpenedDisputeForMediation=O seu par de negociação solicitou uma mediação.\n\n{0}\n\nVersão Haveno: {1} -support.mediatorsDisputeSummary=System message: Mediator''s dispute summary:\n{0} +support.mediatorsDisputeSummary=System message: Mediator's dispute summary:\n{0} support.mediatorsAddress=Endereço do nó do mediador: {0} support.warning.disputesWithInvalidDonationAddress=The delayed payout transaction has used an invalid receiver address. It does not match any of the DAO parameter values for the valid donation addresses.\n\nThis might be a scam attempt. Please inform the developers about that incident and do not close that case before the situation is resolved!\n\nAddress used in the dispute: {0}\n\nAll DAO param donation addresses: {1}\n\nTrade ID: {2}{3} support.warning.disputesWithInvalidDonationAddress.mediator=\n\nDo you still want to close the dispute? @@ -1113,19 +1108,19 @@ setting.about.subsystems.label=Versão de subsistemas setting.about.subsystems.val=Versão da rede: {0}; Versão de mensagem P2P: {1}; Versão da base de dados local: {2}; Versão do protocolo de negócio: {3} setting.about.shortcuts=Atalhos -setting.about.shortcuts.ctrlOrAltOrCmd=''Ctrl + {0}'' ou ''alt + {0}'' ou ''cmd + {0}'' +setting.about.shortcuts.ctrlOrAltOrCmd='Ctrl + {0}' ou 'alt + {0}' ou 'cmd + {0}' setting.about.shortcuts.menuNav=Navigar o menu principal setting.about.shortcuts.menuNav.value=Para navigar o menu principal pressione:: 'Ctrl' ou 'alt' ou 'cmd' juntamente com uma tecla numérica entre '1-9' setting.about.shortcuts.close=Fechar o Haveno -setting.about.shortcuts.close.value=''Ctrl + {0}'' ou ''cmd + {0}'' ou ''Ctrl + {1}'' ou ''cmd + {1}'' +setting.about.shortcuts.close.value='Ctrl + {0}' ou 'cmd + {0}' ou 'Ctrl + {1}' ou 'cmd + {1}' setting.about.shortcuts.closePopup=Fechar popup ou janela de diálogo setting.about.shortcuts.closePopup.value=Tecla "ESCAPE" setting.about.shortcuts.chatSendMsg=Enviar uma mensagem ao negociador -setting.about.shortcuts.chatSendMsg.value=''Ctrl + ENTER'' ou ''alt + ENTER'' ou ''cmd + ENTER'' +setting.about.shortcuts.chatSendMsg.value='Ctrl + ENTER' ou 'alt + ENTER' ou 'cmd + ENTER' setting.about.shortcuts.openDispute=Abrir disputa setting.about.shortcuts.openDispute.value=Selecionar negócio pendente e clicar: {0} @@ -1316,7 +1311,7 @@ account.notifications.marketAlert.manageAlerts.header.offerType=Tipo de oferta account.notifications.marketAlert.message.title=Alerta de oferta account.notifications.marketAlert.message.msg.below=abaixo de account.notifications.marketAlert.message.msg.above=acima de -account.notifications.marketAlert.message.msg=Uma nova ''{0} {1}'' com o preço de {2} ({3} {4} preço de mercado) e método de pagamento ''{5}'' foi publicada no livro de ofertas do Haveno.\nID da oferta: {6}. +account.notifications.marketAlert.message.msg=Uma nova '{0} {1}' com o preço de {2} ({3} {4} preço de mercado) e método de pagamento '{5}' foi publicada no livro de ofertas do Haveno.\nID da oferta: {6}. account.notifications.priceAlert.message.title=Alerta de preço para {0} account.notifications.priceAlert.message.msg=O teu alerta de preço foi desencadeado. O preço atual de {0} é de {1} {2} account.notifications.noWebCamFound.warning=Nenhuma webcam foi encontrada.\n\nPor favor use a opção email para enviar o token e a chave de criptografia do seu telemóvel para o programa da Haveno. @@ -1975,8 +1970,8 @@ payment.accountType=Tipo de conta payment.checking=Conta Corrente payment.savings=Poupança payment.personalId=ID pessoal -payment.zelle.info=Zelle is a money transfer service that works best *through* another bank.\n\n1. Check this page to see if (and how) your bank works with Zelle: [HYPERLINK:https://www.zellepay.com/get-started]\n\n2. Take special note of your transfer limits—sending limits vary by bank, and banks often specify separate daily, weekly, and monthly limits.\n\n3. If your bank does not work with Zelle, you can still use it through the Zelle mobile app, but your transfer limits will be much lower.\n\n4. The name specified on your Haveno account MUST match the name on your Zelle/bank account. \n\nIf you cannot complete a Zelle transaction as specified in your trade contract, you may lose some (or all) of your security deposit.\n\nBecause of Zelle''s somewhat higher chargeback risk, sellers are advised to contact unsigned buyers through email or SMS to verify that the buyer really owns the Zelle account specified in Haveno. -payment.fasterPayments.newRequirements.info=Some banks have started verifying the receiver''s full name for Faster Payments transfers. Your current Faster Payments account does not specify a full name.\n\nPlease consider recreating your Faster Payments account in Haveno to provide future {0} buyers with a full name.\n\nWhen you recreate the account, make sure to copy the precise sort code, account number and account age verification salt values from your old account to your new account. This will ensure your existing account''s age and signing status are preserved. +payment.zelle.info=Zelle is a money transfer service that works best *through* another bank.\n\n1. Check this page to see if (and how) your bank works with Zelle: [HYPERLINK:https://www.zellepay.com/get-started]\n\n2. Take special note of your transfer limits—sending limits vary by bank, and banks often specify separate daily, weekly, and monthly limits.\n\n3. If your bank does not work with Zelle, you can still use it through the Zelle mobile app, but your transfer limits will be much lower.\n\n4. The name specified on your Haveno account MUST match the name on your Zelle/bank account. \n\nIf you cannot complete a Zelle transaction as specified in your trade contract, you may lose some (or all) of your security deposit.\n\nBecause of Zelle's somewhat higher chargeback risk, sellers are advised to contact unsigned buyers through email or SMS to verify that the buyer really owns the Zelle account specified in Haveno. +payment.fasterPayments.newRequirements.info=Some banks have started verifying the receiver's full name for Faster Payments transfers. Your current Faster Payments account does not specify a full name.\n\nPlease consider recreating your Faster Payments account in Haveno to provide future {0} buyers with a full name.\n\nWhen you recreate the account, make sure to copy the precise sort code, account number and account age verification salt values from your old account to your new account. This will ensure your existing account's age and signing status are preserved. payment.moneyGram.info=When using MoneyGram the XMR buyer has to send the Authorisation number and a photo of the receipt by email to the XMR seller. The receipt must clearly show the seller's full name, country, state and the amount. The seller's email will be displayed to the buyer during the trade process. payment.westernUnion.info=When using Western Union the XMR buyer has to send the MTCN (tracking number) and a photo of the receipt by email to the XMR seller. The receipt must clearly show the seller's full name, city, country and the amount. The seller's email will be displayed to the buyer during the trade process. payment.halCash.info=Ao usar o HalCash, o comprador de XMR precisa enviar ao vendedor de XMR o código HalCash através de uma mensagem de texto do seu telemóvel.\n\nPor favor, certifique-se de não exceder a quantia máxima que seu banco lhe permite enviar com o HalCash. A quantia mín. de levantamento é de 10 euros e a quantia máx. é de 600 EUR. Para levantamentos repetidos é de 3000 euros por recipiente por dia e 6000 euros por recipiente por mês. Por favor confirme esses limites com seu banco para ter certeza de que eles usam os mesmos limites mencionados aqui.\n\nA quantia de levantamento deve ser um múltiplo de 10 euros, pois você não pode levantar outras quantias de uma ATM. A interface do utilizador no ecrã para criar oferta e aceitar ofertas ajustará a quantia de XMR para que a quantia de EUR esteja correta. Você não pode usar o preço com base no mercado, pois o valor do EUR estaria mudando com a variação dos preços.\n\nEm caso de disputa, o comprador de XMR precisa fornecer a prova de que enviou o EUR. @@ -1988,7 +1983,7 @@ payment.limits.info.withSigning=To limit chargeback risk, Haveno sets per-trade payment.cashDeposit.info=Por favor, confirme que seu banco permite-lhe enviar depósitos em dinheiro para contas de outras pessoas. Por exemplo, o Bank of America e o Wells Fargo não permitem mais esses depósitos. payment.revolut.info=Revolut requires the 'Username' as account ID not the phone number or email as it was the case in the past. -payment.account.revolut.addUserNameInfo={0}\nYour existing Revolut account ({1}) does not have a ''Username''.\nPlease enter your Revolut ''Username'' to update your account data.\nThis will not affect your account age signing status. +payment.account.revolut.addUserNameInfo={0}\nYour existing Revolut account ({1}) does not have a 'Username'.\nPlease enter your Revolut 'Username' to update your account data.\nThis will not affect your account age signing status. payment.revolut.addUserNameInfo.headLine=Update Revolut account payment.cashapp.info=Esteja ciente de que o Cash App tem um risco de estorno maior do que a maioria das transferências bancárias. @@ -2022,7 +2017,7 @@ payment.japan.recipient=Nome payment.australia.payid=PayID payment.payid=PayID linked to financial institution. Like email address or mobile phone. payment.payid.info=A PayID like a phone number, email address or an Australian Business Number (ABN), that you can securely link to your bank, credit union or building society account. You need to have already created a PayID with your Australian financial institution. Both sending and receiving financial institutions must support PayID. For more information please check [HYPERLINK:https://payid.com.au/faqs/] -payment.amazonGiftCard.info=To pay with Amazon eGift Card, you will need to send an Amazon eGift Card to the XMR seller via your Amazon account. \n\nHaveno will show the XMR seller''s email address or phone number where the gift card should be sent, and you must include the trade ID in the gift card''s message field. Please see the wiki [HYPERLINK:https://docs.haveno.exchange/the-project/payment_methods/Amazon_eGift_card] for further details and best practices. \n\nThree important notes:\n- try to send gift cards with amounts of 100 USD or smaller, as Amazon is known to flag larger gift cards as fraudulent\n- try to use creative, believable text for the gift card''s message (e.g., "Happy birthday Susan!") along with the trade ID (and use trader chat to tell your trading peer the reference text you picked so they can verify your payment)\n- Amazon eGift Cards can only be redeemed on the Amazon website they were purchased on (e.g., a gift card purchased on amazon.it can only be redeemed on amazon.it) +payment.amazonGiftCard.info=To pay with Amazon eGift Card, you will need to send an Amazon eGift Card to the XMR seller via your Amazon account. \n\nHaveno will show the XMR seller's email address or phone number where the gift card should be sent, and you must include the trade ID in the gift card's message field. Please see the wiki [HYPERLINK:https://docs.haveno.exchange/the-project/payment_methods/Amazon_eGift_card] for further details and best practices. \n\nThree important notes:\n- try to send gift cards with amounts of 100 USD or smaller, as Amazon is known to flag larger gift cards as fraudulent\n- try to use creative, believable text for the gift card's message (e.g., "Happy birthday Susan!") along with the trade ID (and use trader chat to tell your trading peer the reference text you picked so they can verify your payment)\n- Amazon eGift Cards can only be redeemed on the Amazon website they were purchased on (e.g., a gift card purchased on amazon.it can only be redeemed on amazon.it) payment.paysafe.info=Para sua proteção, desaconselhamos fortemente o uso de PINs do Paysafecard para pagamento.\n\n\ Transações feitas por PINs não podem ser verificadas de forma independente para resolução de disputas. Se ocorrer um problema, a recuperação dos fundos pode não ser possível.\n\n\ Para garantir a segurança das transações com resolução de disputas, sempre use métodos de pagamento que forneçam registros verificáveis. diff --git a/core/src/main/resources/i18n/displayStrings_ru.properties b/core/src/main/resources/i18n/displayStrings_ru.properties index 848b765ae2..aac587efda 100644 --- a/core/src/main/resources/i18n/displayStrings_ru.properties +++ b/core/src/main/resources/i18n/displayStrings_ru.properties @@ -4,11 +4,6 @@ # E.g.: [main-view].[component].[description] # In some cases we use enum values or constants to map to display strings -# A annoying issue with property files is that we need to use 2 single quotes in display string -# containing variables (e.g. {0}), otherwise the variable will not be resolved. -# In display string which do not use a variable a single quote is ok. -# E.g. Don''t .... {1} - # We use sometimes dynamic parts which are put together in the code and therefore sometimes use line breaks or spaces # at the end of the string. Please never remove any line breaks or spaces. They are there with a purpose! # To make longer strings with better readable you can make a line break with \ which does not result in a line break @@ -112,7 +107,7 @@ shared.belowInPercent=% ниже рыночного курса shared.aboveInPercent=% выше рыночного курса shared.enterPercentageValue=Ввести величину в % shared.OR=ИЛИ -shared.notEnoughFunds=You don''t have enough funds in your Haveno wallet for this transaction—{0} is needed but only {1} is available.\n\nPlease add funds from an external wallet, or fund your Haveno wallet at Funds > Receive Funds. +shared.notEnoughFunds=You don't have enough funds in your Haveno wallet for this transaction—{0} is needed but only {1} is available.\n\nPlease add funds from an external wallet, or fund your Haveno wallet at Funds > Receive Funds. shared.waitingForFunds=Ожидание средств... shared.TheXMRBuyer=Покупатель ВТС shared.You=Вы @@ -359,7 +354,7 @@ offerbook.xmrAutoConf=Is auto-confirm enabled offerbook.buyXmrWith=Купить XMR с помощью: offerbook.sellXmrFor=Продать XMR за: -offerbook.timeSinceSigning.help=When you successfully complete a trade with a peer who has a signed payment account, your payment account is signed.\n{0} days later, the initial limit of {1} is lifted and your account can sign other peers'' payment accounts. +offerbook.timeSinceSigning.help=When you successfully complete a trade with a peer who has a signed payment account, your payment account is signed.\n{0} days later, the initial limit of {1} is lifted and your account can sign other peers' payment accounts. offerbook.timeSinceSigning.notSigned=Not signed yet offerbook.timeSinceSigning.notSigned.ageDays={0} дн. offerbook.timeSinceSigning.notSigned.noNeed=Н/Д @@ -399,7 +394,7 @@ offerbook.warning.counterpartyTradeRestrictions=This offer cannot be taken due t offerbook.warning.newVersionAnnouncement=With this version of the software, trading peers can verify and sign each others' payment accounts to create a network of trusted payment accounts.\n\nAfter successfully trading with a peer with a verified payment account, your payment account will be signed and trading limits will be lifted after a certain time interval (length of this interval is based on the verification method).\n\nFor more information on account signing, please see the documentation at [HYPERLINK:https://docs.haveno.exchange/payment-methods#account-signing]. -popup.warning.tradeLimitDueAccountAgeRestriction.seller=The allowed trade amount is limited to {0} because of security restrictions based on the following criteria:\n- The buyer''s account has not been signed by an arbitrator or a peer\n- The time since signing of the buyer''s account is not at least 30 days\n- The payment method for this offer is considered risky for bank chargebacks\n\n{1} +popup.warning.tradeLimitDueAccountAgeRestriction.seller=The allowed trade amount is limited to {0} because of security restrictions based on the following criteria:\n- The buyer's account has not been signed by an arbitrator or a peer\n- The time since signing of the buyer's account is not at least 30 days\n- The payment method for this offer is considered risky for bank chargebacks\n\n{1} popup.warning.tradeLimitDueAccountAgeRestriction.buyer=The allowed trade amount is limited to {0} because of security restrictions based on the following criteria:\n- Your account has not been signed by an arbitrator or a peer\n- The time since signing of your account is not at least 30 days\n- The payment method for this offer is considered risky for bank chargebacks\n\n{1} popup.warning.tradeLimitDueAccountAgeRestriction.seller.releaseLimit=Этот способ оплаты временно ограничен до {0} до {1}, поскольку все покупатели имеют новые аккаунты.\n\n{2} popup.warning.tradeLimitDueAccountAgeRestriction.seller.exceedsUnsignedBuyLimit=Ваше предложение будет ограничено для покупателей с подписанными и старыми аккаунтами, потому что оно превышает {0}.\n\n{1} @@ -643,7 +638,7 @@ portfolio.pending.step2_buyer.postal=Отправьте {0} \«Почтовым # suppress inspection "TrailingSpacesInProperty" portfolio.pending.step2_buyer.payByMail=Please send {0} using \"Pay by Mail\" to the XMR seller. Specific instructions are in the trade contract, or if unclear you may ask questions via trader chat. See more details about Pay by Mail on the Haveno wiki [HYPERLINK:https://docs.haveno.exchange/the-project/payment_methods/Pay_By_Mail].\n\n # suppress inspection "TrailingSpacesInProperty" -portfolio.pending.step2_buyer.pay=Please pay {0} via the specified payment method to the XMR seller. You''ll find the seller's account details on the next screen.\n\n +portfolio.pending.step2_buyer.pay=Please pay {0} via the specified payment method to the XMR seller. You'll find the seller's account details on the next screen.\n\n # suppress inspection "TrailingSpacesInProperty" portfolio.pending.step2_buyer.f2f=Свяжитесь с продавцом XMR с помощью указанных контактных данных и договоритесь о встрече для оплаты {0}.\n\n portfolio.pending.step2_buyer.startPaymentUsing=Начать оплату, используя {0} @@ -675,7 +670,7 @@ portfolio.pending.step2_seller.f2fInfo.headline=Контактная инфор portfolio.pending.step2_seller.waitPayment.msg=Депозитная транзакция подтверждена в блокчейне не менее одного раза.\nДождитесь начала платежа в {0} покупателем XMR. portfolio.pending.step2_seller.warn=Покупатель XMR все еще не завершил платеж в {0}.\nДождитесь начала оплаты.\nЕсли сделка не завершится {1}, арбитр начнет разбирательство. portfolio.pending.step2_seller.openForDispute=The XMR buyer has not started their payment!\nThe max. allowed period for the trade has elapsed.\nYou can wait longer and give the trading peer more time or contact the mediator for assistance. -tradeChat.chatWindowTitle=Chat window for trade with ID ''{0}'' +tradeChat.chatWindowTitle=Chat window for trade with ID '{0}' tradeChat.openChat=Open chat window tradeChat.rules=You can communicate with your trade peer to resolve potential problems with this trade.\nIt is not mandatory to reply in the chat.\nIf a trader violates any of the rules below, open a dispute and report it to the mediator or arbitrator.\n\nChat rules:\n\t● Do not send any links (risk of malware). You can send the transaction ID and the name of a block explorer.\n\t● Do not send your seed words, private keys, passwords or other sensitive information!\n\t● Do not encourage trading outside of Haveno (no security).\n\t● Do not engage in any form of social engineering scam attempts.\n\t● If a peer is not responding and prefers to not communicate via chat, respect their decision.\n\t● Keep conversation scope limited to the trade. This chat is not a messenger replacement or troll-box.\n\t● Keep conversation friendly and respectful. @@ -715,7 +710,7 @@ portfolio.pending.step3_seller.westernUnion=Покупатель обязан о portfolio.pending.step3_seller.halCash=Покупатель должен отправить вам код HalCash в текстовом сообщении. Кроме того, вы получите сообщение от HalCash с информацией, необходимой для снятия EUR в банкомате, поддерживающем HalCash.\n\nПосле того, как вы заберете деньги из банкомата, подтвердите получение платежа в приложении! portfolio.pending.step3_seller.amazonGiftCard=The buyer has sent you an Amazon eGift Card by email or by text message to your mobile phone. Please redeem now the Amazon eGift Card at your Amazon account and once accepted confirm the payment receipt. -portfolio.pending.step3_seller.bankCheck=\n\nPlease also verify that the name of the sender specified on the trade contract matches the name that appears on your bank statement:\nSender''s name, per trade contract: {0}\n\nIf the names are not exactly the same, {1} +portfolio.pending.step3_seller.bankCheck=\n\nPlease also verify that the name of the sender specified on the trade contract matches the name that appears on your bank statement:\nSender's name, per trade contract: {0}\n\nIf the names are not exactly the same, {1} # suppress inspection "TrailingSpacesInProperty" portfolio.pending.step3_seller.openDispute=don't confirm payment receipt. Instead, open a dispute by pressing \"alt + o\" or \"option + o\".\n\n portfolio.pending.step3_seller.confirmPaymentReceipt=Подтвердите получение платежа @@ -737,7 +732,7 @@ portfolio.pending.step3_seller.openForDispute=You have not confirmed the receipt # suppress inspection "TrailingSpacesInProperty" portfolio.pending.step3_seller.onPaymentReceived.part1=Вы получили платеж в {0} от своего контрагента?\n\n # suppress inspection "TrailingSpacesInProperty" -portfolio.pending.step3_seller.onPaymentReceived.name=Please also verify that the name of the sender specified on the trade contract matches the name that appears on your bank statement:\nSender''s name, per trade contract: {0}\n\nIf the names are not exactly the same, don''t confirm payment receipt. Instead, open a dispute by pressing \"alt + o\" or \"option + o\".\n\n +portfolio.pending.step3_seller.onPaymentReceived.name=Please also verify that the name of the sender specified on the trade contract matches the name that appears on your bank statement:\nSender's name, per trade contract: {0}\n\nIf the names are not exactly the same, don't confirm payment receipt. Instead, open a dispute by pressing \"alt + o\" or \"option + o\".\n\n # suppress inspection "TrailingSpacesInProperty" portfolio.pending.step3_seller.onPaymentReceived.note=Please note, that as soon you have confirmed the receipt, the locked trade amount will be released to the XMR buyer and the security deposit will be refunded.\n\n portfolio.pending.step3_seller.onPaymentReceived.confirm.headline=Подтвердите получение платежа @@ -811,9 +806,9 @@ portfolio.pending.mediationResult.info.selfAccepted=You have accepted the mediat portfolio.pending.mediationResult.info.peerAccepted=Your trade peer has accepted the mediator's suggestion. Do you accept as well? portfolio.pending.mediationResult.button=View proposed resolution portfolio.pending.mediationResult.popup.headline=Mediation result for trade with ID: {0} -portfolio.pending.mediationResult.popup.headline.peerAccepted=Your trade peer has accepted the mediator''s suggestion for trade {0} -portfolio.pending.mediationResult.popup.info=The mediator has suggested the following payout:\nYou receive: {0}\nYour trading peer receives: {1}\n\nYou can accept or reject this suggested payout.\n\nBy accepting, you sign the proposed payout transaction. If your trading peer also accepts and signs, the payout will be completed, and the trade will be closed.\n\nIf one or both of you reject the suggestion, you will have to wait until {2} (block {3}) to open a second-round dispute with an arbitrator who will investigate the case again and do a payout based on their findings.\n\nThe arbitrator may charge a small fee (fee maximum: the trader''s security deposit) as compensation for their work. Both traders agreeing to the mediator''s suggestion is the happy path—requesting arbitration is meant for exceptional circumstances, such as if a trader is sure the mediator did not make a fair payout suggestion (or if the other peer is unresponsive).\n\nMore details about the new arbitration model: [HYPERLINK:https://docs.haveno.exchange/trading-rules.html#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\nOnce 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\nYou can find more details about the arbitration model at:[HYPERLINK:https://docs.haveno.exchange/trading-rules.html#arbitration] +portfolio.pending.mediationResult.popup.headline.peerAccepted=Your trade peer has accepted the mediator's suggestion for trade {0} +portfolio.pending.mediationResult.popup.info=The mediator has suggested the following payout:\nYou receive: {0}\nYour trading peer receives: {1}\n\nYou can accept or reject this suggested payout.\n\nBy accepting, you sign the proposed payout transaction. If your trading peer also accepts and signs, the payout will be completed, and the trade will be closed.\n\nIf one or both of you reject the suggestion, you will have to wait until {2} (block {3}) to open a second-round dispute with an arbitrator who will investigate the case again and do a payout based on their findings.\n\nThe arbitrator may charge a small fee (fee maximum: the trader's security deposit) as compensation for their work. Both traders agreeing to the mediator's suggestion is the happy path—requesting arbitration is meant for exceptional circumstances, such as if a trader is sure the mediator did not make a fair payout suggestion (or if the other peer is unresponsive).\n\nMore details about the new arbitration model: [HYPERLINK:https://docs.haveno.exchange/trading-rules.html#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\nOnce 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\nYou can find more details about the arbitration model at:[HYPERLINK:https://docs.haveno.exchange/trading-rules.html#arbitration] portfolio.pending.mediationResult.popup.openArbitration=Reject and request arbitration portfolio.pending.mediationResult.popup.alreadyAccepted=You've already accepted @@ -977,7 +972,7 @@ support.sellerMaker=Продавец ВТС/мейкер support.buyerTaker=Покупатель ВТС/тейкер support.sellerTaker=Продавец XMR/тейкер -support.initialInfo=Please enter a description of your problem in the text field below. Add as much information as possible to speed up dispute resolution time.\n\nHere is a check list for information you should provide:\n\t● If you are the XMR buyer: Did you make the Fiat or Crypto transfer? If so, did you click the 'payment started' button in the application?\n\t● If you are the XMR seller: Did you receive the Fiat or Crypto payment? If so, did you click the 'payment received' button in the application?\n\t● Which version of Haveno are you using?\n\t● Which operating system are you using?\n\t● If you encountered an issue with failed transactions please consider switching to a new data directory.\n\t Sometimes the data directory gets corrupted and leads to strange bugs. \n\t See: https://docs.haveno.exchange/backup-recovery.html#switch-to-a-new-data-directory\n\nPlease 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● Mediators respond in between 2 days. Arbitrators respond in between 5 business days.\n\t● The maximum period for a dispute is 14 days.\n\t● You need to cooperate with the {1} 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\nYou can read more about the dispute process at: {2} +support.initialInfo=Please enter a description of your problem in the text field below. Add as much information as possible to speed up dispute resolution time.\n\nHere is a check list for information you should provide:\n\t● If you are the XMR buyer: Did you make the Fiat or Crypto transfer? If so, did you click the 'payment started' button in the application?\n\t● If you are the XMR seller: Did you receive the Fiat or Crypto payment? If so, did you click the 'payment received' button in the application?\n\t● Which version of Haveno are you using?\n\t● Which operating system are you using?\n\t● If you encountered an issue with failed transactions please consider switching to a new data directory.\n\t Sometimes the data directory gets corrupted and leads to strange bugs. \n\t See: https://docs.haveno.exchange/backup-recovery.html#switch-to-a-new-data-directory\n\nPlease 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● Mediators respond in between 2 days. Arbitrators respond in between 5 business days.\n\t● The maximum period for a dispute is 14 days.\n\t● You need to cooperate with the {1} 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\nYou can read more about the dispute process at: {2} support.systemMsg=Системное сообщение: {0} support.youOpenedTicket=Вы запросили поддержку.\n\n{0}\n\nВерсия Haveno: {1} support.youOpenedDispute=Вы начали спор.\n\n{0}\n\nВерсия Haveno: {1} @@ -985,8 +980,8 @@ support.youOpenedDisputeForMediation=You requested mediation.\n\n{0}\n\nHaveno v support.peerOpenedTicket=Your trading peer has requested support due to technical problems.\n\n{0}\n\nHaveno version: {1} support.peerOpenedDispute=Your trading peer has requested a dispute.\n\n{0}\n\nHaveno version: {1} support.peerOpenedDisputeForMediation=Your trading peer has requested mediation.\n\n{0}\n\nHaveno version: {1} -support.mediatorsDisputeSummary=System message: Mediator''s dispute summary:\n{0} -support.mediatorsAddress=Mediator''s node address: {0} +support.mediatorsDisputeSummary=System message: Mediator's dispute summary:\n{0} +support.mediatorsAddress=Mediator's node address: {0} support.warning.disputesWithInvalidDonationAddress=The delayed payout transaction has used an invalid receiver address. It does not match any of the DAO parameter values for the valid donation addresses.\n\nThis might be a scam attempt. Please inform the developers about that incident and do not close that case before the situation is resolved!\n\nAddress used in the dispute: {0}\n\nAll DAO param donation addresses: {1}\n\nTrade ID: {2}{3} support.warning.disputesWithInvalidDonationAddress.mediator=\n\nDo you still want to close the dispute? support.warning.disputesWithInvalidDonationAddress.refundAgent=\n\nYou must not do the payout. @@ -1112,19 +1107,19 @@ setting.about.subsystems.label=Версии подсистем setting.about.subsystems.val=Версия сети: {0}; версия P2P-сообщений: {1}; версия локальной базы данных: {2}; версия торгового протокола: {3} setting.about.shortcuts=Short cuts -setting.about.shortcuts.ctrlOrAltOrCmd=''Ctrl + {0}'' or ''alt + {0}'' or ''cmd + {0}'' +setting.about.shortcuts.ctrlOrAltOrCmd='Ctrl + {0}' or 'alt + {0}' or 'cmd + {0}' setting.about.shortcuts.menuNav=Navigate main menu setting.about.shortcuts.menuNav.value=To navigate the main menu press: 'Ctrl' or 'alt' or 'cmd' with a numeric key between '1-9' setting.about.shortcuts.close=Close Haveno -setting.about.shortcuts.close.value=''Ctrl + {0}'' or ''cmd + {0}'' or ''Ctrl + {1}'' or ''cmd + {1}'' +setting.about.shortcuts.close.value='Ctrl + {0}' or 'cmd + {0}' or 'Ctrl + {1}' or 'cmd + {1}' setting.about.shortcuts.closePopup=Close popup or dialog window setting.about.shortcuts.closePopup.value='ESCAPE' key setting.about.shortcuts.chatSendMsg=Send trader chat message -setting.about.shortcuts.chatSendMsg.value=''Ctrl + ENTER'' or ''alt + ENTER'' or ''cmd + ENTER'' +setting.about.shortcuts.chatSendMsg.value='Ctrl + ENTER' or 'alt + ENTER' or 'cmd + ENTER' setting.about.shortcuts.openDispute=Open dispute setting.about.shortcuts.openDispute.value=Select pending trade and click: {0} @@ -1199,7 +1194,7 @@ account.arbitratorRegistration.registerSuccess=You have successfully registered account.arbitratorRegistration.registerFailed=Could not complete registration.{0} account.crypto.yourCryptoAccounts=Ваши альткойн-счета -account.crypto.popup.wallet.msg=Please be sure that you follow the requirements for the usage of {0} wallets as described on the {1} web page.\nUsing wallets from centralized exchanges where (a) you don''t control your keys or (b) which don''t use compatible wallet software is risky: it can lead to loss of the traded funds!\nThe mediator or arbitrator is not a {2} specialist and cannot help in such cases. +account.crypto.popup.wallet.msg=Please be sure that you follow the requirements for the usage of {0} wallets as described on the {1} web page.\nUsing wallets from centralized exchanges where (a) you don't control your keys or (b) which don't use compatible wallet software is risky: it can lead to loss of the traded funds!\nThe mediator or arbitrator is not a {2} specialist and cannot help in such cases. account.crypto.popup.wallet.confirm=Я понимаю и подтверждаю, что знаю, какой кошелёк нужно использовать. # suppress inspection "UnusedProperty" account.crypto.popup.upx.msg=Trading UPX on Haveno requires that you understand and fulfill the following requirements:\n\nFor sending UPX, you need to use either the official uPlexa GUI wallet or uPlexa CLI wallet with the store-tx-info flag enabled (default in new versions). Please be sure you can access the tx key as that would be required in case of a dispute.\nuplexa-wallet-cli (use the command get_tx_key)\nuplexa-wallet-gui (go to history tab and click on the (P) button for payment proof)\n\nAt normal block explorers the transfer is not verifiable.\n\nYou need to provide the arbitrator the following data in case of a dispute:\n- The tx private key\n- The transaction hash\n- The recipient's public address\n\nFailure to provide the above data, or if you used an incompatible wallet, will result in losing the dispute case. The UPX sender is responsible for providing verification of the UPX transfer to the arbitrator in case of a dispute.\n\nThere is no payment ID required, just the normal public address.\nIf you are not sure about that process visit uPlexa discord channel (https://discord.gg/vhdNSrV) or the uPlexa Telegram Chat (https://t.me/uplexaOfficial) to find more information. @@ -1680,8 +1675,8 @@ popup.accountSigning.signAccounts.ECKey.error=Bad arbitrator ECKey popup.accountSigning.success.headline=Congratulations popup.accountSigning.success.description=All {0} payment accounts were successfully signed! popup.accountSigning.generalInformation=You'll find the signing state of all your accounts in the account section.\n\nFor further information, please visit [HYPERLINK:https://docs.haveno.exchange/payment-methods#account-signing]. -popup.accountSigning.signedByArbitrator=One of your payment accounts has been verified and signed by an arbitrator. Trading with this account will automatically sign your trading peer''s account after a successful trade.\n\n{0} -popup.accountSigning.signedByPeer=One of your payment accounts has been verified and signed by a trading peer. Your initial trading limit will be lifted and you''ll be able to sign other accounts in {0} days from now.\n\n{1} +popup.accountSigning.signedByArbitrator=One of your payment accounts has been verified and signed by an arbitrator. Trading with this account will automatically sign your trading peer's account after a successful trade.\n\n{0} +popup.accountSigning.signedByPeer=One of your payment accounts has been verified and signed by a trading peer. Your initial trading limit will be lifted and you'll be able to sign other accounts in {0} days from now.\n\n{1} popup.accountSigning.peerLimitLifted=The initial limit for one of your accounts has been lifted.\n\n{0} popup.accountSigning.peerSigner=One of your accounts is mature enough to sign other payment accounts and the initial limit for one of your accounts has been lifted.\n\n{0} @@ -1976,8 +1971,8 @@ payment.accountType=Тип счёта payment.checking=Текущий payment.savings=Сберегательный payment.personalId=Личный идентификатор -payment.zelle.info=Zelle is a money transfer service that works best *through* another bank.\n\n1. Check this page to see if (and how) your bank works with Zelle: [HYPERLINK:https://www.zellepay.com/get-started]\n\n2. Take special note of your transfer limits—sending limits vary by bank, and banks often specify separate daily, weekly, and monthly limits.\n\n3. If your bank does not work with Zelle, you can still use it through the Zelle mobile app, but your transfer limits will be much lower.\n\n4. The name specified on your Haveno account MUST match the name on your Zelle/bank account. \n\nIf you cannot complete a Zelle transaction as specified in your trade contract, you may lose some (or all) of your security deposit.\n\nBecause of Zelle''s somewhat higher chargeback risk, sellers are advised to contact unsigned buyers through email or SMS to verify that the buyer really owns the Zelle account specified in Haveno. -payment.fasterPayments.newRequirements.info=Some banks have started verifying the receiver''s full name for Faster Payments transfers. Your current Faster Payments account does not specify a full name.\n\nPlease consider recreating your Faster Payments account in Haveno to provide future {0} buyers with a full name.\n\nWhen you recreate the account, make sure to copy the precise sort code, account number and account age verification salt values from your old account to your new account. This will ensure your existing account''s age and signing status are preserved. +payment.zelle.info=Zelle is a money transfer service that works best *through* another bank.\n\n1. Check this page to see if (and how) your bank works with Zelle: [HYPERLINK:https://www.zellepay.com/get-started]\n\n2. Take special note of your transfer limits—sending limits vary by bank, and banks often specify separate daily, weekly, and monthly limits.\n\n3. If your bank does not work with Zelle, you can still use it through the Zelle mobile app, but your transfer limits will be much lower.\n\n4. The name specified on your Haveno account MUST match the name on your Zelle/bank account. \n\nIf you cannot complete a Zelle transaction as specified in your trade contract, you may lose some (or all) of your security deposit.\n\nBecause of Zelle's somewhat higher chargeback risk, sellers are advised to contact unsigned buyers through email or SMS to verify that the buyer really owns the Zelle account specified in Haveno. +payment.fasterPayments.newRequirements.info=Some banks have started verifying the receiver's full name for Faster Payments transfers. Your current Faster Payments account does not specify a full name.\n\nPlease consider recreating your Faster Payments account in Haveno to provide future {0} buyers with a full name.\n\nWhen you recreate the account, make sure to copy the precise sort code, account number and account age verification salt values from your old account to your new account. This will ensure your existing account's age and signing status are preserved. payment.moneyGram.info=When using MoneyGram the XMR buyer has to send the Authorisation number and a photo of the receipt by email to the XMR seller. The receipt must clearly show the seller's full name, country, state and the amount. The seller's email will be displayed to the buyer during the trade process. payment.westernUnion.info=When using Western Union the XMR buyer has to send the MTCN (tracking number) and a photo of the receipt by email to the XMR seller. The receipt must clearly show the seller's full name, city, country and the amount. The seller's email will be displayed to the buyer during the trade process. payment.halCash.info=Используя HalCash, покупатель XMR обязуется отправить продавцу XMR код HalCash через СМС с мобильного телефона.\n\nУбедитесь, что не вы не превысили максимальную сумму, которую ваш банк позволяет отправить с HalCash. Минимальная сумма на вывод средств составляет 10 EUR, а и максимальная — 600 EUR. При повторном выводе средств лимит составляет 3000 EUR на получателя в день и 6000 EUR на получателя в месяц. Просьба сверить эти лимиты с вашим банком и убедиться, что лимиты банка соответствуют лимитам, указанным здесь.\n\nВыводимая сумма должна быть кратна 10 EUR, так как другие суммы снять из банкомата невозможно. Приложение само отрегулирует сумму XMR, чтобы она соответствовала сумме в EUR, во время создания или принятия предложения. Вы не сможете использовать текущий рыночный курс, так как сумма в EUR будет меняться с изменением курса.\n\nВ случае спора покупателю XMR необходимо предоставить доказательство отправки EUR. @@ -1989,7 +1984,7 @@ payment.limits.info.withSigning=To limit chargeback risk, Haveno sets per-trade payment.cashDeposit.info=Убедитесь, что ваш банк позволяет отправлять денежные переводы на счета других лиц. Например, Bank of America и Wells Fargo больше не разрешают такие переводы. payment.revolut.info=Revolut requires the 'Username' as account ID not the phone number or email as it was the case in the past. -payment.account.revolut.addUserNameInfo={0}\nYour existing Revolut account ({1}) does not have a ''Username''.\nPlease enter your Revolut ''Username'' to update your account data.\nThis will not affect your account age signing status. +payment.account.revolut.addUserNameInfo={0}\nYour existing Revolut account ({1}) does not have a 'Username'.\nPlease enter your Revolut 'Username' to update your account data.\nThis will not affect your account age signing status. payment.revolut.addUserNameInfo.headLine=Update Revolut account payment.cashapp.info=Обратите внимание, что Cash App имеет более высокий риск возврата платежей, чем большинство банковских переводов. @@ -2024,7 +2019,7 @@ payment.japan.recipient=Имя payment.australia.payid=PayID payment.payid=PayID linked to financial institution. Like email address or mobile phone. payment.payid.info=A PayID like a phone number, email address or an Australian Business Number (ABN), that you can securely link to your bank, credit union or building society account. You need to have already created a PayID with your Australian financial institution. Both sending and receiving financial institutions must support PayID. For more information please check [HYPERLINK:https://payid.com.au/faqs/] -payment.amazonGiftCard.info=To pay with Amazon eGift Card, you will need to send an Amazon eGift Card to the XMR seller via your Amazon account. \n\nHaveno will show the XMR seller''s email address or phone number where the gift card should be sent, and you must include the trade ID in the gift card''s message field. Please see the wiki [HYPERLINK:https://docs.haveno.exchange/the-project/payment_methods/Amazon_eGift_card] for further details and best practices. \n\nThree important notes:\n- try to send gift cards with amounts of 100 USD or smaller, as Amazon is known to flag larger gift cards as fraudulent\n- try to use creative, believable text for the gift card''s message (e.g., "Happy birthday Susan!") along with the trade ID (and use trader chat to tell your trading peer the reference text you picked so they can verify your payment)\n- Amazon eGift Cards can only be redeemed on the Amazon website they were purchased on (e.g., a gift card purchased on amazon.it can only be redeemed on amazon.it) +payment.amazonGiftCard.info=To pay with Amazon eGift Card, you will need to send an Amazon eGift Card to the XMR seller via your Amazon account. \n\nHaveno will show the XMR seller's email address or phone number where the gift card should be sent, and you must include the trade ID in the gift card's message field. Please see the wiki [HYPERLINK:https://docs.haveno.exchange/the-project/payment_methods/Amazon_eGift_card] for further details and best practices. \n\nThree important notes:\n- try to send gift cards with amounts of 100 USD or smaller, as Amazon is known to flag larger gift cards as fraudulent\n- try to use creative, believable text for the gift card's message (e.g., "Happy birthday Susan!") along with the trade ID (and use trader chat to tell your trading peer the reference text you picked so they can verify your payment)\n- Amazon eGift Cards can only be redeemed on the Amazon website they were purchased on (e.g., a gift card purchased on amazon.it can only be redeemed on amazon.it) payment.paysafe.info=Для вашей защиты мы настоятельно не рекомендуем использовать PIN-коды Paysafecard для платежей.\n\n\ Транзакции, выполненные с помощью PIN-кодов, не могут быть независимо подтверждены для разрешения споров. В случае возникновения проблемы возврат средств может быть невозможен.\n\n\ Чтобы обеспечить безопасность транзакций с возможностью разрешения споров, всегда используйте методы оплаты, предоставляющие проверяемые записи. diff --git a/core/src/main/resources/i18n/displayStrings_th.properties b/core/src/main/resources/i18n/displayStrings_th.properties index df37fc3f28..8da8c3c0ff 100644 --- a/core/src/main/resources/i18n/displayStrings_th.properties +++ b/core/src/main/resources/i18n/displayStrings_th.properties @@ -4,11 +4,6 @@ # E.g.: [main-view].[component].[description] # In some cases we use enum values or constants to map to display strings -# A annoying issue with property files is that we need to use 2 single quotes in display string -# containing variables (e.g. {0}), otherwise the variable will not be resolved. -# In display string which do not use a variable a single quote is ok. -# E.g. Don''t .... {1} - # We use sometimes dynamic parts which are put together in the code and therefore sometimes use line breaks or spaces # at the end of the string. Please never remove any line breaks or spaces. They are there with a purpose! # To make longer strings with better readable you can make a line break with \ which does not result in a line break @@ -112,7 +107,7 @@ shared.belowInPercent=ต่ำกว่า % จากราคาตลาด shared.aboveInPercent=สูงกว่า % จากราคาตาด shared.enterPercentageValue=เข้าสู่ % ตามมูลค่า shared.OR=หรือ -shared.notEnoughFunds=You don''t have enough funds in your Haveno wallet for this transaction—{0} is needed but only {1} is available.\n\nPlease add funds from an external wallet, or fund your Haveno wallet at Funds > Receive Funds. +shared.notEnoughFunds=You don't have enough funds in your Haveno wallet for this transaction—{0} is needed but only {1} is available.\n\nPlease add funds from an external wallet, or fund your Haveno wallet at Funds > Receive Funds. shared.waitingForFunds=กำลังรอเงิน ... shared.TheXMRBuyer=ผู้ซื้อ XMR shared.You=คุณ @@ -359,7 +354,7 @@ offerbook.xmrAutoConf=Is auto-confirm enabled offerbook.buyXmrWith=ซื้อ XMR ด้วย: offerbook.sellXmrFor=ขาย XMR สำหรับ: -offerbook.timeSinceSigning.help=When you successfully complete a trade with a peer who has a signed payment account, your payment account is signed.\n{0} days later, the initial limit of {1} is lifted and your account can sign other peers'' payment accounts. +offerbook.timeSinceSigning.help=When you successfully complete a trade with a peer who has a signed payment account, your payment account is signed.\n{0} days later, the initial limit of {1} is lifted and your account can sign other peers' payment accounts. offerbook.timeSinceSigning.notSigned=Not signed yet offerbook.timeSinceSigning.notSigned.ageDays={0} วัน offerbook.timeSinceSigning.notSigned.noNeed=ไม่พร้อมใช้งาน @@ -399,7 +394,7 @@ offerbook.warning.counterpartyTradeRestrictions=This offer cannot be taken due t offerbook.warning.newVersionAnnouncement=With this version of the software, trading peers can verify and sign each others' payment accounts to create a network of trusted payment accounts.\n\nAfter successfully trading with a peer with a verified payment account, your payment account will be signed and trading limits will be lifted after a certain time interval (length of this interval is based on the verification method).\n\nFor more information on account signing, please see the documentation at [HYPERLINK:https://docs.haveno.exchange/payment-methods#account-signing]. -popup.warning.tradeLimitDueAccountAgeRestriction.seller=The allowed trade amount is limited to {0} because of security restrictions based on the following criteria:\n- The buyer''s account has not been signed by an arbitrator or a peer\n- The time since signing of the buyer''s account is not at least 30 days\n- The payment method for this offer is considered risky for bank chargebacks\n\n{1} +popup.warning.tradeLimitDueAccountAgeRestriction.seller=The allowed trade amount is limited to {0} because of security restrictions based on the following criteria:\n- The buyer's account has not been signed by an arbitrator or a peer\n- The time since signing of the buyer's account is not at least 30 days\n- The payment method for this offer is considered risky for bank chargebacks\n\n{1} popup.warning.tradeLimitDueAccountAgeRestriction.buyer=The allowed trade amount is limited to {0} because of security restrictions based on the following criteria:\n- Your account has not been signed by an arbitrator or a peer\n- The time since signing of your account is not at least 30 days\n- The payment method for this offer is considered risky for bank chargebacks\n\n{1} popup.warning.tradeLimitDueAccountAgeRestriction.seller.releaseLimit=วิธีการชำระเงินนี้ถูก จำกัด ชั่วคราวไปยัง {0} จนถึง {1} เนื่องจากผู้ซื้อทุกคนมีบัญชีใหม่\n\n{2} popup.warning.tradeLimitDueAccountAgeRestriction.seller.exceedsUnsignedBuyLimit=ข้อเสนอของคุณจะถูก จำกัด เฉพาะผู้ซื้อที่มีบัญชีที่ได้ลงนามและมีอายุ เนื่องจากมันเกิน {0}.\n\n{1} @@ -488,7 +483,7 @@ createOffer.currencyForFee=ค่าธรรมเนียมการซื createOffer.setDeposit=Set buyer's security deposit (%) createOffer.setDepositAsBuyer=Set my security deposit as buyer (%) createOffer.setDepositForBothTraders=Set both traders' security deposit (%) -createOffer.securityDepositInfo=Your buyer''s security deposit will be {0} +createOffer.securityDepositInfo=Your buyer's security deposit will be {0} createOffer.securityDepositInfoAsBuyer=Your security deposit as buyer will be {0} createOffer.minSecurityDepositUsed=เงินประกันความปลอดภัยขั้นต่ำถูกใช้ createOffer.buyerAsTakerWithoutDeposit=ไม่ต้องวางมัดจำจากผู้ซื้อ (ป้องกันด้วยรหัสผ่าน) @@ -643,7 +638,7 @@ portfolio.pending.step2_buyer.postal=โปรดส่ง {0} โดยธน # suppress inspection "TrailingSpacesInProperty" portfolio.pending.step2_buyer.payByMail=Please send {0} using \"Pay by Mail\" to the XMR seller. Specific instructions are in the trade contract, or if unclear you may ask questions via trader chat. See more details about Pay by Mail on the Haveno wiki [HYPERLINK:https://docs.haveno.exchange/the-project/payment_methods/Pay_By_Mail].\n\n # suppress inspection "TrailingSpacesInProperty" -portfolio.pending.step2_buyer.pay=Please pay {0} via the specified payment method to the XMR seller. You''ll find the seller's account details on the next screen.\n\n +portfolio.pending.step2_buyer.pay=Please pay {0} via the specified payment method to the XMR seller. You'll find the seller's account details on the next screen.\n\n # suppress inspection "TrailingSpacesInProperty" portfolio.pending.step2_buyer.f2f=กรุณาติดต่อผู้ขายของ XMR ตามรายชื่อที่ได้รับและนัดประชุมเพื่อจ่ายเงิน {0}\n\n portfolio.pending.step2_buyer.startPaymentUsing=เริ่มต้นการชำระเงินโดยใช้ {0} @@ -675,7 +670,7 @@ portfolio.pending.step2_seller.f2fInfo.headline=ข้อมูลการต portfolio.pending.step2_seller.waitPayment.msg=ธุรกรรมการฝากเงินมีการยืนยันบล็อกเชนอย่างน้อยหนึ่งรายการ\nคุณต้องรอจนกว่าผู้ซื้อ XMR จะเริ่มการชำระเงิน {0} portfolio.pending.step2_seller.warn=ผู้ซื้อ XMR ยังไม่ได้ทำ {0} การชำระเงิน\nคุณต้องรอจนกว่าผู้ซื้อจะเริ่มชำระเงิน\nหากการซื้อขายยังไม่เสร็จสิ้นในวันที่ {1} ผู้ไกล่เกลี่ยจะดำเนินการตรวจสอบ portfolio.pending.step2_seller.openForDispute=The XMR buyer has not started their payment!\nThe max. allowed period for the trade has elapsed.\nYou can wait longer and give the trading peer more time or contact the mediator for assistance. -tradeChat.chatWindowTitle=Chat window for trade with ID ''{0}'' +tradeChat.chatWindowTitle=Chat window for trade with ID '{0}' tradeChat.openChat=Open chat window tradeChat.rules=You can communicate with your trade peer to resolve potential problems with this trade.\nIt is not mandatory to reply in the chat.\nIf a trader violates any of the rules below, open a dispute and report it to the mediator or arbitrator.\n\nChat rules:\n\t● Do not send any links (risk of malware). You can send the transaction ID and the name of a block explorer.\n\t● Do not send your seed words, private keys, passwords or other sensitive information!\n\t● Do not encourage trading outside of Haveno (no security).\n\t● Do not engage in any form of social engineering scam attempts.\n\t● If a peer is not responding and prefers to not communicate via chat, respect their decision.\n\t● Keep conversation scope limited to the trade. This chat is not a messenger replacement or troll-box.\n\t● Keep conversation friendly and respectful. @@ -715,7 +710,7 @@ portfolio.pending.step3_seller.westernUnion=ผู้ซื้อต้องส portfolio.pending.step3_seller.halCash=ผู้ซื้อต้องส่งข้อความรหัส HalCash ให้คุณ ในขณะเดียวกันคุณจะได้รับข้อความจาก HalCash พร้อมกับคำขอข้อมูลจำเป็นในการถอนเงินยูโรุจากตู้เอทีเอ็มที่รองรับ HalCash \n\n หลังจากที่คุณได้รับเงินจากตู้เอทีเอ็มโปรดยืนยันใบเสร็จรับเงินจากการชำระเงินที่นี่ ! portfolio.pending.step3_seller.amazonGiftCard=The buyer has sent you an Amazon eGift Card by email or by text message to your mobile phone. Please redeem now the Amazon eGift Card at your Amazon account and once accepted confirm the payment receipt. -portfolio.pending.step3_seller.bankCheck=\n\nPlease also verify that the name of the sender specified on the trade contract matches the name that appears on your bank statement:\nSender''s name, per trade contract: {0}\n\nIf the names are not exactly the same, {1} +portfolio.pending.step3_seller.bankCheck=\n\nPlease also verify that the name of the sender specified on the trade contract matches the name that appears on your bank statement:\nSender's name, per trade contract: {0}\n\nIf the names are not exactly the same, {1} # suppress inspection "TrailingSpacesInProperty" portfolio.pending.step3_seller.openDispute=don't confirm payment receipt. Instead, open a dispute by pressing \"alt + o\" or \"option + o\".\n\n portfolio.pending.step3_seller.confirmPaymentReceipt=ใบเสร็จยืนยันการชำระเงิน @@ -737,7 +732,7 @@ portfolio.pending.step3_seller.openForDispute=You have not confirmed the receipt # suppress inspection "TrailingSpacesInProperty" portfolio.pending.step3_seller.onPaymentReceived.part1=คุณได้รับ {0} การชำระเงินจากคู่ค้าของคุณหรือไม่\n # suppress inspection "TrailingSpacesInProperty" -portfolio.pending.step3_seller.onPaymentReceived.name=Please also verify that the name of the sender specified on the trade contract matches the name that appears on your bank statement:\nSender''s name, per trade contract: {0}\n\nIf the names are not exactly the same, don''t confirm payment receipt. Instead, open a dispute by pressing \"alt + o\" or \"option + o\".\n\n +portfolio.pending.step3_seller.onPaymentReceived.name=Please also verify that the name of the sender specified on the trade contract matches the name that appears on your bank statement:\nSender's name, per trade contract: {0}\n\nIf the names are not exactly the same, don't confirm payment receipt. Instead, open a dispute by pressing \"alt + o\" or \"option + o\".\n\n # suppress inspection "TrailingSpacesInProperty" portfolio.pending.step3_seller.onPaymentReceived.note=Please note, that as soon you have confirmed the receipt, the locked trade amount will be released to the XMR buyer and the security deposit will be refunded.\n\n portfolio.pending.step3_seller.onPaymentReceived.confirm.headline=ยืนยันว่าคุณได้รับการชำระเงินแล้ว @@ -811,9 +806,9 @@ portfolio.pending.mediationResult.info.selfAccepted=You have accepted the mediat portfolio.pending.mediationResult.info.peerAccepted=Your trade peer has accepted the mediator's suggestion. Do you accept as well? portfolio.pending.mediationResult.button=View proposed resolution portfolio.pending.mediationResult.popup.headline=Mediation result for trade with ID: {0} -portfolio.pending.mediationResult.popup.headline.peerAccepted=Your trade peer has accepted the mediator''s suggestion for trade {0} -portfolio.pending.mediationResult.popup.info=The mediator has suggested the following payout:\nYou receive: {0}\nYour trading peer receives: {1}\n\nYou can accept or reject this suggested payout.\n\nBy accepting, you sign the proposed payout transaction. If your trading peer also accepts and signs, the payout will be completed, and the trade will be closed.\n\nIf one or both of you reject the suggestion, you will have to wait until {2} (block {3}) to open a second-round dispute with an arbitrator who will investigate the case again and do a payout based on their findings.\n\nThe arbitrator may charge a small fee (fee maximum: the trader''s security deposit) as compensation for their work. Both traders agreeing to the mediator''s suggestion is the happy path—requesting arbitration is meant for exceptional circumstances, such as if a trader is sure the mediator did not make a fair payout suggestion (or if the other peer is unresponsive).\n\nMore details about the new arbitration model: [HYPERLINK:https://docs.haveno.exchange/trading-rules.html#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\nOnce 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\nYou can find more details about the arbitration model at:[HYPERLINK:https://docs.haveno.exchange/trading-rules.html#arbitration] +portfolio.pending.mediationResult.popup.headline.peerAccepted=Your trade peer has accepted the mediator's suggestion for trade {0} +portfolio.pending.mediationResult.popup.info=The mediator has suggested the following payout:\nYou receive: {0}\nYour trading peer receives: {1}\n\nYou can accept or reject this suggested payout.\n\nBy accepting, you sign the proposed payout transaction. If your trading peer also accepts and signs, the payout will be completed, and the trade will be closed.\n\nIf one or both of you reject the suggestion, you will have to wait until {2} (block {3}) to open a second-round dispute with an arbitrator who will investigate the case again and do a payout based on their findings.\n\nThe arbitrator may charge a small fee (fee maximum: the trader's security deposit) as compensation for their work. Both traders agreeing to the mediator's suggestion is the happy path—requesting arbitration is meant for exceptional circumstances, such as if a trader is sure the mediator did not make a fair payout suggestion (or if the other peer is unresponsive).\n\nMore details about the new arbitration model: [HYPERLINK:https://docs.haveno.exchange/trading-rules.html#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\nOnce 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\nYou can find more details about the arbitration model at:[HYPERLINK:https://docs.haveno.exchange/trading-rules.html#arbitration] portfolio.pending.mediationResult.popup.openArbitration=Reject and request arbitration portfolio.pending.mediationResult.popup.alreadyAccepted=You've already accepted @@ -977,7 +972,7 @@ support.sellerMaker= XMR ผู้ขาย/ ผู้สร้าง support.buyerTaker=XMR ผู้ซื้อ / ผู้รับ support.sellerTaker=XMR ผู้ขาย / ผู้รับ -support.initialInfo=Please enter a description of your problem in the text field below. Add as much information as possible to speed up dispute resolution time.\n\nHere is a check list for information you should provide:\n\t● If you are the XMR buyer: Did you make the Fiat or Crypto transfer? If so, did you click the 'payment started' button in the application?\n\t● If you are the XMR seller: Did you receive the Fiat or Crypto payment? If so, did you click the 'payment received' button in the application?\n\t● Which version of Haveno are you using?\n\t● Which operating system are you using?\n\t● If you encountered an issue with failed transactions please consider switching to a new data directory.\n\t Sometimes the data directory gets corrupted and leads to strange bugs. \n\t See: https://docs.haveno.exchange/backup-recovery.html#switch-to-a-new-data-directory\n\nPlease 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● Mediators respond in between 2 days. Arbitrators respond in between 5 business days.\n\t● The maximum period for a dispute is 14 days.\n\t● You need to cooperate with the {1} 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\nYou can read more about the dispute process at: {2} +support.initialInfo=Please enter a description of your problem in the text field below. Add as much information as possible to speed up dispute resolution time.\n\nHere is a check list for information you should provide:\n\t● If you are the XMR buyer: Did you make the Fiat or Crypto transfer? If so, did you click the 'payment started' button in the application?\n\t● If you are the XMR seller: Did you receive the Fiat or Crypto payment? If so, did you click the 'payment received' button in the application?\n\t● Which version of Haveno are you using?\n\t● Which operating system are you using?\n\t● If you encountered an issue with failed transactions please consider switching to a new data directory.\n\t Sometimes the data directory gets corrupted and leads to strange bugs. \n\t See: https://docs.haveno.exchange/backup-recovery.html#switch-to-a-new-data-directory\n\nPlease 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● Mediators respond in between 2 days. Arbitrators respond in between 5 business days.\n\t● The maximum period for a dispute is 14 days.\n\t● You need to cooperate with the {1} 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\nYou can read more about the dispute process at: {2} support.systemMsg=ระบบข้อความ: {0} support.youOpenedTicket=You opened a request for support.\n\n{0}\n\nHaveno version: {1} support.youOpenedDispute=You opened a request for a dispute.\n\n{0}\n\nHaveno version: {1} @@ -985,8 +980,8 @@ support.youOpenedDisputeForMediation=You requested mediation.\n\n{0}\n\nHaveno v support.peerOpenedTicket=Your trading peer has requested support due to technical problems.\n\n{0}\n\nHaveno version: {1} support.peerOpenedDispute=Your trading peer has requested a dispute.\n\n{0}\n\nHaveno version: {1} support.peerOpenedDisputeForMediation=Your trading peer has requested mediation.\n\n{0}\n\nHaveno version: {1} -support.mediatorsDisputeSummary=System message: Mediator''s dispute summary:\n{0} -support.mediatorsAddress=Mediator''s node address: {0} +support.mediatorsDisputeSummary=System message: Mediator's dispute summary:\n{0} +support.mediatorsAddress=Mediator's node address: {0} support.warning.disputesWithInvalidDonationAddress=The delayed payout transaction has used an invalid receiver address. It does not match any of the DAO parameter values for the valid donation addresses.\n\nThis might be a scam attempt. Please inform the developers about that incident and do not close that case before the situation is resolved!\n\nAddress used in the dispute: {0}\n\nAll DAO param donation addresses: {1}\n\nTrade ID: {2}{3} support.warning.disputesWithInvalidDonationAddress.mediator=\n\nDo you still want to close the dispute? support.warning.disputesWithInvalidDonationAddress.refundAgent=\n\nYou must not do the payout. @@ -1112,19 +1107,19 @@ setting.about.subsystems.label=เวอร์ชั่นของระบบ setting.about.subsystems.val=เวอร์ชั่นของเครือข่าย: {0}; เวอร์ชั่นข้อความ P2P: {1}; เวอร์ชั่นฐานข้อมูลท้องถิ่น: {2}; เวอร์ชั่นโปรโตคอลการซื้อขาย: {3} setting.about.shortcuts=Short cuts -setting.about.shortcuts.ctrlOrAltOrCmd=''Ctrl + {0}'' or ''alt + {0}'' or ''cmd + {0}'' +setting.about.shortcuts.ctrlOrAltOrCmd='Ctrl + {0}' or 'alt + {0}' or 'cmd + {0}' setting.about.shortcuts.menuNav=Navigate main menu setting.about.shortcuts.menuNav.value=To navigate the main menu press: 'Ctrl' or 'alt' or 'cmd' with a numeric key between '1-9' setting.about.shortcuts.close=Close Haveno -setting.about.shortcuts.close.value=''Ctrl + {0}'' or ''cmd + {0}'' or ''Ctrl + {1}'' or ''cmd + {1}'' +setting.about.shortcuts.close.value='Ctrl + {0}' or 'cmd + {0}' or 'Ctrl + {1}' or 'cmd + {1}' setting.about.shortcuts.closePopup=Close popup or dialog window setting.about.shortcuts.closePopup.value='ESCAPE' key setting.about.shortcuts.chatSendMsg=Send trader chat message -setting.about.shortcuts.chatSendMsg.value=''Ctrl + ENTER'' or ''alt + ENTER'' or ''cmd + ENTER'' +setting.about.shortcuts.chatSendMsg.value='Ctrl + ENTER' or 'alt + ENTER' or 'cmd + ENTER' setting.about.shortcuts.openDispute=Open dispute setting.about.shortcuts.openDispute.value=Select pending trade and click: {0} @@ -1199,7 +1194,7 @@ account.arbitratorRegistration.registerSuccess=You have successfully registered account.arbitratorRegistration.registerFailed=Could not complete registration.{0} account.crypto.yourCryptoAccounts=บัญชี crypto (เหรียญทางเลือก) ของคุณ -account.crypto.popup.wallet.msg=Please be sure that you follow the requirements for the usage of {0} wallets as described on the {1} web page.\nUsing wallets from centralized exchanges where (a) you don''t control your keys or (b) which don''t use compatible wallet software is risky: it can lead to loss of the traded funds!\nThe mediator or arbitrator is not a {2} specialist and cannot help in such cases. +account.crypto.popup.wallet.msg=Please be sure that you follow the requirements for the usage of {0} wallets as described on the {1} web page.\nUsing wallets from centralized exchanges where (a) you don't control your keys or (b) which don't use compatible wallet software is risky: it can lead to loss of the traded funds!\nThe mediator or arbitrator is not a {2} specialist and cannot help in such cases. account.crypto.popup.wallet.confirm=ฉันเข้าใจและยืนยันว่าฉันรู้ว่า wallet ใดที่ฉันต้องการใช้ # suppress inspection "UnusedProperty" account.crypto.popup.upx.msg=Trading UPX on Haveno requires that you understand and fulfill the following requirements:\n\nFor sending UPX, you need to use either the official uPlexa GUI wallet or uPlexa CLI wallet with the store-tx-info flag enabled (default in new versions). Please be sure you can access the tx key as that would be required in case of a dispute.\nuplexa-wallet-cli (use the command get_tx_key)\nuplexa-wallet-gui (go to history tab and click on the (P) button for payment proof)\n\nAt normal block explorers the transfer is not verifiable.\n\nYou need to provide the arbitrator the following data in case of a dispute:\n- The tx private key\n- The transaction hash\n- The recipient's public address\n\nFailure to provide the above data, or if you used an incompatible wallet, will result in losing the dispute case. The UPX sender is responsible for providing verification of the UPX transfer to the arbitrator in case of a dispute.\n\nThere is no payment ID required, just the normal public address.\nIf you are not sure about that process visit uPlexa discord channel (https://discord.gg/vhdNSrV) or the uPlexa Telegram Chat (https://t.me/uplexaOfficial) to find more information. @@ -1631,7 +1626,7 @@ popup.warning.priceRelay=ราคาผลัดเปลี่ยน popup.warning.seed=รหัสลับเพื่อกู้ข้อมูล popup.warning.mandatoryUpdate.trading=Please update to the latest Haveno version. A mandatory update was released which disables trading for old versions. Please check out the Haveno Forum for more information. popup.warning.noFilter=เราไม่ได้รับวัตถุกรองจากโหนดต้นทาง กรุณาแจ้งผู้ดูแลระบบเครือข่ายให้ลงทะเบียนวัตถุกรอง -popup.warning.burnXMR=This transaction is not possible, as the mining fees of {0} would exceed the amount to transfer of {1}. Please wait until the mining fees are low again or until you''ve accumulated more XMR to transfer. +popup.warning.burnXMR=This transaction is not possible, as the mining fees of {0} would exceed the amount to transfer of {1}. Please wait until the mining fees are low again or until you've accumulated more XMR to transfer. popup.warning.openOffer.makerFeeTxRejected=The maker fee transaction for offer with ID {0} was rejected by the Monero network.\nTransaction ID={1}.\nThe offer has been removed to avoid further problems.\nPlease go to \"Settings/Network info\" and do a SPV resync.\nFor further help please contact the Haveno support channel at the Haveno Keybase team. @@ -1680,8 +1675,8 @@ popup.accountSigning.signAccounts.ECKey.error=Bad arbitrator ECKey popup.accountSigning.success.headline=Congratulations popup.accountSigning.success.description=All {0} payment accounts were successfully signed! popup.accountSigning.generalInformation=You'll find the signing state of all your accounts in the account section.\n\nFor further information, please visit [HYPERLINK:https://docs.haveno.exchange/payment-methods#account-signing]. -popup.accountSigning.signedByArbitrator=One of your payment accounts has been verified and signed by an arbitrator. Trading with this account will automatically sign your trading peer''s account after a successful trade.\n\n{0} -popup.accountSigning.signedByPeer=One of your payment accounts has been verified and signed by a trading peer. Your initial trading limit will be lifted and you''ll be able to sign other accounts in {0} days from now.\n\n{1} +popup.accountSigning.signedByArbitrator=One of your payment accounts has been verified and signed by an arbitrator. Trading with this account will automatically sign your trading peer's account after a successful trade.\n\n{0} +popup.accountSigning.signedByPeer=One of your payment accounts has been verified and signed by a trading peer. Your initial trading limit will be lifted and you'll be able to sign other accounts in {0} days from now.\n\n{1} popup.accountSigning.peerLimitLifted=The initial limit for one of your accounts has been lifted.\n\n{0} popup.accountSigning.peerSigner=One of your accounts is mature enough to sign other payment accounts and the initial limit for one of your accounts has been lifted.\n\n{0} @@ -1976,8 +1971,8 @@ payment.accountType=ประเภทบัญชี payment.checking=การตรวจสอบ payment.savings=ออมทรัพย์ payment.personalId=รหัส ID ประจำตัวบุคคล -payment.zelle.info=Zelle is a money transfer service that works best *through* another bank.\n\n1. Check this page to see if (and how) your bank works with Zelle: [HYPERLINK:https://www.zellepay.com/get-started]\n\n2. Take special note of your transfer limits—sending limits vary by bank, and banks often specify separate daily, weekly, and monthly limits.\n\n3. If your bank does not work with Zelle, you can still use it through the Zelle mobile app, but your transfer limits will be much lower.\n\n4. The name specified on your Haveno account MUST match the name on your Zelle/bank account. \n\nIf you cannot complete a Zelle transaction as specified in your trade contract, you may lose some (or all) of your security deposit.\n\nBecause of Zelle''s somewhat higher chargeback risk, sellers are advised to contact unsigned buyers through email or SMS to verify that the buyer really owns the Zelle account specified in Haveno. -payment.fasterPayments.newRequirements.info=Some banks have started verifying the receiver''s full name for Faster Payments transfers. Your current Faster Payments account does not specify a full name.\n\nPlease consider recreating your Faster Payments account in Haveno to provide future {0} buyers with a full name.\n\nWhen you recreate the account, make sure to copy the precise sort code, account number and account age verification salt values from your old account to your new account. This will ensure your existing account''s age and signing status are preserved. +payment.zelle.info=Zelle is a money transfer service that works best *through* another bank.\n\n1. Check this page to see if (and how) your bank works with Zelle: [HYPERLINK:https://www.zellepay.com/get-started]\n\n2. Take special note of your transfer limits—sending limits vary by bank, and banks often specify separate daily, weekly, and monthly limits.\n\n3. If your bank does not work with Zelle, you can still use it through the Zelle mobile app, but your transfer limits will be much lower.\n\n4. The name specified on your Haveno account MUST match the name on your Zelle/bank account. \n\nIf you cannot complete a Zelle transaction as specified in your trade contract, you may lose some (or all) of your security deposit.\n\nBecause of Zelle's somewhat higher chargeback risk, sellers are advised to contact unsigned buyers through email or SMS to verify that the buyer really owns the Zelle account specified in Haveno. +payment.fasterPayments.newRequirements.info=Some banks have started verifying the receiver's full name for Faster Payments transfers. Your current Faster Payments account does not specify a full name.\n\nPlease consider recreating your Faster Payments account in Haveno to provide future {0} buyers with a full name.\n\nWhen you recreate the account, make sure to copy the precise sort code, account number and account age verification salt values from your old account to your new account. This will ensure your existing account's age and signing status are preserved. payment.moneyGram.info=When using MoneyGram the XMR buyer has to send the Authorisation number and a photo of the receipt by email to the XMR seller. The receipt must clearly show the seller's full name, country, state and the amount. The seller's email will be displayed to the buyer during the trade process. payment.westernUnion.info=When using Western Union the XMR buyer has to send the MTCN (tracking number) and a photo of the receipt by email to the XMR seller. The receipt must clearly show the seller's full name, city, country and the amount. The seller's email will be displayed to the buyer during the trade process. payment.halCash.info=เมื่อมีการใช้งาน HalCash ผู้ซื้อ XMR จำเป็นต้องส่งรหัส Halcash ให้กับผู้ขายทางข้อความโทรศัพท์มือถือ\n\nโปรดตรวจสอบว่าไม่เกินจำนวนเงินสูงสุดที่ธนาคารของคุณอนุญาตให้คุณส่งด้วย HalCash จำนวนเงินขั้นต่ำในการเบิกถอนคือ 10 EUR และสูงสุดในจำนวนเงิน 600 EUR สำหรับการถอนซ้ำเป็น 3000 EUR ต่อผู้รับและต่อวัน และ 6000 EUR ต่อผู้รับและต่อเดือน โปรดตรวจสอบข้อจำกัดจากทางธนาคารคุณเพื่อให้มั่นใจได้ว่าทางธนาคารได้มีการใช้มาตรฐานข้อกำหนดเดียวกันกับดังที่ระบุไว้ ณ ที่นี่\n\nจำนวนเงินที่ถอนจะต้องเป็นจำนวนเงินหลาย 10 EUR เนื่องจากคุณไม่สามารถถอนเงินอื่น ๆ ออกจากตู้เอทีเอ็มได้ UI ในหน้าจอสร้างข้อเสนอและรับข้อเสนอจะปรับจำนวนเงิน XMR เพื่อให้จำนวนเงิน EUR ถูกต้อง คุณไม่สามารถใช้ราคาตลาดเป็นจำนวนเงิน EUR ซึ่งจะเปลี่ยนแปลงไปตามราคาที่มีการปรับเปลี่ยน\n\nในกรณีที่มีข้อพิพาทผู้ซื้อ XMR ต้องแสดงหลักฐานว่าได้ส่ง EUR แล้ว @@ -1989,7 +1984,7 @@ payment.limits.info.withSigning=To limit chargeback risk, Haveno sets per-trade payment.cashDeposit.info=โปรดยืนยันว่าธนาคารของคุณได้อนุมัติให้คุณสามารถส่งเงินสดให้กับบัญชีบุคคลอื่นได้ ตัวอย่างเช่น บางธนาคารที่ไม่ได้มีการบริการถ่ายโอนเงินสดอย่าง Bank of America และ Wells Fargo payment.revolut.info=Revolut requires the 'Username' as account ID not the phone number or email as it was the case in the past. -payment.account.revolut.addUserNameInfo={0}\nYour existing Revolut account ({1}) does not have a ''Username''.\nPlease enter your Revolut ''Username'' to update your account data.\nThis will not affect your account age signing status. +payment.account.revolut.addUserNameInfo={0}\nYour existing Revolut account ({1}) does not have a 'Username'.\nPlease enter your Revolut 'Username' to update your account data.\nThis will not affect your account age signing status. payment.revolut.addUserNameInfo.headLine=Update Revolut account payment.cashapp.info=โปรดทราบว่า Cash App มีความเสี่ยงในการเรียกเงินคืนสูงกว่าการโอนเงินผ่านธนาคารส่วนใหญ่ @@ -2023,7 +2018,7 @@ payment.japan.recipient=ชื่อ payment.australia.payid=PayID payment.payid=PayID linked to financial institution. Like email address or mobile phone. payment.payid.info=A PayID like a phone number, email address or an Australian Business Number (ABN), that you can securely link to your bank, credit union or building society account. You need to have already created a PayID with your Australian financial institution. Both sending and receiving financial institutions must support PayID. For more information please check [HYPERLINK:https://payid.com.au/faqs/] -payment.amazonGiftCard.info=To pay with Amazon eGift Card, you will need to send an Amazon eGift Card to the XMR seller via your Amazon account. \n\nHaveno will show the XMR seller''s email address or phone number where the gift card should be sent, and you must include the trade ID in the gift card''s message field. Please see the wiki [HYPERLINK:https://docs.haveno.exchange/the-project/payment_methods/Amazon_eGift_card] for further details and best practices. \n\nThree important notes:\n- try to send gift cards with amounts of 100 USD or smaller, as Amazon is known to flag larger gift cards as fraudulent\n- try to use creative, believable text for the gift card''s message (e.g., "Happy birthday Susan!") along with the trade ID (and use trader chat to tell your trading peer the reference text you picked so they can verify your payment)\n- Amazon eGift Cards can only be redeemed on the Amazon website they were purchased on (e.g., a gift card purchased on amazon.it can only be redeemed on amazon.it) +payment.amazonGiftCard.info=To pay with Amazon eGift Card, you will need to send an Amazon eGift Card to the XMR seller via your Amazon account. \n\nHaveno will show the XMR seller's email address or phone number where the gift card should be sent, and you must include the trade ID in the gift card's message field. Please see the wiki [HYPERLINK:https://docs.haveno.exchange/the-project/payment_methods/Amazon_eGift_card] for further details and best practices. \n\nThree important notes:\n- try to send gift cards with amounts of 100 USD or smaller, as Amazon is known to flag larger gift cards as fraudulent\n- try to use creative, believable text for the gift card's message (e.g., "Happy birthday Susan!") along with the trade ID (and use trader chat to tell your trading peer the reference text you picked so they can verify your payment)\n- Amazon eGift Cards can only be redeemed on the Amazon website they were purchased on (e.g., a gift card purchased on amazon.it can only be redeemed on amazon.it) payment.paysafe.info=เพื่อความปลอดภัยของคุณ เราขอแนะนำอย่างยิ่งให้หลีกเลี่ยงการใช้ Paysafecard PINs ในการชำระเงิน\n\n\ ธุรกรรมที่ดำเนินการผ่าน PIN ไม่สามารถตรวจสอบได้อย่างอิสระสำหรับการระงับข้อพิพาท หากเกิดปัญหา อาจไม่สามารถกู้คืนเงินได้\n\n\ เพื่อความปลอดภัยของธุรกรรมและรองรับการระงับข้อพิพาท โปรดใช้วิธีการชำระเงินที่มีบันทึกการทำธุรกรรมที่ตรวจสอบได้ diff --git a/core/src/main/resources/i18n/displayStrings_tr.properties b/core/src/main/resources/i18n/displayStrings_tr.properties index 9b7f12e466..723e108b86 100644 --- a/core/src/main/resources/i18n/displayStrings_tr.properties +++ b/core/src/main/resources/i18n/displayStrings_tr.properties @@ -4,11 +4,6 @@ # E.g.: [main-view].[component].[description] # In some cases we use enum values or constants to map to display strings -# A annoying issue with property files is that we need to use 2 single quotes in display string -# containing variables (e.g. {0}), otherwise the variable will not be resolved. -# In display string which do not use a variable a single quote is ok. -# E.g. Don''t .... {1} - # We use sometimes dynamic parts which are put together in the code and therefore sometimes use line breaks or spaces # at the end of the string. Please never remove any line breaks or spaces. They are there with a purpose! # To make longer strings with better readable you can make a line break with \ which does not result in a line break @@ -752,8 +747,8 @@ portfolio.pending.step2_seller.f2fInfo.headline=Alıcının iletişim bilgileri portfolio.pending.step2_seller.waitPayment.msg=Yatırım işlemi kilidi açıldı.\nXMR alıcısının {0} ödemesini başlatmasını beklemeniz gerekiyor. portfolio.pending.step2_seller.warn=XMR alıcısı hala {0} ödemesini yapmadı.\nÖdeme başlatılana kadar beklemeniz gerekiyor.\nİşlem {1} tarihinde tamamlanmadıysa, arabulucu durumu inceleyecektir. portfolio.pending.step2_seller.openForDispute=XMR alıcısı ödemesine başlamadı!\nİşlem için izin verilen maksimum süre doldu.\nKarşı tarafa daha fazla zaman tanıyabilir veya arabulucu ile iletişime geçebilirsiniz. -disputeChat.chatWindowTitle=İşlem ID''si ile ilgili uyuşmazlık sohbet penceresi ''{0}'' -tradeChat.chatWindowTitle=İşlem ID''si ile ilgili trader sohbet penceresi ''{0}'' +disputeChat.chatWindowTitle=İşlem ID'si ile ilgili uyuşmazlık sohbet penceresi '{0}' +tradeChat.chatWindowTitle=İşlem ID'si ile ilgili trader sohbet penceresi '{0}' tradeChat.openChat=Sohbet penceresini aç tradeChat.rules=Bu işlemle ilgili olası sorunları çözmek için işlem ortağınızla iletişim kurabilirsiniz.\n\ Sohbette yanıt vermek zorunlu değildir.\n\ @@ -1387,19 +1382,19 @@ setting.about.subsystems.label=Alt sistemlerin sürümleri setting.about.subsystems.val=Ağ sürümü: {0}; P2P mesaj sürümü: {1}; Yerel DB sürümü: {2}; Ticaret protokolü sürümü: {3} setting.about.shortcuts=Kısayollar -setting.about.shortcuts.ctrlOrAltOrCmd=''Ctrl + {0}'' veya ''alt + {0}'' veya ''cmd + {0}'' +setting.about.shortcuts.ctrlOrAltOrCmd='Ctrl + {0}' veya 'alt + {0}' veya 'cmd + {0}' setting.about.shortcuts.menuNav=Ana menüde gezin setting.about.shortcuts.menuNav.value=Ana menüde gezinmek için şuna basın: 'Ctrl' veya 'alt' veya 'cmd' ve '1-9' arasındaki bir sayı tuşu setting.about.shortcuts.close=Haveno'yu kapat -setting.about.shortcuts.close.value=''Ctrl + {0}'' veya ''cmd + {0}'' veya ''Ctrl + {1}'' veya ''cmd + {1}'' +setting.about.shortcuts.close.value='Ctrl + {0}' veya 'cmd + {0}' veya 'Ctrl + {1}' veya 'cmd + {1}' setting.about.shortcuts.closePopup=Açılır pencereyi veya iletişim penceresini kapat setting.about.shortcuts.closePopup.value='ESCAPE' tuşu setting.about.shortcuts.chatSendMsg=Trader sohbet mesajı gönder -setting.about.shortcuts.chatSendMsg.value=''Ctrl + ENTER'' veya ''alt + ENTER'' veya ''cmd + ENTER'' +setting.about.shortcuts.chatSendMsg.value='Ctrl + ENTER' veya 'alt + ENTER' veya 'cmd + ENTER' setting.about.shortcuts.openDispute=Uyuşmazlık aç setting.about.shortcuts.openDispute.value=Bekleyen işlemi seçin ve tıklayın: {0} @@ -1784,7 +1779,7 @@ account.notifications.marketAlert.message.title=Teklif uyarısı account.notifications.marketAlert.message.msg.below=altında account.notifications.marketAlert.message.msg.above=üstünde account.notifications.marketAlert.message.msg=Haveno teklif defterine {2} ({3} {4} piyasa fiyatı) fiyatıyla \ - ve ödeme yöntemi ''{5}'' olan yeni bir ''{0} {1}'' teklifi yayınlandı.\n\ + ve ödeme yöntemi '{5}' olan yeni bir '{0} {1}' teklifi yayınlandı.\n\ Teklif ID: {6}. account.notifications.priceAlert.message.title={0} için fiyat uyarısı account.notifications.priceAlert.message.msg=Fiyat uyarınız tetiklendi. Mevcut {0} fiyatı {1} {2} diff --git a/core/src/main/resources/i18n/displayStrings_vi.properties b/core/src/main/resources/i18n/displayStrings_vi.properties index 52854a4208..6962df5af2 100644 --- a/core/src/main/resources/i18n/displayStrings_vi.properties +++ b/core/src/main/resources/i18n/displayStrings_vi.properties @@ -4,11 +4,6 @@ # E.g.: [main-view].[component].[description] # In some cases we use enum values or constants to map to display strings -# A annoying issue with property files is that we need to use 2 single quotes in display string -# containing variables (e.g. {0}), otherwise the variable will not be resolved. -# In display string which do not use a variable a single quote is ok. -# E.g. Don''t .... {1} - # We use sometimes dynamic parts which are put together in the code and therefore sometimes use line breaks or spaces # at the end of the string. Please never remove any line breaks or spaces. They are there with a purpose! # To make longer strings with better readable you can make a line break with \ which does not result in a line break @@ -112,7 +107,7 @@ shared.belowInPercent=Thấp hơn % so với giá thị trường shared.aboveInPercent=Cao hơn % so với giá thị trường shared.enterPercentageValue=Nhập giá trị % shared.OR=HOẶC -shared.notEnoughFunds=You don''t have enough funds in your Haveno wallet for this transaction—{0} is needed but only {1} is available.\n\nPlease add funds from an external wallet, or fund your Haveno wallet at Funds > Receive Funds. +shared.notEnoughFunds=You don't have enough funds in your Haveno wallet for this transaction—{0} is needed but only {1} is available.\n\nPlease add funds from an external wallet, or fund your Haveno wallet at Funds > Receive Funds. shared.waitingForFunds=Đợi nộp tiền... shared.TheXMRBuyer=Người mua XMR shared.You=Bạn @@ -359,7 +354,7 @@ offerbook.xmrAutoConf=Is auto-confirm enabled offerbook.buyXmrWith=Mua XMR với: offerbook.sellXmrFor=Bán XMR để: -offerbook.timeSinceSigning.help=When you successfully complete a trade with a peer who has a signed payment account, your payment account is signed.\n{0} days later, the initial limit of {1} is lifted and your account can sign other peers'' payment accounts. +offerbook.timeSinceSigning.help=When you successfully complete a trade with a peer who has a signed payment account, your payment account is signed.\n{0} days later, the initial limit of {1} is lifted and your account can sign other peers' payment accounts. offerbook.timeSinceSigning.notSigned=Not signed yet offerbook.timeSinceSigning.notSigned.ageDays={0} ngày offerbook.timeSinceSigning.notSigned.noNeed=Không áp dụng @@ -399,7 +394,7 @@ offerbook.warning.counterpartyTradeRestrictions=This offer cannot be taken due t offerbook.warning.newVersionAnnouncement=With this version of the software, trading peers can verify and sign each others' payment accounts to create a network of trusted payment accounts.\n\nAfter successfully trading with a peer with a verified payment account, your payment account will be signed and trading limits will be lifted after a certain time interval (length of this interval is based on the verification method).\n\nFor more information on account signing, please see the documentation at [HYPERLINK:https://docs.haveno.exchange/payment-methods#account-signing]. -popup.warning.tradeLimitDueAccountAgeRestriction.seller=The allowed trade amount is limited to {0} because of security restrictions based on the following criteria:\n- The buyer''s account has not been signed by an arbitrator or a peer\n- The time since signing of the buyer''s account is not at least 30 days\n- The payment method for this offer is considered risky for bank chargebacks\n\n{1} +popup.warning.tradeLimitDueAccountAgeRestriction.seller=The allowed trade amount is limited to {0} because of security restrictions based on the following criteria:\n- The buyer's account has not been signed by an arbitrator or a peer\n- The time since signing of the buyer's account is not at least 30 days\n- The payment method for this offer is considered risky for bank chargebacks\n\n{1} popup.warning.tradeLimitDueAccountAgeRestriction.buyer=The allowed trade amount is limited to {0} because of security restrictions based on the following criteria:\n- Your account has not been signed by an arbitrator or a peer\n- The time since signing of your account is not at least 30 days\n- The payment method for this offer is considered risky for bank chargebacks\n\n{1} popup.warning.tradeLimitDueAccountAgeRestriction.seller.releaseLimit=Phương thức thanh toán này tạm thời chỉ được giới hạn đến {0} cho đến {1} vì tất cả các người mua đều có tài khoản mới.\n\n{2} popup.warning.tradeLimitDueAccountAgeRestriction.seller.exceedsUnsignedBuyLimit=Đề nghị của bạn sẽ bị giới hạn chỉ đối với các người mua có tài khoản đã ký và có tuổi vì nó vượt quá {0}.\n\n{1} @@ -643,7 +638,7 @@ portfolio.pending.step2_buyer.postal=Hãy gửi {0} bằng \"Phiếu chuyển ti # suppress inspection "TrailingSpacesInProperty" portfolio.pending.step2_buyer.payByMail=Please send {0} using \"Pay by Mail\" to the XMR seller. Specific instructions are in the trade contract, or if unclear you may ask questions via trader chat. See more details about Pay by Mail on the Haveno wiki [HYPERLINK:https://docs.haveno.exchange/the-project/payment_methods/Pay_By_Mail].\n\n # suppress inspection "TrailingSpacesInProperty" -portfolio.pending.step2_buyer.pay=Please pay {0} via the specified payment method to the XMR seller. You''ll find the seller's account details on the next screen.\n\n +portfolio.pending.step2_buyer.pay=Please pay {0} via the specified payment method to the XMR seller. You'll find the seller's account details on the next screen.\n\n # suppress inspection "TrailingSpacesInProperty" portfolio.pending.step2_buyer.f2f=Vui lòng liên hệ người bán XMR và cung cấp số liên hệ và sắp xếp cuộc hẹn để thanh toán {0}.\n\n portfolio.pending.step2_buyer.startPaymentUsing=Thanh toán bắt đầu sử dụng {0} @@ -675,7 +670,7 @@ portfolio.pending.step2_seller.f2fInfo.headline=Thông tin liên lạc của ng portfolio.pending.step2_seller.waitPayment.msg=Giao dịch đặt cọc có ít nhất một xác nhận blockchain.\nBạn cần phải đợi cho đến khi người mua XMR bắt đầu thanh toán {0}. portfolio.pending.step2_seller.warn=Người mua XMR vẫn chưa thanh toán {0}.\nBạn cần phải đợi cho đến khi người mua bắt đầu thanh toán.\nNếu giao dịch không được hoàn thành vào {1} trọng tài sẽ điều tra. portfolio.pending.step2_seller.openForDispute=The XMR buyer has not started their payment!\nThe max. allowed period for the trade has elapsed.\nYou can wait longer and give the trading peer more time or contact the mediator for assistance. -tradeChat.chatWindowTitle=Chat window for trade with ID ''{0}'' +tradeChat.chatWindowTitle=Chat window for trade with ID '{0}' tradeChat.openChat=Open chat window tradeChat.rules=You can communicate with your trade peer to resolve potential problems with this trade.\nIt is not mandatory to reply in the chat.\nIf a trader violates any of the rules below, open a dispute and report it to the mediator or arbitrator.\n\nChat rules:\n\t● Do not send any links (risk of malware). You can send the transaction ID and the name of a block explorer.\n\t● Do not send your seed words, private keys, passwords or other sensitive information!\n\t● Do not encourage trading outside of Haveno (no security).\n\t● Do not engage in any form of social engineering scam attempts.\n\t● If a peer is not responding and prefers to not communicate via chat, respect their decision.\n\t● Keep conversation scope limited to the trade. This chat is not a messenger replacement or troll-box.\n\t● Keep conversation friendly and respectful. @@ -715,7 +710,7 @@ portfolio.pending.step3_seller.westernUnion=Người mua phải gửi cho bạn portfolio.pending.step3_seller.halCash=Người mua phải gửi mã HalCash cho bạn bằng tin nhắn. Ngoài ra, bạn sẽ nhận được một tin nhắn từ HalCash với thông tin cần thiết để rút EUR từ một máy ATM có hỗ trợ HalCash. \n\nSau khi nhận được tiền từ ATM vui lòng xác nhận lại biên lai thanh toán tại đây! portfolio.pending.step3_seller.amazonGiftCard=The buyer has sent you an Amazon eGift Card by email or by text message to your mobile phone. Please redeem now the Amazon eGift Card at your Amazon account and once accepted confirm the payment receipt. -portfolio.pending.step3_seller.bankCheck=\n\nPlease also verify that the name of the sender specified on the trade contract matches the name that appears on your bank statement:\nSender''s name, per trade contract: {0}\n\nIf the names are not exactly the same, {1} +portfolio.pending.step3_seller.bankCheck=\n\nPlease also verify that the name of the sender specified on the trade contract matches the name that appears on your bank statement:\nSender's name, per trade contract: {0}\n\nIf the names are not exactly the same, {1} # suppress inspection "TrailingSpacesInProperty" portfolio.pending.step3_seller.openDispute=don't confirm payment receipt. Instead, open a dispute by pressing \"alt + o\" or \"option + o\".\n\n portfolio.pending.step3_seller.confirmPaymentReceipt=Xác nhận đã nhận được thanh toán @@ -737,7 +732,7 @@ portfolio.pending.step3_seller.openForDispute=You have not confirmed the receipt # suppress inspection "TrailingSpacesInProperty" portfolio.pending.step3_seller.onPaymentReceived.part1=Bạn đã nhận được thanh toán {0} từ Đối tác giao dịch của bạn?\n\n # suppress inspection "TrailingSpacesInProperty" -portfolio.pending.step3_seller.onPaymentReceived.name=Please also verify that the name of the sender specified on the trade contract matches the name that appears on your bank statement:\nSender''s name, per trade contract: {0}\n\nIf the names are not exactly the same, don''t confirm payment receipt. Instead, open a dispute by pressing \"alt + o\" or \"option + o\".\n\n +portfolio.pending.step3_seller.onPaymentReceived.name=Please also verify that the name of the sender specified on the trade contract matches the name that appears on your bank statement:\nSender's name, per trade contract: {0}\n\nIf the names are not exactly the same, don't confirm payment receipt. Instead, open a dispute by pressing \"alt + o\" or \"option + o\".\n\n # suppress inspection "TrailingSpacesInProperty" portfolio.pending.step3_seller.onPaymentReceived.note=Please note, that as soon you have confirmed the receipt, the locked trade amount will be released to the XMR buyer and the security deposit will be refunded.\n\n portfolio.pending.step3_seller.onPaymentReceived.confirm.headline=Xác nhận rằng bạn đã nhận được thanh toán @@ -811,9 +806,9 @@ portfolio.pending.mediationResult.info.selfAccepted=You have accepted the mediat portfolio.pending.mediationResult.info.peerAccepted=Your trade peer has accepted the mediator's suggestion. Do you accept as well? portfolio.pending.mediationResult.button=View proposed resolution portfolio.pending.mediationResult.popup.headline=Mediation result for trade with ID: {0} -portfolio.pending.mediationResult.popup.headline.peerAccepted=Your trade peer has accepted the mediator''s suggestion for trade {0} -portfolio.pending.mediationResult.popup.info=The mediator has suggested the following payout:\nYou receive: {0}\nYour trading peer receives: {1}\n\nYou can accept or reject this suggested payout.\n\nBy accepting, you sign the proposed payout transaction. If your trading peer also accepts and signs, the payout will be completed, and the trade will be closed.\n\nIf one or both of you reject the suggestion, you will have to wait until {2} (block {3}) to open a second-round dispute with an arbitrator who will investigate the case again and do a payout based on their findings.\n\nThe arbitrator may charge a small fee (fee maximum: the trader''s security deposit) as compensation for their work. Both traders agreeing to the mediator''s suggestion is the happy path—requesting arbitration is meant for exceptional circumstances, such as if a trader is sure the mediator did not make a fair payout suggestion (or if the other peer is unresponsive).\n\nMore details about the new arbitration model: [HYPERLINK:https://docs.haveno.exchange/trading-rules.html#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\nOnce 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\nYou can find more details about the arbitration model at:[HYPERLINK:https://docs.haveno.exchange/trading-rules.html#arbitration] +portfolio.pending.mediationResult.popup.headline.peerAccepted=Your trade peer has accepted the mediator's suggestion for trade {0} +portfolio.pending.mediationResult.popup.info=The mediator has suggested the following payout:\nYou receive: {0}\nYour trading peer receives: {1}\n\nYou can accept or reject this suggested payout.\n\nBy accepting, you sign the proposed payout transaction. If your trading peer also accepts and signs, the payout will be completed, and the trade will be closed.\n\nIf one or both of you reject the suggestion, you will have to wait until {2} (block {3}) to open a second-round dispute with an arbitrator who will investigate the case again and do a payout based on their findings.\n\nThe arbitrator may charge a small fee (fee maximum: the trader's security deposit) as compensation for their work. Both traders agreeing to the mediator's suggestion is the happy path—requesting arbitration is meant for exceptional circumstances, such as if a trader is sure the mediator did not make a fair payout suggestion (or if the other peer is unresponsive).\n\nMore details about the new arbitration model: [HYPERLINK:https://docs.haveno.exchange/trading-rules.html#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\nOnce 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\nYou can find more details about the arbitration model at:[HYPERLINK:https://docs.haveno.exchange/trading-rules.html#arbitration] portfolio.pending.mediationResult.popup.openArbitration=Reject and request arbitration portfolio.pending.mediationResult.popup.alreadyAccepted=You've already accepted @@ -979,7 +974,7 @@ support.sellerMaker=Người bán XMR/Người tạo support.buyerTaker=Người mua XMR/Người nhận support.sellerTaker=Người bán XMR/Người nhận -support.initialInfo=Please enter a description of your problem in the text field below. Add as much information as possible to speed up dispute resolution time.\n\nHere is a check list for information you should provide:\n\t● If you are the XMR buyer: Did you make the Fiat or Crypto transfer? If so, did you click the 'payment started' button in the application?\n\t● If you are the XMR seller: Did you receive the Fiat or Crypto payment? If so, did you click the 'payment received' button in the application?\n\t● Which version of Haveno are you using?\n\t● Which operating system are you using?\n\t● If you encountered an issue with failed transactions please consider switching to a new data directory.\n\t Sometimes the data directory gets corrupted and leads to strange bugs. \n\t See: https://docs.haveno.exchange/backup-recovery.html#switch-to-a-new-data-directory\n\nPlease 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● Mediators respond in between 2 days. Arbitrators respond in between 5 business days.\n\t● The maximum period for a dispute is 14 days.\n\t● You need to cooperate with the {1} 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\nYou can read more about the dispute process at: {2} +support.initialInfo=Please enter a description of your problem in the text field below. Add as much information as possible to speed up dispute resolution time.\n\nHere is a check list for information you should provide:\n\t● If you are the XMR buyer: Did you make the Fiat or Crypto transfer? If so, did you click the 'payment started' button in the application?\n\t● If you are the XMR seller: Did you receive the Fiat or Crypto payment? If so, did you click the 'payment received' button in the application?\n\t● Which version of Haveno are you using?\n\t● Which operating system are you using?\n\t● If you encountered an issue with failed transactions please consider switching to a new data directory.\n\t Sometimes the data directory gets corrupted and leads to strange bugs. \n\t See: https://docs.haveno.exchange/backup-recovery.html#switch-to-a-new-data-directory\n\nPlease 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● Mediators respond in between 2 days. Arbitrators respond in between 5 business days.\n\t● The maximum period for a dispute is 14 days.\n\t● You need to cooperate with the {1} 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\nYou can read more about the dispute process at: {2} support.systemMsg=Tin nhắn hệ thống: {0} support.youOpenedTicket=Bạn đã mở yêu cầu hỗ trợ.\n\n{0}\n\nPhiên bản Haveno: {1} support.youOpenedDispute=Bạn đã mở yêu cầu giải quyết tranh chấp.\n\n{0}\n\nPhiên bản Haveno: {1} @@ -987,8 +982,8 @@ support.youOpenedDisputeForMediation=You requested mediation.\n\n{0}\n\nHaveno v support.peerOpenedTicket=Your trading peer has requested support due to technical problems.\n\n{0}\n\nHaveno version: {1} support.peerOpenedDispute=Your trading peer has requested a dispute.\n\n{0}\n\nHaveno version: {1} support.peerOpenedDisputeForMediation=Your trading peer has requested mediation.\n\n{0}\n\nHaveno version: {1} -support.mediatorsDisputeSummary=System message: Mediator''s dispute summary:\n{0} -support.mediatorsAddress=Mediator''s node address: {0} +support.mediatorsDisputeSummary=System message: Mediator's dispute summary:\n{0} +support.mediatorsAddress=Mediator's node address: {0} support.warning.disputesWithInvalidDonationAddress=The delayed payout transaction has used an invalid receiver address. It does not match any of the DAO parameter values for the valid donation addresses.\n\nThis might be a scam attempt. Please inform the developers about that incident and do not close that case before the situation is resolved!\n\nAddress used in the dispute: {0}\n\nAll DAO param donation addresses: {1}\n\nTrade ID: {2}{3} support.warning.disputesWithInvalidDonationAddress.mediator=\n\nDo you still want to close the dispute? support.warning.disputesWithInvalidDonationAddress.refundAgent=\n\nYou must not do the payout. @@ -1114,19 +1109,19 @@ setting.about.subsystems.label=Các phiên bản của hệ thống con setting.about.subsystems.val=Phiên bản mạng: {0}; Phiên bản tin nhắn P2P: {1}; Phiên bản DB nội bộ: {2}; Phiên bản giao thức giao dịch: {3} setting.about.shortcuts=Short cuts -setting.about.shortcuts.ctrlOrAltOrCmd=''Ctrl + {0}'' or ''alt + {0}'' or ''cmd + {0}'' +setting.about.shortcuts.ctrlOrAltOrCmd='Ctrl + {0}' or 'alt + {0}' or 'cmd + {0}' setting.about.shortcuts.menuNav=Navigate main menu setting.about.shortcuts.menuNav.value=To navigate the main menu press: 'Ctrl' or 'alt' or 'cmd' with a numeric key between '1-9' setting.about.shortcuts.close=Close Haveno -setting.about.shortcuts.close.value=''Ctrl + {0}'' or ''cmd + {0}'' or ''Ctrl + {1}'' or ''cmd + {1}'' +setting.about.shortcuts.close.value='Ctrl + {0}' or 'cmd + {0}' or 'Ctrl + {1}' or 'cmd + {1}' setting.about.shortcuts.closePopup=Close popup or dialog window setting.about.shortcuts.closePopup.value='ESCAPE' key setting.about.shortcuts.chatSendMsg=Send trader chat message -setting.about.shortcuts.chatSendMsg.value=''Ctrl + ENTER'' or ''alt + ENTER'' or ''cmd + ENTER'' +setting.about.shortcuts.chatSendMsg.value='Ctrl + ENTER' or 'alt + ENTER' or 'cmd + ENTER' setting.about.shortcuts.openDispute=Open dispute setting.about.shortcuts.openDispute.value=Select pending trade and click: {0} @@ -1201,7 +1196,7 @@ account.arbitratorRegistration.registerSuccess=You have successfully registered account.arbitratorRegistration.registerFailed=Could not complete registration.{0} account.crypto.yourCryptoAccounts=Tài khoản crypto của bạn -account.crypto.popup.wallet.msg=Please be sure that you follow the requirements for the usage of {0} wallets as described on the {1} web page.\nUsing wallets from centralized exchanges where (a) you don''t control your keys or (b) which don''t use compatible wallet software is risky: it can lead to loss of the traded funds!\nThe mediator or arbitrator is not a {2} specialist and cannot help in such cases. +account.crypto.popup.wallet.msg=Please be sure that you follow the requirements for the usage of {0} wallets as described on the {1} web page.\nUsing wallets from centralized exchanges where (a) you don't control your keys or (b) which don't use compatible wallet software is risky: it can lead to loss of the traded funds!\nThe mediator or arbitrator is not a {2} specialist and cannot help in such cases. account.crypto.popup.wallet.confirm=Tôi hiểu và xác nhận rằng tôi đã biết loại ví mình cần sử dụng. # suppress inspection "UnusedProperty" account.crypto.popup.upx.msg=Trading UPX on Haveno requires that you understand and fulfill the following requirements:\n\nFor sending UPX, you need to use either the official uPlexa GUI wallet or uPlexa CLI wallet with the store-tx-info flag enabled (default in new versions). Please be sure you can access the tx key as that would be required in case of a dispute.\nuplexa-wallet-cli (use the command get_tx_key)\nuplexa-wallet-gui (go to history tab and click on the (P) button for payment proof)\n\nAt normal block explorers the transfer is not verifiable.\n\nYou need to provide the arbitrator the following data in case of a dispute:\n- The tx private key\n- The transaction hash\n- The recipient's public address\n\nFailure to provide the above data, or if you used an incompatible wallet, will result in losing the dispute case. The UPX sender is responsible for providing verification of the UPX transfer to the arbitrator in case of a dispute.\n\nThere is no payment ID required, just the normal public address.\nIf you are not sure about that process visit uPlexa discord channel (https://discord.gg/vhdNSrV) or the uPlexa Telegram Chat (https://t.me/uplexaOfficial) to find more information. @@ -1317,7 +1312,7 @@ account.notifications.marketAlert.manageAlerts.header.offerType=Loại chào gi account.notifications.marketAlert.message.title=Thông báo chào giá account.notifications.marketAlert.message.msg.below=cao hơn account.notifications.marketAlert.message.msg.above=thấp hơn -account.notifications.marketAlert.message.msg=một ''{0} {1}'' chào giá mới với giá {2} ({3} {4} giá thị trường) và hình thức thanh toán ''{5}''đã được đăng lên danh mục chào giá của Haveno.\nMã chào giá: {6}. +account.notifications.marketAlert.message.msg=một '{0} {1}' chào giá mới với giá {2} ({3} {4} giá thị trường) và hình thức thanh toán '{5}'đã được đăng lên danh mục chào giá của Haveno.\nMã chào giá: {6}. account.notifications.priceAlert.message.title=Thông báo giá cho {0} account.notifications.priceAlert.message.msg=Thông báo giá của bạn đã được kích hoạt. Giá {0} hiện tại là {1} {2} account.notifications.noWebCamFound.warning=Không tìm thấy webcam.\n\nVui lòng sử dụng lựa chọn email để gửi mã bảo mật và khóa mã hóa từ điện thoại di động của bạn tới ứng dùng Haveno. @@ -1682,8 +1677,8 @@ popup.accountSigning.signAccounts.ECKey.error=Bad arbitrator ECKey popup.accountSigning.success.headline=Congratulations popup.accountSigning.success.description=All {0} payment accounts were successfully signed! popup.accountSigning.generalInformation=You'll find the signing state of all your accounts in the account section.\n\nFor further information, please visit [HYPERLINK:https://docs.haveno.exchange/payment-methods#account-signing]. -popup.accountSigning.signedByArbitrator=One of your payment accounts has been verified and signed by an arbitrator. Trading with this account will automatically sign your trading peer''s account after a successful trade.\n\n{0} -popup.accountSigning.signedByPeer=One of your payment accounts has been verified and signed by a trading peer. Your initial trading limit will be lifted and you''ll be able to sign other accounts in {0} days from now.\n\n{1} +popup.accountSigning.signedByArbitrator=One of your payment accounts has been verified and signed by an arbitrator. Trading with this account will automatically sign your trading peer's account after a successful trade.\n\n{0} +popup.accountSigning.signedByPeer=One of your payment accounts has been verified and signed by a trading peer. Your initial trading limit will be lifted and you'll be able to sign other accounts in {0} days from now.\n\n{1} popup.accountSigning.peerLimitLifted=The initial limit for one of your accounts has been lifted.\n\n{0} popup.accountSigning.peerSigner=One of your accounts is mature enough to sign other payment accounts and the initial limit for one of your accounts has been lifted.\n\n{0} @@ -1978,8 +1973,8 @@ payment.accountType=Loại tài khoản payment.checking=Đang kiểm tra payment.savings=Tiết kiệm payment.personalId=ID cá nhân -payment.zelle.info=Zelle is a money transfer service that works best *through* another bank.\n\n1. Check this page to see if (and how) your bank works with Zelle: [HYPERLINK:https://www.zellepay.com/get-started]\n\n2. Take special note of your transfer limits—sending limits vary by bank, and banks often specify separate daily, weekly, and monthly limits.\n\n3. If your bank does not work with Zelle, you can still use it through the Zelle mobile app, but your transfer limits will be much lower.\n\n4. The name specified on your Haveno account MUST match the name on your Zelle/bank account. \n\nIf you cannot complete a Zelle transaction as specified in your trade contract, you may lose some (or all) of your security deposit.\n\nBecause of Zelle''s somewhat higher chargeback risk, sellers are advised to contact unsigned buyers through email or SMS to verify that the buyer really owns the Zelle account specified in Haveno. -payment.fasterPayments.newRequirements.info=Some banks have started verifying the receiver''s full name for Faster Payments transfers. Your current Faster Payments account does not specify a full name.\n\nPlease consider recreating your Faster Payments account in Haveno to provide future {0} buyers with a full name.\n\nWhen you recreate the account, make sure to copy the precise sort code, account number and account age verification salt values from your old account to your new account. This will ensure your existing account''s age and signing status are preserved. +payment.zelle.info=Zelle is a money transfer service that works best *through* another bank.\n\n1. Check this page to see if (and how) your bank works with Zelle: [HYPERLINK:https://www.zellepay.com/get-started]\n\n2. Take special note of your transfer limits—sending limits vary by bank, and banks often specify separate daily, weekly, and monthly limits.\n\n3. If your bank does not work with Zelle, you can still use it through the Zelle mobile app, but your transfer limits will be much lower.\n\n4. The name specified on your Haveno account MUST match the name on your Zelle/bank account. \n\nIf you cannot complete a Zelle transaction as specified in your trade contract, you may lose some (or all) of your security deposit.\n\nBecause of Zelle's somewhat higher chargeback risk, sellers are advised to contact unsigned buyers through email or SMS to verify that the buyer really owns the Zelle account specified in Haveno. +payment.fasterPayments.newRequirements.info=Some banks have started verifying the receiver's full name for Faster Payments transfers. Your current Faster Payments account does not specify a full name.\n\nPlease consider recreating your Faster Payments account in Haveno to provide future {0} buyers with a full name.\n\nWhen you recreate the account, make sure to copy the precise sort code, account number and account age verification salt values from your old account to your new account. This will ensure your existing account's age and signing status are preserved. payment.moneyGram.info=When using MoneyGram the XMR buyer has to send the Authorisation number and a photo of the receipt by email to the XMR seller. The receipt must clearly show the seller's full name, country, state and the amount. The seller's email will be displayed to the buyer during the trade process. payment.westernUnion.info=When using Western Union the XMR buyer has to send the MTCN (tracking number) and a photo of the receipt by email to the XMR seller. The receipt must clearly show the seller's full name, city, country and the amount. The seller's email will be displayed to the buyer during the trade process. payment.halCash.info=Khi sử dụng HalCash người mua XMR cần phải gửi cho người bán XMR mã HalCash bằng tin nhắn điện thoại.\n\nVui lòng đảm bảo là lượng tiền này không vượt quá số lượng tối đa mà ngân hàng của bạn cho phép gửi khi dùng HalCash. Số lượng rút tối thiểu là 10 EUR và tối đa là 600 EUR. Nếu rút nhiều lần thì giới hạn sẽ là 3000 EUR/ người nhận/ ngày và 6000 EUR/người nhận/tháng. Vui lòng kiểm tra chéo những giới hạn này với ngân hàng của bạn để chắc chắn là họ cũng dùng những giới hạn như ghi ở đây.\n\nSố tiền rút phải là bội số của 10 EUR vì bạn không thể rút các mệnh giá khác từ ATM. Giao diện người dùng ở phần 'tạo chào giá' và 'chấp nhận chào giá' sẽ điều chỉnh lượng btc sao cho lượng EUR tương ứng sẽ chính xác. Bạn không thể dùng giá thị trường vì lượng EUR có thể sẽ thay đổi khi giá thay đổi.\n\nTrường hợp tranh chấp, người mua XMR cần phải cung cấp bằng chứng chứng minh mình đã gửi EUR. @@ -1991,7 +1986,7 @@ payment.limits.info.withSigning=To limit chargeback risk, Haveno sets per-trade payment.cashDeposit.info=Vui lòng xác nhận rằng ngân hàng của bạn cho phép nạp tiền mặt vào tài khoản của người khác. Chẳng hạn, Ngân Hàng Mỹ và Wells Fargo không còn cho phép nạp tiền như vậy nữa. payment.revolut.info=Revolut requires the 'Username' as account ID not the phone number or email as it was the case in the past. -payment.account.revolut.addUserNameInfo={0}\nYour existing Revolut account ({1}) does not have a ''Username''.\nPlease enter your Revolut ''Username'' to update your account data.\nThis will not affect your account age signing status. +payment.account.revolut.addUserNameInfo={0}\nYour existing Revolut account ({1}) does not have a 'Username'.\nPlease enter your Revolut 'Username' to update your account data.\nThis will not affect your account age signing status. payment.revolut.addUserNameInfo.headLine=Update Revolut account payment.cashapp.info=Vui lòng lưu ý rằng Cash App có rủi ro bồi hoàn cao hơn so với hầu hết các chuyển khoản ngân hàng. @@ -2025,7 +2020,7 @@ payment.japan.recipient=Tên payment.australia.payid=PayID payment.payid=PayID linked to financial institution. Like email address or mobile phone. payment.payid.info=A PayID like a phone number, email address or an Australian Business Number (ABN), that you can securely link to your bank, credit union or building society account. You need to have already created a PayID with your Australian financial institution. Both sending and receiving financial institutions must support PayID. For more information please check [HYPERLINK:https://payid.com.au/faqs/] -payment.amazonGiftCard.info=To pay with Amazon eGift Card, you will need to send an Amazon eGift Card to the XMR seller via your Amazon account. \n\nHaveno will show the XMR seller''s email address or phone number where the gift card should be sent, and you must include the trade ID in the gift card''s message field. Please see the wiki [HYPERLINK:https://docs.haveno.exchange/the-project/payment_methods/Amazon_eGift_card] for further details and best practices. \n\nThree important notes:\n- try to send gift cards with amounts of 100 USD or smaller, as Amazon is known to flag larger gift cards as fraudulent\n- try to use creative, believable text for the gift card''s message (e.g., "Happy birthday Susan!") along with the trade ID (and use trader chat to tell your trading peer the reference text you picked so they can verify your payment)\n- Amazon eGift Cards can only be redeemed on the Amazon website they were purchased on (e.g., a gift card purchased on amazon.it can only be redeemed on amazon.it) +payment.amazonGiftCard.info=To pay with Amazon eGift Card, you will need to send an Amazon eGift Card to the XMR seller via your Amazon account. \n\nHaveno will show the XMR seller's email address or phone number where the gift card should be sent, and you must include the trade ID in the gift card's message field. Please see the wiki [HYPERLINK:https://docs.haveno.exchange/the-project/payment_methods/Amazon_eGift_card] for further details and best practices. \n\nThree important notes:\n- try to send gift cards with amounts of 100 USD or smaller, as Amazon is known to flag larger gift cards as fraudulent\n- try to use creative, believable text for the gift card's message (e.g., "Happy birthday Susan!") along with the trade ID (and use trader chat to tell your trading peer the reference text you picked so they can verify your payment)\n- Amazon eGift Cards can only be redeemed on the Amazon website they were purchased on (e.g., a gift card purchased on amazon.it can only be redeemed on amazon.it) payment.paysafe.info=Vì sự bảo vệ của bạn, chúng tôi khuyến cáo không nên sử dụng mã PIN Paysafecard để thanh toán.\n\n\ Các giao dịch được thực hiện bằng mã PIN không thể được xác minh độc lập để giải quyết tranh chấp. Nếu có vấn đề xảy ra, có thể không thể khôi phục số tiền đã mất.\n\n\ Để đảm bảo an toàn giao dịch và có thể giải quyết tranh chấp, hãy luôn sử dụng các phương thức thanh toán có hồ sơ xác minh được. diff --git a/core/src/main/resources/i18n/displayStrings_zh-hans.properties b/core/src/main/resources/i18n/displayStrings_zh-hans.properties index 7e6b9be4d5..daf103a968 100644 --- a/core/src/main/resources/i18n/displayStrings_zh-hans.properties +++ b/core/src/main/resources/i18n/displayStrings_zh-hans.properties @@ -4,11 +4,6 @@ # E.g.: [main-view].[component].[description] # In some cases we use enum values or constants to map to display strings -# A annoying issue with property files is that we need to use 2 single quotes in display string -# containing variables (e.g. {0}), otherwise the variable will not be resolved. -# In display string which do not use a variable a single quote is ok. -# E.g. Don''t .... {1} - # We use sometimes dynamic parts which are put together in the code and therefore sometimes use line breaks or spaces # at the end of the string. Please never remove any line breaks or spaces. They are there with a purpose! # To make longer strings with better readable you can make a line break with \ which does not result in a line break @@ -644,7 +639,7 @@ portfolio.pending.step2_buyer.postal=请用“美国邮政汇票”发送 {0} # suppress inspection "TrailingSpacesInProperty" portfolio.pending.step2_buyer.payByMail=Please send {0} using \"Pay by Mail\" to the XMR seller. Specific instructions are in the trade contract, or if unclear you may ask questions via trader chat. See more details about Pay by Mail on the Haveno wiki [HYPERLINK:https://docs.haveno.exchange/the-project/payment_methods/Pay_By_Mail].\n\n # suppress inspection "TrailingSpacesInProperty" -portfolio.pending.step2_buyer.pay=Please pay {0} via the specified payment method to the XMR seller. You''ll find the seller's account details on the next screen.\n\n +portfolio.pending.step2_buyer.pay=Please pay {0} via the specified payment method to the XMR seller. You'll find the seller's account details on the next screen.\n\n # suppress inspection "TrailingSpacesInProperty" portfolio.pending.step2_buyer.f2f=请通过提供的联系人与 XMR 卖家联系,并安排会议支付 {0}。\n\n portfolio.pending.step2_buyer.startPaymentUsing=使用 {0} 开始付款 @@ -1160,7 +1155,7 @@ setting.about.shortcuts.sendPrivateNotification=发送私人通知到对等点 setting.about.shortcuts.sendPrivateNotification.value=点击交易伙伴头像并按下:{0} 以显示更多信息 setting.info.headline=新 XMR 自动确认功能 -setting.info.msg=当你完成 XMR/XMR 交易时,您可以使用自动确认功能来验证是否向您的钱包中发送了正确数量的 XMR,以便 Haveno 可以自动将交易标记为完成,从而使每个人都可以更快地进行交易。\n\n自动确认使用 XMR 发送方提供的交易密钥在至少 2 个 XMR 区块浏览器节点上检查 XMR 交易。在默认情况下,Haveno 使用由 Haveno 贡献者运行的区块浏览器节点,但是我们建议运行您自己的 XMR 区块浏览器节点以最大程度地保护隐私和安全。\n\n您还可以在``设置''中将每笔交易的最大 XMR 数量设置为自动确认以及所需确认的数量。\n\n在 Haveno Wiki 上查看更多详细信息(包括如何设置自己的区块浏览器节点):https://haveno.exchange/wiki/Trading_Monero#Auto-confirming_trades +setting.info.msg=当你完成 XMR/XMR 交易时,您可以使用自动确认功能来验证是否向您的钱包中发送了正确数量的 XMR,以便 Haveno 可以自动将交易标记为完成,从而使每个人都可以更快地进行交易。\n\n自动确认使用 XMR 发送方提供的交易密钥在至少 2 个 XMR 区块浏览器节点上检查 XMR 交易。在默认情况下,Haveno 使用由 Haveno 贡献者运行的区块浏览器节点,但是我们建议运行您自己的 XMR 区块浏览器节点以最大程度地保护隐私和安全。\n\n您还可以在``设置'中将每笔交易的最大 XMR 数量设置为自动确认以及所需确认的数量。\n\n在 Haveno Wiki 上查看更多详细信息(包括如何设置自己的区块浏览器节点):https://haveno.exchange/wiki/Trading_Monero#Auto-confirming_trades #################################################################### # Account #################################################################### @@ -1603,7 +1598,7 @@ error.closedTradeWithUnconfirmedDepositTx=交易 ID 为 {0} 的已关闭交易 error.closedTradeWithNoDepositTx=交易 ID 为 {0} 的保证金交易已被确认。\n\n请重新启动应用程序来清理已关闭的交易列表。 popup.warning.walletNotInitialized=钱包至今未初始化 -popup.warning.osxKeyLoggerWarning=由于 MacOS 10.14 及更高版本中的安全措施更加严格,因此启动 Java 应用程序(Haveno 使用Java)会在 MacOS 中引发弹出警告(``Haveno 希望从任何应用程序接收击键'').\n\n为了避免该问题,请打开“ MacOS 设置”,然后转到“安全和隐私”->“隐私”->“输入监视”,然后从右侧列表中删除“ Haveno”。\n\n一旦解决了技术限制(所需的 Java 版本的 Java 打包程序尚未交付),Haveno将升级到新的 Java 版本,以避免该问题。 +popup.warning.osxKeyLoggerWarning=由于 MacOS 10.14 及更高版本中的安全措施更加严格,因此启动 Java 应用程序(Haveno 使用Java)会在 MacOS 中引发弹出警告(``Haveno 希望从任何应用程序接收击键').\n\n为了避免该问题,请打开“ MacOS 设置”,然后转到“安全和隐私”->“隐私”->“输入监视”,然后从右侧列表中删除“ Haveno”。\n\n一旦解决了技术限制(所需的 Java 版本的 Java 打包程序尚未交付),Haveno将升级到新的 Java 版本,以避免该问题。 popup.warning.wrongVersion=您这台电脑上可能有错误的 Haveno 版本。\n您的电脑的架构是:{0}\n您安装的 Haveno 二进制文件是:{1}\n请关闭并重新安装正确的版本({2})。 popup.warning.incompatibleDB=我们检测到不兼容的数据库文件!\n\n那些数据库文件与我们当前的代码库不兼容:\n{0}\n\n我们对损坏的文件进行了备份,并将默认值应用于新的数据库版本。\n\n备份位于:\n{1}/db/backup_of_corrupted_data。\n\n请检查您是否安装了最新版本的 Haveno\n您可以下载:\nhttps://haveno.exchange/downloads\n\n请重新启动应用程序。 popup.warning.startupFailed.twoInstances=Haveno 已经在运行。 您不能运行两个 Haveno 实例。 @@ -1998,7 +1993,7 @@ payment.limits.info.withSigning=为了降低这一风险,Haveno 基于两个 payment.cashDeposit.info=请确认您的银行允许您将现金存款汇入他人账户。例如,美国银行和富国银行不再允许此类存款。 payment.revolut.info=Revolut 要求使用“用户名”作为帐户 ID,而不是像以往的电话号码或电子邮件。 -payment.account.revolut.addUserNameInfo={0}\n您现有的 Revolut 帐户({1})尚未设置“用户名”。\n请输入您的 Revolut ``用户名''以更新您的帐户数据。\n这不会影响您的账龄验证状态。 +payment.account.revolut.addUserNameInfo={0}\n您现有的 Revolut 帐户({1})尚未设置“用户名”。\n请输入您的 Revolut ``用户名'以更新您的帐户数据。\n这不会影响您的账龄验证状态。 payment.revolut.addUserNameInfo.headLine=更新 Revolut 账户 payment.cashapp.info=请注意,Cash App 的退款风险高于大多数银行转账。 @@ -2035,7 +2030,7 @@ payment.japan.recipient=名称 payment.australia.payid=PayID payment.payid=PayID 需链接至金融机构。例如电子邮件地址或手机。 payment.payid.info=PayID,如电话号码、电子邮件地址或澳大利亚商业号码(ABN),您可以安全地连接到您的银行、信用合作社或建立社会帐户。你需要在你的澳大利亚金融机构创建一个 PayID。发送和接收金融机构都必须支持 PayID。更多信息请查看[HYPERLINK:https://payid.com.au/faqs/] -payment.amazonGiftCard.info=To pay with Amazon eGift Card, you will need to send an Amazon eGift Card to the XMR seller via your Amazon account. \n\nHaveno will show the XMR seller''s email address or phone number where the gift card should be sent, and you must include the trade ID in the gift card''s message field. Please see the wiki [HYPERLINK:https://docs.haveno.exchange/the-project/payment_methods/Amazon_eGift_card] for further details and best practices. \n\nThree important notes:\n- try to send gift cards with amounts of 100 USD or smaller, as Amazon is known to flag larger gift cards as fraudulent\n- try to use creative, believable text for the gift card''s message (e.g., "Happy birthday Susan!") along with the trade ID (and use trader chat to tell your trading peer the reference text you picked so they can verify your payment)\n- Amazon eGift Cards can only be redeemed on the Amazon website they were purchased on (e.g., a gift card purchased on amazon.it can only be redeemed on amazon.it) +payment.amazonGiftCard.info=To pay with Amazon eGift Card, you will need to send an Amazon eGift Card to the XMR seller via your Amazon account. \n\nHaveno will show the XMR seller's email address or phone number where the gift card should be sent, and you must include the trade ID in the gift card's message field. Please see the wiki [HYPERLINK:https://docs.haveno.exchange/the-project/payment_methods/Amazon_eGift_card] for further details and best practices. \n\nThree important notes:\n- try to send gift cards with amounts of 100 USD or smaller, as Amazon is known to flag larger gift cards as fraudulent\n- try to use creative, believable text for the gift card's message (e.g., "Happy birthday Susan!") along with the trade ID (and use trader chat to tell your trading peer the reference text you picked so they can verify your payment)\n- Amazon eGift Cards can only be redeemed on the Amazon website they were purchased on (e.g., a gift card purchased on amazon.it can only be redeemed on amazon.it) payment.paysafe.info=为了保障您的安全,我们强烈不建议使用 Paysafecard PIN 进行支付。\n\n\ 通过 PIN 进行的交易无法被独立验证以解决争议。如果出现问题,资金可能无法追回。\n\n\ 为确保交易安全并支持争议解决,请始终使用提供可验证记录的支付方式。 diff --git a/core/src/main/resources/i18n/displayStrings_zh-hant.properties b/core/src/main/resources/i18n/displayStrings_zh-hant.properties index a1694e3999..a816482877 100644 --- a/core/src/main/resources/i18n/displayStrings_zh-hant.properties +++ b/core/src/main/resources/i18n/displayStrings_zh-hant.properties @@ -4,11 +4,6 @@ # E.g.: [main-view].[component].[description] # In some cases we use enum values or constants to map to display strings -# A annoying issue with property files is that we need to use 2 single quotes in display string -# containing variables (e.g. {0}), otherwise the variable will not be resolved. -# In display string which do not use a variable a single quote is ok. -# E.g. Don''t .... {1} - # We use sometimes dynamic parts which are put together in the code and therefore sometimes use line breaks or spaces # at the end of the string. Please never remove any line breaks or spaces. They are there with a purpose! # To make longer strings with better readable you can make a line break with \ which does not result in a line break @@ -644,7 +639,7 @@ portfolio.pending.step2_buyer.postal=請用“美國郵政匯票”發送 {0} # suppress inspection "TrailingSpacesInProperty" portfolio.pending.step2_buyer.payByMail=Please send {0} using \"Pay by Mail\" to the XMR seller. Specific instructions are in the trade contract, or if unclear you may ask questions via trader chat. See more details about Pay by Mail on the Haveno wiki [HYPERLINK:https://docs.haveno.exchange/the-project/payment_methods/Pay_By_Mail].\n\n # suppress inspection "TrailingSpacesInProperty" -portfolio.pending.step2_buyer.pay=Please pay {0} via the specified payment method to the XMR seller. You''ll find the seller's account details on the next screen.\n\n +portfolio.pending.step2_buyer.pay=Please pay {0} via the specified payment method to the XMR seller. You'll find the seller's account details on the next screen.\n\n # suppress inspection "TrailingSpacesInProperty" portfolio.pending.step2_buyer.f2f=請通過提供的聯繫人與 XMR 賣家聯繫,並安排會議支付 {0}。\n\n portfolio.pending.step2_buyer.startPaymentUsing=使用 {0} 開始付款 @@ -1160,7 +1155,7 @@ setting.about.shortcuts.sendPrivateNotification=發送私人通知到對等點 setting.about.shortcuts.sendPrivateNotification.value=點擊交易夥伴頭像並按下:{0} 以顯示更多信息 setting.info.headline=新 XMR 自動確認功能 -setting.info.msg=當你完成 XMR/XMR 交易時,您可以使用自動確認功能來驗證是否向您的錢包中發送了正確數量的 XMR,以便 Haveno 可以自動將交易標記為完成,從而使每個人都可以更快地進行交易。\n\n自動確認使用 XMR 發送方提供的交易密鑰在至少 2 個 XMR 區塊瀏覽器節點上檢查 XMR 交易。在默認情況下,Haveno 使用由 Haveno 貢獻者運行的區塊瀏覽器節點,但是我們建議運行您自己的 XMR 區塊瀏覽器節點以最大程度地保護隱私和安全。\n\n您還可以在``設置''中將每筆交易的最大 XMR 數量設置為自動確認以及所需確認的數量。\n\n在 Haveno Wiki 上查看更多詳細信息(包括如何設置自己的區塊瀏覽器節點):https://haveno.exchange/wiki/Trading_Monero#Auto-confirming_trades +setting.info.msg=當你完成 XMR/XMR 交易時,您可以使用自動確認功能來驗證是否向您的錢包中發送了正確數量的 XMR,以便 Haveno 可以自動將交易標記為完成,從而使每個人都可以更快地進行交易。\n\n自動確認使用 XMR 發送方提供的交易密鑰在至少 2 個 XMR 區塊瀏覽器節點上檢查 XMR 交易。在默認情況下,Haveno 使用由 Haveno 貢獻者運行的區塊瀏覽器節點,但是我們建議運行您自己的 XMR 區塊瀏覽器節點以最大程度地保護隱私和安全。\n\n您還可以在``設置'中將每筆交易的最大 XMR 數量設置為自動確認以及所需確認的數量。\n\n在 Haveno Wiki 上查看更多詳細信息(包括如何設置自己的區塊瀏覽器節點):https://haveno.exchange/wiki/Trading_Monero#Auto-confirming_trades #################################################################### # Account #################################################################### @@ -1601,7 +1596,7 @@ error.closedTradeWithUnconfirmedDepositTx=交易 ID 為 {0} 的已關閉交易 error.closedTradeWithNoDepositTx=交易 ID 為 {0} 的保證金交易已被確認。\n\n請重新啟動應用程序來清理已關閉的交易列表。 popup.warning.walletNotInitialized=錢包至今未初始化 -popup.warning.osxKeyLoggerWarning=由於 MacOS 10.14 及更高版本中的安全措施更加嚴格,因此啟動 Java 應用程序(Haveno 使用Java)會在 MacOS 中引發彈出警吿(``Haveno 希望從任何應用程序接收擊鍵'').\n\n為了避免該問題,請打開“ MacOS 設置”,然後轉到“安全和隱私”->“隱私”->“輸入監視”,然後從右側列表中刪除“ Haveno”。\n\n一旦解決了技術限制(所需的 Java 版本的 Java 打包程序尚未交付),Haveno將升級到新的 Java 版本,以避免該問題。 +popup.warning.osxKeyLoggerWarning=由於 MacOS 10.14 及更高版本中的安全措施更加嚴格,因此啟動 Java 應用程序(Haveno 使用Java)會在 MacOS 中引發彈出警吿(``Haveno 希望從任何應用程序接收擊鍵').\n\n為了避免該問題,請打開“ MacOS 設置”,然後轉到“安全和隱私”->“隱私”->“輸入監視”,然後從右側列表中刪除“ Haveno”。\n\n一旦解決了技術限制(所需的 Java 版本的 Java 打包程序尚未交付),Haveno將升級到新的 Java 版本,以避免該問題。 popup.warning.wrongVersion=您這台電腦上可能有錯誤的 Haveno 版本。\n您的電腦的架構是:{0}\n您安裝的 Haveno 二進制文件是:{1}\n請關閉並重新安裝正確的版本({2})。 popup.warning.incompatibleDB=我們檢測到不兼容的數據庫文件!\n\n那些數據庫文件與我們當前的代碼庫不兼容:\n{0}\n\n我們對損壞的文件進行了備份,並將默認值應用於新的數據庫版本。\n\n備份位於:\n{1}/db/backup_of_corrupted_data。\n\n請檢查您是否安裝了最新版本的 Haveno\n您可以下載:\nhttps://haveno.exchange/downloads\n\n請重新啟動應用程序。 popup.warning.startupFailed.twoInstances=Haveno 已經在運行。 您不能運行兩個 Haveno 實例。 @@ -1992,7 +1987,7 @@ payment.limits.info.withSigning=為了降低這一風險,Haveno 基於兩個 payment.cashDeposit.info=請確認您的銀行允許您將現金存款匯入他人賬户。例如,美國銀行和富國銀行不再允許此類存款。 payment.revolut.info=Revolut 要求使用“用户名”作為帳户 ID,而不是像以往的電話號碼或電子郵件。 -payment.account.revolut.addUserNameInfo={0}\n您現有的 Revolut 帳户({1})尚未設置“用户名”。\n請輸入您的 Revolut ``用户名''以更新您的帳户數據。\n這不會影響您的賬齡驗證狀態。 +payment.account.revolut.addUserNameInfo={0}\n您現有的 Revolut 帳户({1})尚未設置“用户名”。\n請輸入您的 Revolut ``用户名'以更新您的帳户數據。\n這不會影響您的賬齡驗證狀態。 payment.revolut.addUserNameInfo.headLine=更新 Revolut 賬户 payment.cashapp.info=請注意,Cash App 的退款風險高於大多數銀行轉帳。 @@ -2029,7 +2024,7 @@ payment.japan.recipient=名稱 payment.australia.payid=PayID payment.payid=PayID 需鏈接至金融機構。例如電子郵件地址或手機。 payment.payid.info=PayID,如電話號碼、電子郵件地址或澳大利亞商業號碼(ABN),您可以安全地連接到您的銀行、信用合作社或建立社會帳户。你需要在你的澳大利亞金融機構創建一個 PayID。發送和接收金融機構都必須支持 PayID。更多信息請查看[HYPERLINK:https://payid.com.au/faqs/] -payment.amazonGiftCard.info=To pay with Amazon eGift Card, you will need to send an Amazon eGift Card to the XMR seller via your Amazon account. \n\nHaveno will show the XMR seller''s email address or phone number where the gift card should be sent, and you must include the trade ID in the gift card''s message field. Please see the wiki [HYPERLINK:https://docs.haveno.exchange/the-project/payment_methods/Amazon_eGift_card] for further details and best practices. \n\nThree important notes:\n- try to send gift cards with amounts of 100 USD or smaller, as Amazon is known to flag larger gift cards as fraudulent\n- try to use creative, believable text for the gift card''s message (e.g., "Happy birthday Susan!") along with the trade ID (and use trader chat to tell your trading peer the reference text you picked so they can verify your payment)\n- Amazon eGift Cards can only be redeemed on the Amazon website they were purchased on (e.g., a gift card purchased on amazon.it can only be redeemed on amazon.it) +payment.amazonGiftCard.info=To pay with Amazon eGift Card, you will need to send an Amazon eGift Card to the XMR seller via your Amazon account. \n\nHaveno will show the XMR seller's email address or phone number where the gift card should be sent, and you must include the trade ID in the gift card's message field. Please see the wiki [HYPERLINK:https://docs.haveno.exchange/the-project/payment_methods/Amazon_eGift_card] for further details and best practices. \n\nThree important notes:\n- try to send gift cards with amounts of 100 USD or smaller, as Amazon is known to flag larger gift cards as fraudulent\n- try to use creative, believable text for the gift card's message (e.g., "Happy birthday Susan!") along with the trade ID (and use trader chat to tell your trading peer the reference text you picked so they can verify your payment)\n- Amazon eGift Cards can only be redeemed on the Amazon website they were purchased on (e.g., a gift card purchased on amazon.it can only be redeemed on amazon.it) payment.paysafe.info=為了保護您的安全,我們強烈不建議使用 Paysafecard PIN 進行付款。\n\n\ 透過 PIN 進行的交易無法獨立驗證以進行爭議解決。如果發生問題,可能無法追回資金。\n\n\ 為確保交易安全並支持爭議解決,請始終使用可驗證記錄的付款方式。 From 5141574d7082a2454974653f97f317ca7d93627e Mon Sep 17 00:00:00 2001 From: woodser Date: Sun, 22 Jun 2025 08:38:43 -0400 Subject: [PATCH 72/74] fix error on export table columns --- .../haveno/desktop/main/funds/locked/LockedView.java | 2 +- .../desktop/main/funds/reserved/ReservedView.java | 2 +- .../main/funds/transactions/TransactionsView.java | 2 +- .../desktop/main/market/trades/TradesChartsView.java | 2 +- desktop/src/main/java/haveno/desktop/util/GUIUtil.java | 10 ++++++++++ 5 files changed, 14 insertions(+), 4 deletions(-) diff --git a/desktop/src/main/java/haveno/desktop/main/funds/locked/LockedView.java b/desktop/src/main/java/haveno/desktop/main/funds/locked/LockedView.java index 5570ca1bef..8cb80b16f7 100644 --- a/desktop/src/main/java/haveno/desktop/main/funds/locked/LockedView.java +++ b/desktop/src/main/java/haveno/desktop/main/funds/locked/LockedView.java @@ -165,7 +165,7 @@ public class LockedView extends ActivatableView { numItems.setText(Res.get("shared.numItemsLabel", sortedList.size())); exportButton.setOnAction(event -> { - ObservableList> tableColumns = tableView.getColumns(); + ObservableList> tableColumns = GUIUtil.getContentColumns(tableView); int reportColumns = tableColumns.size(); CSVEntryConverter headerConverter = item -> { String[] columns = new String[reportColumns]; diff --git a/desktop/src/main/java/haveno/desktop/main/funds/reserved/ReservedView.java b/desktop/src/main/java/haveno/desktop/main/funds/reserved/ReservedView.java index 3ed2dfea85..e71fae8d89 100644 --- a/desktop/src/main/java/haveno/desktop/main/funds/reserved/ReservedView.java +++ b/desktop/src/main/java/haveno/desktop/main/funds/reserved/ReservedView.java @@ -165,7 +165,7 @@ public class ReservedView extends ActivatableView { numItems.setText(Res.get("shared.numItemsLabel", sortedList.size())); exportButton.setOnAction(event -> { - ObservableList> tableColumns = tableView.getColumns(); + ObservableList> tableColumns = GUIUtil.getContentColumns(tableView); int reportColumns = tableColumns.size(); CSVEntryConverter headerConverter = item -> { String[] columns = new String[reportColumns]; diff --git a/desktop/src/main/java/haveno/desktop/main/funds/transactions/TransactionsView.java b/desktop/src/main/java/haveno/desktop/main/funds/transactions/TransactionsView.java index 66434a8663..eb97c325ab 100644 --- a/desktop/src/main/java/haveno/desktop/main/funds/transactions/TransactionsView.java +++ b/desktop/src/main/java/haveno/desktop/main/funds/transactions/TransactionsView.java @@ -203,7 +203,7 @@ public class TransactionsView extends ActivatableView { numItems.setText(Res.get("shared.numItemsLabel", sortedDisplayedTransactions.size())); exportButton.setOnAction(event -> { - final ObservableList> tableColumns = tableView.getColumns(); + final ObservableList> tableColumns = GUIUtil.getContentColumns(tableView); final int reportColumns = tableColumns.size() - 1; // CSV report excludes the last column (an icon) CSVEntryConverter headerConverter = item -> { String[] columns = new String[reportColumns]; diff --git a/desktop/src/main/java/haveno/desktop/main/market/trades/TradesChartsView.java b/desktop/src/main/java/haveno/desktop/main/market/trades/TradesChartsView.java index 7470cf1c4b..bb75c80684 100644 --- a/desktop/src/main/java/haveno/desktop/main/market/trades/TradesChartsView.java +++ b/desktop/src/main/java/haveno/desktop/main/market/trades/TradesChartsView.java @@ -397,7 +397,7 @@ public class TradesChartsView extends ActivatableViewAndModel> tableColumns = tableView.getColumns(); + ObservableList> tableColumns = GUIUtil.getContentColumns(tableView); int reportColumns = tableColumns.size() + 1; boolean showAllTradeCurrencies = model.showAllTradeCurrenciesProperty.get(); diff --git a/desktop/src/main/java/haveno/desktop/util/GUIUtil.java b/desktop/src/main/java/haveno/desktop/util/GUIUtil.java index 3097780c1a..4aad28f464 100644 --- a/desktop/src/main/java/haveno/desktop/util/GUIUtil.java +++ b/desktop/src/main/java/haveno/desktop/util/GUIUtil.java @@ -1281,6 +1281,16 @@ public class GUIUtil { } } + public static ObservableList> getContentColumns(TableView tableView) { + ObservableList> contentColumns = FXCollections.observableArrayList(); + for (TableColumn column : tableView.getColumns()) { + if (!column.getStyleClass().contains("first-column") && !column.getStyleClass().contains("last-column")) { + contentColumns.add(column); + } + } + return contentColumns; + } + public static ImageView getCurrencyIcon(String currencyCode) { return getCurrencyIcon(currencyCode, 24); } From b289a256eeef0bade8bdb8a237471c9ce2f6e62c Mon Sep 17 00:00:00 2001 From: jermanuts <109705802+jermanuts@users.noreply.github.com> Date: Mon, 23 Jun 2025 17:42:14 +0200 Subject: [PATCH 73/74] fix donation permalink --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9469a16e2f..ab232aafa7 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,7 @@ See the [developer guide](docs/developer-guide.md) to get started developing for See [docs/CONTRIBUTING.md](docs/CONTRIBUTING.md) for our styling guides. -If you are not able to contribute code and want to contribute development resources, [donations](#support-and-sponsorships) fund development bounties. +If you are not able to contribute code and want to contribute development resources, [donations](#support) fund development bounties. ## Bounties From 8cca2cbb52379973840ed806ab4fd781a9e337c0 Mon Sep 17 00:00:00 2001 From: woodser Date: Sat, 28 Jun 2025 10:17:38 -0400 Subject: [PATCH 74/74] do not color currency code in offer book volume column --- .../desktop/main/offer/offerbook/OfferBookView.java | 5 +++-- .../main/offer/offerbook/OfferBookViewModel.java | 10 +++++++++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/desktop/src/main/java/haveno/desktop/main/offer/offerbook/OfferBookView.java b/desktop/src/main/java/haveno/desktop/main/offer/offerbook/OfferBookView.java index e3ed68ccf1..7cb2bc7bdb 100644 --- a/desktop/src/main/java/haveno/desktop/main/offer/offerbook/OfferBookView.java +++ b/desktop/src/main/java/haveno/desktop/main/offer/offerbook/OfferBookView.java @@ -946,8 +946,9 @@ abstract public class OfferBookView

    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 literal 0 HcmV?d00001 diff --git a/desktop/src/main/resources/images/yellow_circle.png b/desktop/src/main/resources/images/yellow_circle.png index 44e5a272fa5d4c5c0496f592cc505b1b697054d5..0fe6fc0b4e36af3da43dab76f785f720587868fc 100644 GIT binary patch literal 4887 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 JPDHLkV1mB$S%Lrn literal 510 zcmVvUIiu8jY=WyX1KJL97_rHfR){(b%mwwdW;5&QOq_&8? z>WW~v8s9cdTqS-<7tpB8z$FQI^f^4b0MD3*qtC+8XOXp1$-r>gJ&)J52|w(T_-`cO zkT~p83~B4b%@WV^IvjltHT4M^m1)$=lW3Hu0<;M$jzM+~BYwdNOTV_{DC|-UcIjyw zZF;7R;vIP@_yw2d3z%oau#^$7;@B_719Tir8A0_Tit5E<_yu<98Hm^iqtFj34T4HT zpwh!YH3|bDrw>5HzUzXT8iRHchMvCr14R_E>Thxs7g!86IMWNB%GRb-U>ctvUIiu8jY=WyX1KJL97_rHfR){(b%mwwdW;5&QOq_&8? z>WW~v8s9cdTqS-<7tpB8z$FQI^f^4b0MD3*qtC+8XOXp1$-r>gJ&)J52|w(T_-`cO zkT~p83~B4b%@WV^IvjltHT4M^m1)$=lW3Hu0<;M$jzM+~BYwdNOTV_{DC|-UcIjyw zZF;7R;vIP@_yw2d3z%oau#^$7;@B_719Tir8A0_Tit5E<_yu<98Hm^iqtFj34T4HT zpwh!YH3|bDrw>5HzUzXT8iRHchMvCr14R_E>Thxs7g!86IMWNB%GRb-U>ct Date: Fri, 6 Jun 2025 08:24:45 -0400 Subject: [PATCH 57/74] bump version to 1.1.2 --- build.gradle | 2 +- .../src/main/java/haveno/common/app/Version.java | 2 +- .../linux/exchange.haveno.Haveno.metainfo.xml | 2 +- desktop/package/macosx/Info.plist | 4 ++-- scripts/install_whonix_qubes/INSTALL.md | 14 +++++++------- scripts/install_whonix_qubes/README.md | 2 +- .../main/java/haveno/seednode/SeedNodeMain.java | 2 +- 7 files changed, 14 insertions(+), 14 deletions(-) diff --git a/build.gradle b/build.gradle index 0c8a4412f2..f52dd1a10d 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.1.1-SNAPSHOT' + version = '1.1.2-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 d7be891aea..325f4a7a0e 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.1.1"; + public static final String VERSION = "1.1.2"; /** * Holds a list of the tagged resource files for optimizing the getData requests. diff --git a/desktop/package/linux/exchange.haveno.Haveno.metainfo.xml b/desktop/package/linux/exchange.haveno.Haveno.metainfo.xml index 57aeb02f09..277b583389 100644 --- a/desktop/package/linux/exchange.haveno.Haveno.metainfo.xml +++ b/desktop/package/linux/exchange.haveno.Haveno.metainfo.xml @@ -60,6 +60,6 @@ - + diff --git a/desktop/package/macosx/Info.plist b/desktop/package/macosx/Info.plist index c6bf09f2de..e4f331e614 100644 --- a/desktop/package/macosx/Info.plist +++ b/desktop/package/macosx/Info.plist @@ -5,10 +5,10 @@ CFBundleVersion - 1.1.1 + 1.1.2 CFBundleShortVersionString - 1.1.1 + 1.1.2 CFBundleExecutable Haveno diff --git a/scripts/install_whonix_qubes/INSTALL.md b/scripts/install_whonix_qubes/INSTALL.md index adee6d2e60..fac278a406 100644 --- a/scripts/install_whonix_qubes/INSTALL.md +++ b/scripts/install_whonix_qubes/INSTALL.md @@ -153,7 +153,7 @@ $ printf 'haveno-Haveno.desktop' | qvm-appmenus --set-whitelist – haveno