show trade wallet sync progress with fixes

This commit is contained in:
woodser 2025-12-19 09:40:47 -05:00 committed by woodser
parent 1f62ca3970
commit a3c38ff678
26 changed files with 160 additions and 65 deletions

View file

@ -69,7 +69,7 @@ public class AppStartupState {
});
xmrWalletService.downloadPercentageProperty().addListener((observable, oldValue, newValue) -> {
if (xmrWalletService.isDownloadComplete())
if (xmrWalletService.wasWalletSynced())
isWalletSynced.set(true);
});

View file

@ -387,7 +387,7 @@ public abstract class SupportManager {
p2PService.isBootstrapped() &&
xmrConnectionService.isDownloadComplete() &&
xmrConnectionService.hasSufficientPeersForBroadcast() &&
xmrWalletService.isDownloadComplete();
xmrWalletService.wasWalletSynced();
}

View file

@ -10,6 +10,7 @@ import java.util.Date;
public class DownloadListener {
private final DoubleProperty percentage = new SimpleDoubleProperty(-1);
// TODO: remove redundant execute?
public void progress(double percentage, long blocksLeft, Date date) {
UserThread.execute(() -> {
UserThread.await(() -> this.percentage.set(percentage)); // TODO: these awaits are jenky

View file

@ -19,6 +19,7 @@ import haveno.core.api.XmrConnectionService;
import haveno.core.trade.HavenoUtils;
import haveno.core.xmr.setup.DownloadListener;
import javafx.beans.property.LongProperty;
import javafx.beans.property.ReadOnlyDoubleProperty;
import javafx.beans.property.SimpleLongProperty;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
@ -50,13 +51,14 @@ public abstract class XmrWalletBase {
protected boolean wasWalletSynced;
protected final Map<String, Optional<MoneroTx>> txCache = new HashMap<String, Optional<MoneroTx>>();
protected boolean isClosingWallet;
protected boolean isSyncingWithoutProgress;
protected boolean isSyncingWithProgress;
protected Long syncStartHeight;
protected TaskLooper syncProgressLooper;
protected CountDownLatch syncProgressLatch;
protected Exception syncProgressError;
protected Timer syncProgressTimeout;
protected final DownloadListener downloadListener = new DownloadListener();
protected final DownloadListener walletSyncListener = new DownloadListener();
protected final LongProperty walletHeight = new SimpleLongProperty(0);
@Getter
protected boolean isShutDownStarted;
@ -82,9 +84,12 @@ public abstract class XmrWalletBase {
ExecutorService executor = Executors.newSingleThreadExecutor();
Callable<MoneroSyncResult> task = () -> {
MoneroSyncResult result = wallet.sync();
saveWalletIfElapsedTime();
if (isSyncing()) log.warn("Syncing without progress while already syncing. That should never happen.");
isSyncingWithoutProgress = true;
walletHeight.set(wallet.getHeight());
MoneroSyncResult result = wallet.sync();
walletHeight.set(wallet.getHeight());
wasWalletSynced = true;
return result;
};
@ -101,6 +106,8 @@ public abstract class XmrWalletBase {
Thread.currentThread().interrupt(); // restore interrupt status
throw new RuntimeException("Sync was interrupted", e);
} finally {
isSyncingWithoutProgress = false;
saveWalletIfElapsedTime();
executor.shutdownNow();
}
}
@ -116,13 +123,13 @@ public abstract class XmrWalletBase {
try {
// set initial state
if (isSyncingWithProgress) log.warn("Syncing with progress while already syncing with progress. That should never happen");
if (isSyncing()) log.warn("Syncing with progress while already syncing. That should never happen.");
resetSyncProgressTimeout();
isSyncingWithProgress = true;
walletSyncListener.progress(0, -1, null); // reset progress
syncProgressError = null;
long targetHeightAtStart = xmrConnectionService.getTargetHeight();
syncStartHeight = walletHeight.get();
updateSyncProgress(syncStartHeight, targetHeightAtStart);
updateSyncProgress(walletHeight.get(), targetHeightAtStart);
// test connection changing on startup before wallet synced
if (testReconnectOnStartup) {
@ -144,7 +151,7 @@ public abstract class XmrWalletBase {
updateSyncProgress(height, appliedTargetHeight);
}
});
setWalletSyncedWithProgress();
onDoneSyncWithProgress();
return;
}
@ -189,11 +196,8 @@ public abstract class XmrWalletBase {
syncProgressLooper.stop();
// set synced or throw error
if (syncProgressError == null) {
setWalletSyncedWithProgress();
} else {
throw new RuntimeException(syncProgressError);
}
if (syncProgressError == null) onDoneSyncWithProgress();
else throw new RuntimeException(syncProgressError);
} catch (Exception e) {
throw e;
} finally {
@ -203,6 +207,10 @@ public abstract class XmrWalletBase {
}
}
public boolean wasWalletSynced() {
return wasWalletSynced;
}
public boolean requestSwitchToNextBestConnection(MoneroRpcConnection sourceConnection) {
if (xmrConnectionService.requestSwitchToNextBestConnection(sourceConnection)) {
onConnectionChanged(xmrConnectionService.getConnection()); // change connection on same thread
@ -224,6 +232,14 @@ public abstract class XmrWalletBase {
ThreadUtils.submitToPool(() -> saveWalletIfElapsedTime());
}
public boolean isSyncing() {
return isSyncingWithProgress || isSyncingWithoutProgress;
}
public ReadOnlyDoubleProperty downloadPercentageProperty() {
return walletSyncListener.percentageProperty();
}
public static boolean isSyncWithProgressTimeout(Throwable e) {
return e.getMessage() != null && e.getMessage().contains(SYNC_TIMEOUT_MSG);
}
@ -246,17 +262,15 @@ public abstract class XmrWalletBase {
// set wallet height
walletHeight.set(height);
// new wallet reports height 1 before synced
if (height == 1) {
downloadListener.progress(0, targetHeight - height, null);
return;
}
// new wallet reports height 0 or 1 before synced
if (height <= 1) return;
// set progress
long blocksLeft = targetHeight - height;
if (syncStartHeight == null) syncStartHeight = height;
double percent = Math.min(1.0, targetHeight == syncStartHeight ? 1.0 : ((double) height - syncStartHeight) / (double) (targetHeight - syncStartHeight));
downloadListener.progress(percent, blocksLeft, null);
if (percent >= 1.0) wasWalletSynced = true; // set synced state before announcing progress
walletSyncListener.progress(percent, blocksLeft, null);
}
private synchronized void resetSyncProgressTimeout() {
@ -268,7 +282,7 @@ public abstract class XmrWalletBase {
}, SYNC_TIMEOUT_SECONDS, TimeUnit.SECONDS);
}
private void setWalletSyncedWithProgress() {
private void onDoneSyncWithProgress() {
// stop syncing and save wallet if elapsed time
if (wallet != null) { // can become null if interrupted by force close
@ -277,8 +291,5 @@ public abstract class XmrWalletBase {
saveWalletIfElapsedTime();
}
}
// update state
wasWalletSynced = true;
}
}

View file

@ -63,7 +63,6 @@ import java.util.concurrent.CopyOnWriteArraySet;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javafx.beans.property.LongProperty;
import javafx.beans.property.ReadOnlyDoubleProperty;
import javafx.beans.value.ChangeListener;
import monero.common.MoneroError;
import monero.common.MoneroRpcConnection;
@ -266,18 +265,6 @@ public class XmrWalletService extends XmrWalletBase {
return accountService.getPassword() != null;
}
public ReadOnlyDoubleProperty downloadPercentageProperty() {
return downloadListener.percentageProperty();
}
private void doneDownload() {
downloadListener.doneDownload();
}
public boolean isDownloadComplete() {
return downloadPercentageProperty().get() == 1d;
}
public LongProperty walletHeightProperty() {
return walletHeight;
}
@ -1494,7 +1481,7 @@ public class XmrWalletService extends XmrWalletBase {
resetIfWalletChanged();
// signal that main wallet is synced
doneDownload();
walletSyncListener.doneDownload();
// notify setup that main wallet is initialized
// TODO: app fully initializes after this is set to true, even though wallet might not be initialized if unconnected. wallet will be created when connection detected

View file

@ -660,6 +660,7 @@ portfolio.pending.unconfirmedTooLong=Deposit transactions on trade {0} are still
confirmed in Haveno, try restarting Haveno.\n\n\
If the problem persists, contact Haveno support [HYPERLINK:https://matrix.to/#/#haveno:monero.social].
portfolio.pending.syncing=Syncing trade wallet — {0}% ({1} blocks remaining)
portfolio.pending.step1.waitForConf=Wait for blockchain confirmations
portfolio.pending.step2_buyer.additionalConf=Deposits have reached 10 confirmations.\nFor extra security, we recommend waiting {0} confirmations before sending payment.\nProceed early at your own risk.
portfolio.pending.step2_buyer.startPayment=Start payment

View file

@ -660,6 +660,7 @@ portfolio.pending.unconfirmedTooLong=Vkladové transakce obchodu {0} jsou stále
potvrzené v Haveno, zkuste Haveno restartovat.\n\n\
Pokud problém přetrvává, kontaktujte podporu Haveno [HYPERLINK:https://matrix.to/#/#haveno:monero.social].
portfolio.pending.syncing=Synchronizuji obchodní peněženku — {0}% ({1} bloků zbývá)
portfolio.pending.step1.waitForConf=Počkejte na potvrzení na blockchainu
portfolio.pending.step2_buyer.additionalConf=Vklady dosáhly 10 potvrzení.\nPro vyšší bezpečnost doporučujeme počkat na {0} potvrzení před odesláním platby.\nPokračujte dříve na vlastní riziko.
portfolio.pending.step2_buyer.startPayment=Zahajte platbu

View file

@ -577,6 +577,7 @@ portfolio.closedTrades.deviation.help=Prozentuale Preisabweichung vom Markt
portfolio.pending.invalidTx=There is an issue with a missing or invalid transaction.\n\nPlease do NOT send the traditional or crypto payment.\n\nOpen a support ticket to get assistance from a Mediator.\n\nError message: {0}
portfolio.pending.syncing=Handelwallet wird synchronisiert — {0}% ({1} Blöcke verbleibend)
portfolio.pending.step1.waitForConf=Auf Blockchain-Bestätigung warten
portfolio.pending.step2_buyer.additionalConf=Einzahlungen haben 10 Bestätigungen erreicht.\nFür zusätzliche Sicherheit empfehlen wir, {0} Bestätigungen abzuwarten, bevor Sie die Zahlung senden.\nEin früheres Vorgehen erfolgt auf eigenes Risiko.
portfolio.pending.step2_buyer.startPayment=Zahlung beginnen

View file

@ -577,6 +577,7 @@ portfolio.closedTrades.deviation.help=Desviación porcentual de precio de mercad
portfolio.pending.invalidTx=Hay un problema con una transacción inválida o no encontrada.\n\nPor faovr NO envíe el pago de traditional o cryptos.\n\nAbra un ticket de soporte para obtener asistencia de un mediador.\n\nMensaje de error: {0}
portfolio.pending.syncing=Sincronizando la billetera de comercio — {0}% ({1} bloques restantes)
portfolio.pending.step1.waitForConf=Esperar a la confirmación en la cadena de bloques
portfolio.pending.step2_buyer.additionalConf=Los depósitos han alcanzado 10 confirmaciones.\nPara mayor seguridad, recomendamos esperar {0} confirmaciones antes de enviar el pago.\nProceda antes bajo su propio riesgo.
portfolio.pending.step2_buyer.startPayment=Comenzar pago

View file

@ -576,6 +576,7 @@ portfolio.closedTrades.deviation.help=Percentage price deviation from market
portfolio.pending.invalidTx=There is an issue with a missing or invalid transaction.\n\nPlease do NOT send the traditional or crypto payment.\n\nOpen a support ticket to get assistance from a Mediator.\n\nError message: {0}
portfolio.pending.syncing=همگام‌سازی کیف‌پول معامله — {0}% ({1} بلاک باقی‌مانده)
portfolio.pending.step1.waitForConf=برای تأییدیه بلاک چین منتظر باشید
portfolio.pending.step2_buyer.additionalConf=واریزها به ۱۰ تأیید رسیده‌اند.\nبرای امنیت بیشتر، توصیه می‌کنیم قبل از ارسال پرداخت، {0} تأیید صبر کنید.\nاقدام زودهنگام با مسئولیت خودتان است.
portfolio.pending.step2_buyer.startPayment=آغاز پرداخت

View file

@ -577,6 +577,7 @@ portfolio.closedTrades.deviation.help=Pourcentage de déviation du prix par rapp
portfolio.pending.invalidTx=Il y'a un problème avec une transaction manquante ou invalide.\n\nVeuillez NE PAS envoyer le payement Traditional ou crypto.\n\nOuvrez un ticket de support pour avoir l'aide d'un médiateur.\n\nMessage d'erreur: {0}
portfolio.pending.syncing=Synchronisation du portefeuille de trading — {0}% ({1} blocs restants)
portfolio.pending.step1.waitForConf=Attendre la confirmation de la blockchain
portfolio.pending.step2_buyer.additionalConf=Les dépôts ont atteint 10 confirmations.\nPour plus de sécurité, nous recommandons dattendre {0} confirmations avant denvoyer le paiement.\nProcédez plus tôt à vos propres risques.
portfolio.pending.step2_buyer.startPayment=Initier le paiement

View file

@ -576,6 +576,7 @@ portfolio.closedTrades.deviation.help=Percentage price deviation from market
portfolio.pending.invalidTx=There is an issue with a missing or invalid transaction.\n\nPlease do NOT send the traditional or crypto payment.\n\nOpen a support ticket to get assistance from a Mediator.\n\nError message: {0}
portfolio.pending.syncing=Sincronizzazione del portafoglio trade — {0}% ({1} blocchi rimanenti)
portfolio.pending.step1.waitForConf=Attendi la conferma della blockchain
portfolio.pending.step2_buyer.additionalConf=I depositi hanno raggiunto 10 conferme.\nPer maggiore sicurezza, consigliamo di attendere {0} conferme prima di inviare il pagamento.\nProcedi in anticipo a tuo rischio.
portfolio.pending.step2_buyer.startPayment=Inizia il pagamento

View file

@ -577,6 +577,7 @@ portfolio.closedTrades.deviation.help=市場からの割合価格偏差
portfolio.pending.invalidTx=There is an issue with a missing or invalid transaction.\n\nPlease do NOT send the traditional or crypto payment.\n\nOpen a support ticket to get assistance from a Mediator.\n\nError message: {0}
portfolio.pending.syncing=取引ウォレットを同期中 — {0}%(残り {1} ブロック)
portfolio.pending.step1.waitForConf=ブロックチェーンの承認をお待ち下さい
portfolio.pending.step2_buyer.additionalConf=入金は10承認に達しました。\n追加の安全のため、支払いを送信する前に{0}承認を待つことをお勧めします。\n早めに進める場合は自己責任となります。
portfolio.pending.step2_buyer.startPayment=支払い開始

View file

@ -579,6 +579,7 @@ portfolio.closedTrades.deviation.help=Percentage price deviation from market
portfolio.pending.invalidTx=There is an issue with a missing or invalid transaction.\n\nPlease do NOT send the traditional or crypto payment.\n\nOpen a support ticket to get assistance from a Mediator.\n\nError message: {0}
portfolio.pending.syncing=Sincronizando a carteira de negociação — {0}% ({1} blocos restantes)
portfolio.pending.step1.waitForConf=Aguardar confirmação da blockchain
portfolio.pending.step2_buyer.additionalConf=Depósitos alcançaram 10 confirmações.\nPara maior segurança, recomendamos aguardar {0} confirmações antes de enviar o pagamento.\nProssiga antecipadamente por sua própria conta e risco.
portfolio.pending.step2_buyer.startPayment=Iniciar pagamento

View file

@ -576,6 +576,7 @@ portfolio.closedTrades.deviation.help=Percentage price deviation from market
portfolio.pending.invalidTx=There is an issue with a missing or invalid transaction.\n\nPlease do NOT send the traditional or crypto payment.\n\nOpen a support ticket to get assistance from a Mediator.\n\nError message: {0}
portfolio.pending.syncing=Sincronizando a carteira de negociação — {0}% ({1} blocos restantes)
portfolio.pending.step1.waitForConf=Esperando confirmação da blockchain
portfolio.pending.step2_buyer.additionalConf=Os depósitos alcançaram 10 confirmações.\nPara maior segurança, recomendamos aguardar {0} confirmações antes de enviar o pagamento.\nProceda antecipadamente por sua própria conta e risco.
portfolio.pending.step2_buyer.startPayment=Iniciar pagamento

View file

@ -576,6 +576,7 @@ portfolio.closedTrades.deviation.help=Percentage price deviation from market
portfolio.pending.invalidTx=There is an issue with a missing or invalid transaction.\n\nPlease do NOT send the traditional or crypto payment.\n\nOpen a support ticket to get assistance from a Mediator.\n\nError message: {0}
portfolio.pending.syncing=Синхронизация торгового кошелька — {0}% (осталось блоков: {1})
portfolio.pending.step1.waitForConf=Ожидание подтверждения в блокчейне
portfolio.pending.step2_buyer.additionalConf=Депозиты достигли 10 подтверждений.\nДля дополнительной безопасности мы рекомендуем дождаться {0} подтверждений перед отправкой платежа.\nРанее действия осуществляются на ваш страх и риск.
portfolio.pending.step2_buyer.startPayment=Сделать платеж

View file

@ -576,6 +576,7 @@ portfolio.closedTrades.deviation.help=Percentage price deviation from market
portfolio.pending.invalidTx=There is an issue with a missing or invalid transaction.\n\nPlease do NOT send the traditional or crypto payment.\n\nOpen a support ticket to get assistance from a Mediator.\n\nError message: {0}
portfolio.pending.syncing=กำลังซิงค์กระเป๋าเงินสำหรับการซื้อขาย — {0}% (เหลืออีก {1} บล็อก)
portfolio.pending.step1.waitForConf=รอการยืนยันของบล็อกเชน
portfolio.pending.step2_buyer.additionalConf=ยอดฝากถึง 10 การยืนยันแล้ว\nเพื่อความปลอดภัยเพิ่มเติม เราแนะนำให้รอ {0} การยืนยันก่อนทำการชำระเงิน\nดำเนินการล่วงหน้าตามความเสี่ยงของคุณเอง
portfolio.pending.step2_buyer.startPayment=เริ่มการชำระเงิน

View file

@ -623,6 +623,7 @@ portfolio.pending.unconfirmedTooLong=İşlem {0} üzerindeki güvence işlemleri
onaylanmış olarak gösterilmiyorlarsa, Haveno'yu yeniden başlatmayı deneyin.\n\n\
Sorun devam ederse, Haveno desteğiyle iletişime geçin [HYPERLINK:https://matrix.to/#/#haveno:monero.social].
portfolio.pending.syncing=Ticaret cüzdanı senkronize ediliyor — %{0} ({1} blok kaldı)
portfolio.pending.step1.waitForConf=Blok zinciri onaylarını bekleyin
portfolio.pending.step2_buyer.additionalConf=Mevduatlar 10 onayı ulaştı.\nEkstra güvenlik için, ödeme göndermeden önce {0} onayı beklemenizi öneririz.\nErken ilerlemek kendi riskinizdedir.
portfolio.pending.step2_buyer.startPayment=Ödemeyi başlat

View file

@ -576,6 +576,7 @@ portfolio.closedTrades.deviation.help=Percentage price deviation from market
portfolio.pending.invalidTx=There is an issue with a missing or invalid transaction.\n\nPlease do NOT send the traditional or crypto payment.\n\nOpen a support ticket to get assistance from a Mediator.\n\nError message: {0}
portfolio.pending.syncing=Đang đồng bộ ví giao dịch — {0}% (còn lại {1} khối)
portfolio.pending.step1.waitForConf=Đợi xác nhận blockchain
portfolio.pending.step2_buyer.additionalConf=Tiền gửi đã đạt 10 xác nhận.\nĐể tăng cường bảo mật, chúng tôi khuyên bạn chờ {0} xác nhận trước khi gửi thanh toán.\nTiến hành sớm là rủi ro của bạn.
portfolio.pending.step2_buyer.startPayment=Bắt đầu thanh toán

View file

@ -577,6 +577,7 @@ portfolio.closedTrades.deviation.help=与市场价格偏差百分比
portfolio.pending.invalidTx=There is an issue with a missing or invalid transaction.\n\nPlease do NOT send the traditional or crypto payment.\n\nOpen a support ticket to get assistance from a Mediator.\n\nError message: {0}
portfolio.pending.syncing=正在同步交易钱包 — {0}%(剩余 {1} 个区块)
portfolio.pending.step1.waitForConf=等待区块链确认
portfolio.pending.step2_buyer.additionalConf=存款已达到 10 个确认。\n为了额外安全我们建议在发送付款前等待 {0} 个确认。\n提前操作风险自负。
portfolio.pending.step2_buyer.startPayment=开始付款

View file

@ -577,6 +577,7 @@ portfolio.closedTrades.deviation.help=Percentage price deviation from market
portfolio.pending.invalidTx=There is an issue with a missing or invalid transaction.\n\nPlease do NOT send the traditional or crypto payment.\n\nOpen a support ticket to get assistance from a Mediator.\n\nError message: {0}
portfolio.pending.syncing=正在同步交易錢包 — {0}%(剩餘 {1} 個區塊)
portfolio.pending.step1.waitForConf=等待區塊鏈確認
portfolio.pending.step2_buyer.additionalConf=存款已達 10 次確認。\n為了額外安全我們建議在發送付款前等待 {0} 次確認。\n提前操作風險自負。
portfolio.pending.step2_buyer.startPayment=開始付款

View file

@ -77,7 +77,7 @@ public abstract class TradeStepView extends AnchorPane {
protected final Preferences preferences;
protected final GridPane gridPane;
private Subscription tradePeriodStateSubscription, tradeStateSubscription, disputeStateSubscription, mediationResultStateSubscription;
private Subscription tradePeriodStateSubscription, tradeStateSubscription, disputeStateSubscription, mediationResultStateSubscription, syncProgressSubscription;
protected int gridRow = 0;
private TextField timeLeftTextField;
private ProgressBar timeLeftProgressBar;
@ -93,6 +93,9 @@ public abstract class TradeStepView extends AnchorPane {
private BootstrapListener bootstrapListener;
private TradeSubView.ChatCallback chatCallback;
private ChangeListener<Boolean> pendingTradesInitializedListener;
protected Label statusLabel;
protected String syncStatus;
protected String tradeStatus;
///////////////////////////////////////////////////////////////////////////////////////////
@ -258,9 +261,21 @@ public abstract class TradeStepView extends AnchorPane {
if (newValue) addTradeStateSubscription();
});
syncProgressSubscription = EasyBind.subscribe(trade.downloadPercentageProperty(), newValue -> {
if (newValue != null) onSyncProgress((double) newValue);
});
UserThread.execute(() -> model.p2PService.removeP2PServiceListener(bootstrapListener));
}
protected void onSyncProgress(double percent) {
if (percent < 0.0 || percent >= 1.0) setSyncStatus("");
else {
long blocksRemaining = HavenoUtils.xmrConnectionService.getTargetHeight() - trade.getHeight();
setSyncStatus(Res.get("portfolio.pending.syncing", ((int) Math.round(percent * 100)), blocksRemaining));
}
}
private void addTradeStateSubscription() {
tradeStateSubscription = EasyBind.subscribe(trade.stateProperty(), newValue -> {
if (newValue != null) {
@ -301,6 +316,9 @@ public abstract class TradeStepView extends AnchorPane {
if (mediationResultStateSubscription != null)
mediationResultStateSubscription.unsubscribe();
if (syncProgressSubscription != null)
syncProgressSubscription.unsubscribe();
if (tradePeriodStateSubscription != null)
tradePeriodStateSubscription.unsubscribe();
@ -402,6 +420,9 @@ public abstract class TradeStepView extends AnchorPane {
infoLabel = addMultilineLabel(gridPane, gridRow, "", Layout.COMPACT_FIRST_ROW_AND_COMPACT_GROUP_DISTANCE);
GridPane.setColumnSpan(infoLabel, 2);
statusLabel = new Label();
gridPane.add(statusLabel, 0, ++gridRow, 2, 1);
}
protected String getInfoText() {
@ -869,4 +890,24 @@ public abstract class TradeStepView extends AnchorPane {
public void setChatCallback(TradeSubView.ChatCallback chatCallback) {
this.chatCallback = chatCallback;
}
protected void setSyncStatus(String text) {
syncStatus = text;
if (syncStatus == null || syncStatus.isEmpty()) {
setStatus(tradeStatus);
} else {
setStatus(syncStatus);
}
}
protected void setTradeStatus(String text) {
tradeStatus = text;
if (syncStatus == null || syncStatus.isEmpty()) {
setStatus(tradeStatus);
}
}
private void setStatus(String text) {
if (statusLabel != null) statusLabel.setText(text == null ? "" : text);
}
}

View file

@ -128,7 +128,6 @@ import static haveno.desktop.util.FormBuilder.addTopLabelTextFieldWithCopyIcon;
public class BuyerStep2View extends TradeStepView {
private Button confirmButton;
private Label statusLabel;
private BusyAnimation busyAnimation;
private Subscription tradeStatePropertySubscription;
private Timer timeoutTimer;
@ -159,40 +158,40 @@ public class BuyerStep2View extends TradeStepView {
if (trade.isDepositsUnlocked() && !trade.isPaymentSent()) {
busyAnimation.stop();
statusLabel.setText("");
setTradeStatus("");
showPopup();
} else if (state.ordinal() <= Trade.State.SELLER_RECEIVED_PAYMENT_SENT_MSG.ordinal()) {
switch (state) {
case BUYER_CONFIRMED_PAYMENT_SENT:
busyAnimation.play();
statusLabel.setText(Res.get("shared.preparingConfirmation"));
setTradeStatus(Res.get("shared.preparingConfirmation"));
break;
case BUYER_SENT_PAYMENT_SENT_MSG:
busyAnimation.play();
statusLabel.setText(Res.get("shared.sendingConfirmation"));
setTradeStatus(Res.get("shared.sendingConfirmation"));
timeoutTimer = UserThread.runAfter(() -> {
busyAnimation.stop();
statusLabel.setText(Res.get("shared.sendingConfirmationAgain"));
setTradeStatus(Res.get("shared.sendingConfirmationAgain"));
}, 30);
break;
case BUYER_STORED_IN_MAILBOX_PAYMENT_SENT_MSG:
busyAnimation.stop();
statusLabel.setText(Res.get("shared.messageStoredInMailbox"));
setTradeStatus(Res.get("shared.messageStoredInMailbox"));
break;
case BUYER_SAW_ARRIVED_PAYMENT_SENT_MSG:
case SELLER_RECEIVED_PAYMENT_SENT_MSG:
busyAnimation.stop();
statusLabel.setText(Res.get("shared.messageArrived"));
setTradeStatus(Res.get("shared.messageArrived"));
break;
case BUYER_SEND_FAILED_PAYMENT_SENT_MSG:
// We get a popup and the trade closed, so we dont need to show anything here
busyAnimation.stop();
statusLabel.setText("");
setTradeStatus("");
break;
default:
log.warn("Unexpected case: State={}, tradeId={} ", state.name(), trade.getId());
busyAnimation.stop();
statusLabel.setText(Res.get("shared.sendingConfirmationAgain"));
setTradeStatus(Res.get("shared.sendingConfirmationAgain"));
break;
}
}
@ -676,7 +675,7 @@ public class BuyerStep2View extends TradeStepView {
private void confirmPaymentSent() {
busyAnimation.play();
statusLabel.setText(Res.get("shared.preparingConfirmation"));
setTradeStatus(Res.get("shared.preparingConfirmation"));
confirmButton.setDisable(true);
model.dataModel.onPaymentSent(() -> {
@ -684,7 +683,7 @@ public class BuyerStep2View extends TradeStepView {
busyAnimation.stop();
new Popup().warning(Res.get("popup.warning.sendMsgFailed") + "\n\n" + errorMessage).show();
confirmButton.setDisable(!confirmPaymentSentPermitted());
UserThread.execute(() -> statusLabel.setText("Error confirming payment sent."));
UserThread.execute(() -> setTradeStatus("Error confirming payment sent."));
});
}

View file

@ -18,6 +18,7 @@
package haveno.desktop.main.portfolio.pendingtrades.steps.buyer;
import de.jensd.fx.fontawesome.AwesomeIcon;
import haveno.common.util.Tuple4;
import haveno.core.locale.Res;
import haveno.core.network.MessageState;
import haveno.desktop.components.TextFieldWithIcon;
@ -27,10 +28,11 @@ import haveno.desktop.util.Layout;
import javafx.beans.value.ChangeListener;
import javafx.scene.control.Label;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.VBox;
import static haveno.desktop.util.FormBuilder.addMultilineLabel;
import static haveno.desktop.util.FormBuilder.addTitledGroupBg;
import static haveno.desktop.util.FormBuilder.addTopLabelTextFieldWithIcon;
import static haveno.desktop.util.FormBuilder.addTopLabelTextFieldWithIconLabel;
public class BuyerStep3View extends TradeStepView {
private final ChangeListener<MessageState> messageStateChangeListener;
@ -74,8 +76,11 @@ public class BuyerStep3View extends TradeStepView {
addTitledGroupBg(gridPane, ++gridRow, 2, getInfoBlockTitle(), Layout.GROUP_DISTANCE);
infoLabel = addMultilineLabel(gridPane, gridRow, "", Layout.FIRST_ROW_AND_GROUP_DISTANCE);
GridPane.setColumnSpan(infoLabel, 2);
textFieldWithIcon = addTopLabelTextFieldWithIcon(gridPane, ++gridRow,
Res.get("portfolio.pending.step3_buyer.wait.msgStateInfo.label"), 0).second;
Tuple4<VBox, Label, TextFieldWithIcon, Label> tuple = addTopLabelTextFieldWithIconLabel(gridPane, ++gridRow,
Res.get("portfolio.pending.step3_buyer.wait.msgStateInfo.label"), 0);
GridPane.setColumnSpan(tuple.first, 2);
textFieldWithIcon = tuple.third;
statusLabel = tuple.fourth;
}
@Override

View file

@ -77,7 +77,6 @@ import static haveno.desktop.util.Layout.FLOATING_LABEL_DISTANCE;
public class SellerStep3View extends TradeStepView {
private Button confirmButton;
private Label statusLabel;
private BusyAnimation busyAnimation;
private Subscription tradeStatePropertySubscription;
private Timer timeoutTimer;
@ -110,44 +109,44 @@ public class SellerStep3View extends TradeStepView {
if (trade.isPaymentSent() && !trade.isPaymentReceived()) {
busyAnimation.stop();
statusLabel.setText("");
setTradeStatus("");
showPopup();
} else if (trade.isPaymentReceived()) {
if (trade.isCompleted()) {
if (!trade.isPayoutPublished()) log.warn("Payout is expected to be published for {} {} state {}", trade.getClass().getSimpleName(), trade.getId(), trade.getState());
busyAnimation.stop();
statusLabel.setText("");
setTradeStatus("");
} else switch (state) {
case SELLER_CONFIRMED_PAYMENT_RECEIPT:
busyAnimation.play();
statusLabel.setText(Res.get("shared.preparingConfirmation"));
setTradeStatus(Res.get("shared.preparingConfirmation"));
break;
case SELLER_SENT_PAYMENT_RECEIVED_MSG:
busyAnimation.play();
statusLabel.setText(Res.get("shared.sendingConfirmation"));
setTradeStatus(Res.get("shared.sendingConfirmation"));
timeoutTimer = UserThread.runAfter(() -> {
busyAnimation.stop();
statusLabel.setText(Res.get("shared.sendingConfirmationAgain"));
setTradeStatus(Res.get("shared.sendingConfirmationAgain"));
}, 30);
break;
case SELLER_STORED_IN_MAILBOX_PAYMENT_RECEIVED_MSG:
busyAnimation.stop();
statusLabel.setText(Res.get("shared.messageStoredInMailbox"));
setTradeStatus(Res.get("shared.messageStoredInMailbox"));
break;
case SELLER_SAW_ARRIVED_PAYMENT_RECEIVED_MSG:
case BUYER_RECEIVED_PAYMENT_RECEIVED_MSG:
busyAnimation.stop();
statusLabel.setText(Res.get("shared.messageArrived"));
setTradeStatus(Res.get("shared.messageArrived"));
break;
case SELLER_SEND_FAILED_PAYMENT_RECEIVED_MSG:
// We get a popup and the trade closed, so we dont need to show anything here
busyAnimation.stop();
statusLabel.setText("");
setTradeStatus("");
break;
default:
log.warn("Unexpected case: State={}, tradeId={} " + state.name(), trade.getId());
busyAnimation.stop();
statusLabel.setText(Res.get("shared.sendingConfirmationAgain"));
setTradeStatus(Res.get("shared.sendingConfirmationAgain"));
break;
}
}
@ -446,7 +445,7 @@ public class SellerStep3View extends TradeStepView {
private void confirmPaymentReceived() {
log.info("User pressed the [Confirm payment receipt] button for Trade {}", trade.getShortId());
busyAnimation.play();
statusLabel.setText(Res.get("shared.preparingConfirmation"));
setTradeStatus(Res.get("shared.preparingConfirmation"));
confirmButton.setDisable(true);
model.dataModel.onPaymentReceived(() -> {
@ -454,7 +453,7 @@ public class SellerStep3View extends TradeStepView {
busyAnimation.stop();
new Popup().warning(Res.get("popup.warning.sendMsgFailed") + "\n\n" + errorMessage).show();
confirmButton.setDisable(!confirmPaymentReceivedPermitted());
UserThread.execute(() -> statusLabel.setText("Error confirming payment received."));
UserThread.execute(() -> setTradeStatus("Error confirming payment received."));
});
}

View file

@ -540,6 +540,42 @@ public class FormBuilder {
return new Tuple2<>(addTopLabelWithVBox(gridPane, rowIndex, columnIndex, title, textFieldWithIcon, top).first, textFieldWithIcon);
}
///////////////////////////////////////////////////////////////////////////////////////////
// Label + TextFieldWithIcon + Label
///////////////////////////////////////////////////////////////////////////////////////////
public static Tuple4<VBox, Label, TextFieldWithIcon, Label> addTopLabelTextFieldWithIconLabel(GridPane gridPane,
int rowIndex,
String title,
double top) {
return addTopLabelTextFieldWithIconLabel(gridPane, rowIndex, 0, title, top);
}
public static Tuple4<VBox, Label, TextFieldWithIcon, Label> addTopLabelTextFieldWithIconLabel(GridPane gridPane,
int rowIndex,
int columnIndex,
String title,
double top) {
HBox hBox = new HBox();
hBox.setSpacing(10);
TextFieldWithIcon textFieldWithIcon = new TextFieldWithIcon();
textFieldWithIcon.setFocusTraversable(false);
Label label = new AutoTooltipLabel();
hBox.setAlignment(Pos.CENTER_LEFT);
hBox.getChildren().addAll(textFieldWithIcon, label);
GridPane.setRowIndex(hBox, rowIndex);
GridPane.setColumnIndex(hBox, columnIndex);
GridPane.setMargin(hBox, new Insets(top, 0, 0, 0));
gridPane.getChildren().add(hBox);
Tuple2<Label, VBox> topLabelWithVBox = addTopLabelWithVBox(gridPane, rowIndex, columnIndex, title, hBox, top);
return new Tuple4<>(topLabelWithVBox.second, topLabelWithVBox.first, textFieldWithIcon, label);
}
///////////////////////////////////////////////////////////////////////////////////////////
// HyperlinkWithIcon