diff --git a/core/src/main/java/haveno/core/support/dispute/DisputeManager.java b/core/src/main/java/haveno/core/support/dispute/DisputeManager.java index ba23f41e..71155402 100644 --- a/core/src/main/java/haveno/core/support/dispute/DisputeManager.java +++ b/core/src/main/java/haveno/core/support/dispute/DisputeManager.java @@ -764,6 +764,7 @@ public abstract class DisputeManager> extends Sup disputeResult.getChatMessage().getUid()); clearPendingMessage(); + dispute.setIsClosed(); // We use the chatMessage wrapped inside the DisputeClosedMessage for // the state, as that is displayed to the user and we only persist that msg disputeResult.getChatMessage().setArrived(true); @@ -782,6 +783,7 @@ public abstract class DisputeManager> extends Sup disputeResult.getChatMessage().getUid()); clearPendingMessage(); + dispute.setIsClosed(); // We use the chatMessage wrapped inside the DisputeClosedMessage for // the state, as that is displayed to the user and we only persist that msg disputeResult.getChatMessage().setStoredInMailbox(true); diff --git a/core/src/main/java/haveno/core/trade/Trade.java b/core/src/main/java/haveno/core/trade/Trade.java index 18a944b5..f315c6cb 100644 --- a/core/src/main/java/haveno/core/trade/Trade.java +++ b/core/src/main/java/haveno/core/trade/Trade.java @@ -392,8 +392,8 @@ public abstract class Trade implements Tradable, Model { transient private Subscription tradePhaseSubscription; transient private Subscription payoutStateSubscription; transient private TaskLooper txPollLooper; - transient private Long walletRefreshPeriod; - transient private Long syncNormalStartTime; + transient private Long walletRefreshPeriodMs; + transient private Long syncNormalStartTimeMs; public static final long DEFER_PUBLISH_MS = 25000; // 25 seconds private static final long IDLE_SYNC_PERIOD_MS = 1680000; // 28 minutes (monero's default connection timeout is 30 minutes on a local connection, so beyond this the wallets will disconnect) @@ -775,7 +775,7 @@ public abstract class Trade implements Tradable, Model { } public boolean isIdling() { - return this instanceof ArbitratorTrade && isDepositsConfirmed() && walletExists(); // arbitrator idles trade after deposits confirm + return this instanceof ArbitratorTrade && isDepositsConfirmed() && walletExists() && syncNormalStartTimeMs == null; // arbitrator idles trade after deposits confirm unless overriden } public void syncAndPollWallet() { @@ -783,11 +783,25 @@ public abstract class Trade implements Tradable, Model { } public void syncWalletNormallyForMs(long syncNormalDuration) { - syncNormalStartTime = System.currentTimeMillis(); + syncNormalStartTimeMs = System.currentTimeMillis(); + + // override wallet refresh period setWalletRefreshPeriod(xmrWalletService.getConnectionService().getRefreshPeriodMs()); - UserThread.runAfter(() -> { - if (!isShutDown && System.currentTimeMillis() >= syncNormalStartTime + syncNormalDuration) updateWalletRefreshPeriod(); - }, syncNormalDuration); + + // reset wallet refresh period after duration + new Thread(() -> { + GenUtils.waitFor(syncNormalDuration); + if (!isShutDown && System.currentTimeMillis() >= syncNormalStartTimeMs + syncNormalDuration) { + syncNormalStartTimeMs = null; + updateWalletRefreshPeriod(); + } + }).start(); + + // TODO: sync wallet because `auto_refresh` will not sync wallet until end of last sync period (which could be a long idle) + new Thread(() -> { + GenUtils.waitFor(1000); + if (!isShutDownStarted) trySyncWallet(true); + }).start(); } public void importMultisigHex() { @@ -824,7 +838,7 @@ public abstract class Trade implements Tradable, Model { stopPolling(); xmrWalletService.closeWallet(wallet, true); wallet = null; - walletRefreshPeriod = null; + walletRefreshPeriodMs = null; } } @@ -1317,11 +1331,14 @@ public abstract class Trade implements Tradable, Model { getSeller().setPayoutAmount(getSeller().getSecurityDeposit().subtract(getSeller().getPayoutTxFee())); } else if (getDisputeState().isClosed()) { DisputeResult disputeResult = getDisputeResult(); - BigInteger[] buyerSellerPayoutTxFees = ArbitrationManager.getBuyerSellerPayoutTxCost(disputeResult, payoutTx.getFee()); - getBuyer().setPayoutTxFee(buyerSellerPayoutTxFees[0]); - getSeller().setPayoutTxFee(buyerSellerPayoutTxFees[1]); - getBuyer().setPayoutAmount(disputeResult.getBuyerPayoutAmountBeforeCost().subtract(getBuyer().getPayoutTxFee())); - getSeller().setPayoutAmount(disputeResult.getSellerPayoutAmountBeforeCost().subtract(getSeller().getPayoutTxFee())); + if (disputeResult == null) log.warn("Dispute result is not set for {} {}", getClass().getSimpleName(), getId()); + else { + BigInteger[] buyerSellerPayoutTxFees = ArbitrationManager.getBuyerSellerPayoutTxCost(disputeResult, payoutTx.getFee()); + getBuyer().setPayoutTxFee(buyerSellerPayoutTxFees[0]); + getSeller().setPayoutTxFee(buyerSellerPayoutTxFees[1]); + getBuyer().setPayoutAmount(disputeResult.getBuyerPayoutAmountBeforeCost().subtract(getBuyer().getPayoutTxFee())); + getSeller().setPayoutAmount(disputeResult.getSellerPayoutAmountBeforeCost().subtract(getSeller().getPayoutTxFee())); + } } } @@ -1834,11 +1851,11 @@ public abstract class Trade implements Tradable, Model { setWalletRefreshPeriod(getWalletRefreshPeriod()); } - private void setWalletRefreshPeriod(long walletRefreshPeriod) { + private void setWalletRefreshPeriod(long walletRefreshPeriodMs) { synchronized (walletLock) { if (this.isShutDownStarted) return; - if (this.walletRefreshPeriod != null && this.walletRefreshPeriod == walletRefreshPeriod) return; - this.walletRefreshPeriod = walletRefreshPeriod; + if (this.walletRefreshPeriodMs != null && this.walletRefreshPeriodMs == walletRefreshPeriodMs) return; + this.walletRefreshPeriodMs = walletRefreshPeriodMs; if (getWallet() != null) { log.info("Setting wallet refresh rate for {} {} to {}", getClass().getSimpleName(), getId(), getWalletRefreshPeriod()); getWallet().startSyncing(getWalletRefreshPeriod()); // TODO (monero-project): wallet rpc waits until last sync period finishes before starting new sync period @@ -1855,7 +1872,7 @@ public abstract class Trade implements Tradable, Model { if (isShutDownStarted || isPolling()) return; log.info("Starting to poll wallet for {} {}", getClass().getSimpleName(), getId()); txPollLooper = new TaskLooper(() -> pollWallet()); - txPollLooper.start(walletRefreshPeriod); + txPollLooper.start(walletRefreshPeriodMs); } } @@ -1961,6 +1978,7 @@ public abstract class Trade implements Tradable, Model { } } catch (Exception e) { if (!isShutDownStarted && wallet != null && isWalletConnected()) { + e.printStackTrace(); log.warn("Error polling trade wallet for {} {}: {}. Monerod={}", getClass().getSimpleName(), getId(), e.getMessage(), getXmrWalletService().getConnectionService().getConnection()); } } 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 a85b3bab..a5e4a683 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 @@ -581,6 +581,8 @@ public class DisputeSummaryWindow extends Overlay { Button cancelButton = tuple.second; closeTicketButton.setOnAction(e -> { + closeTicketButton.disableProperty().unbind(); + closeTicketButton.setDisable(true); if (dispute.getSupportType() == SupportType.ARBITRATION && peersDisputeOptional.isPresent() && !peersDisputeOptional.get().isClosed() && 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 7491acce..7a259d84 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 @@ -40,7 +40,6 @@ import haveno.core.support.messages.ChatMessage; import haveno.core.trade.Contract; import haveno.core.trade.HavenoUtils; import haveno.core.trade.Trade; -import haveno.core.trade.Trade.DisputeState; import haveno.core.trade.TradeManager; import haveno.core.user.Preferences; import haveno.core.util.FormattingUtils; @@ -1353,7 +1352,7 @@ public abstract class DisputeView extends ActivatableView { // subscribe to trade's dispute state Trade trade = tradeManager.getTrade(item.getTradeId()); if (trade == null) log.warn("Dispute's trade is null for trade {}", item.getTradeId()); - else subscription = EasyBind.subscribe(trade.disputeStateProperty(), disputeState -> setText(getDisputeStateText(disputeState))); + else subscription = EasyBind.subscribe(item.isClosedProperty(), closedProp -> setText(getDisputeStateText(item))); } else { if (closedProperty != null) { closedProperty.removeListener(listener); @@ -1373,28 +1372,16 @@ public abstract class DisputeView extends ActivatableView { return column; } - private String getDisputeStateText(DisputeState disputeState) { - switch (disputeState) { - case DISPUTE_REQUESTED: - return Res.get("support.requested"); - case DISPUTE_CLOSED: - return Res.get("support.closed"); - default: - return Res.get("support.open"); - } - } - private String getDisputeStateText(Dispute dispute) { Trade trade = tradeManager.getTrade(dispute.getTradeId()); if (trade == null) { log.warn("Dispute's trade is null for trade {}", dispute.getTradeId()); return Res.get("support.closed"); } + if (dispute.isClosed()) return Res.get("support.closed"); switch (trade.getDisputeState()) { case DISPUTE_REQUESTED: return Res.get("support.requested"); - case DISPUTE_CLOSED: - return Res.get("support.closed"); default: return Res.get("support.open"); }