progress notifications during take offer are more frequent and reliable

This commit is contained in:
woodser 2023-04-17 19:15:38 -04:00
parent cffbfa8aaa
commit 427c762620
12 changed files with 59 additions and 25 deletions

View file

@ -333,7 +333,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
stopped = true;
p2PService.getPeerManager().removeListener(this);
p2PService.removeDecryptedDirectMessageListener(this);
signedOfferKeyImagePoller.clearKeyImages();
if (signedOfferKeyImagePoller != null) signedOfferKeyImagePoller.clearKeyImages();
stopPeriodicRefreshOffersTimer();
stopPeriodicRepublishOffersTimer();

View file

@ -54,10 +54,13 @@ import haveno.core.xmr.wallet.XmrWalletService;
import haveno.network.p2p.AckMessage;
import haveno.network.p2p.NodeAddress;
import haveno.network.p2p.P2PService;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ReadOnlyDoubleProperty;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.beans.property.ReadOnlyStringProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
@ -333,6 +336,10 @@ public abstract class Trade implements Tradable, Model {
private long amount;
@Setter
private long price;
private int initStep = 0;
private static final int TOTAL_INIT_STEPS = 15; // total estimated steps
@Getter
private double initProgress = 0;
@Nullable
@Getter
private State state = State.PREPARATION;
@ -368,6 +375,7 @@ public abstract class Trade implements Tradable, Model {
@Getter
transient final private XmrWalletService xmrWalletService;
transient final private DoubleProperty initProgressProperty = new SimpleDoubleProperty(0.0);
transient final private ObjectProperty<State> stateProperty = new SimpleObjectProperty<>(state);
transient final private ObjectProperty<Phase> phaseProperty = new SimpleObjectProperty<>(state.phase);
transient final private ObjectProperty<PayoutState> payoutStateProperty = new SimpleObjectProperty<>(payoutState);
@ -1167,7 +1175,10 @@ public abstract class Trade implements Tradable, Model {
isInitialized = false;
isShutDown = true;
synchronized (walletLock) {
if (wallet != null) closeWallet();
if (wallet != null) {
saveWallet();
stopWallet();
}
}
if (tradePhaseSubscription != null) tradePhaseSubscription.unsubscribe();
if (payoutStateSubscription != null) payoutStateSubscription.unsubscribe();
@ -1206,6 +1217,11 @@ public abstract class Trade implements Tradable, Model {
}
}
public void addInitProgressStep() {
initProgress = Math.min(1.0, (double) ++initStep / TOTAL_INIT_STEPS);
UserThread.execute(() -> initProgressProperty.set(initProgress));
}
public void setState(State state) {
if (isInitialized) {
// We don't want to log at startup the setState calls from all persisted trades
@ -1551,6 +1567,10 @@ public abstract class Trade implements Tradable, Model {
return getPayoutState().ordinal() >= PayoutState.PAYOUT_UNLOCKED.ordinal();
}
public ReadOnlyDoubleProperty initProgressProperty() {
return initProgressProperty;
}
public ReadOnlyObjectProperty<State> stateProperty() {
return stateProperty;
}

View file

@ -834,6 +834,7 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
trade.getMaker().setPubKeyRing(trade.getOffer().getPubKeyRing());
trade.getSelf().setPubKeyRing(model.getPubKeyRing());
trade.getSelf().setPaymentAccountId(paymentAccountId);
trade.addInitProgressStep();
// ensure trade is not already open
Optional<Trade> tradeOptional = getOpenTrade(offer.getId());

View file

@ -72,6 +72,7 @@ public class MaybeSendSignContractRequest extends TradeTask {
}
// create deposit tx and freeze inputs
trade.addInitProgressStep();
MoneroTxWallet depositTx = trade.getXmrWalletService().createDepositTx(trade);
// collect reserved key images
@ -142,6 +143,7 @@ public class MaybeSendSignContractRequest extends TradeTask {
private void completeAux() {
trade.setState(State.CONTRACT_SIGNATURE_REQUESTED);
trade.addInitProgressStep();
processModel.getTradeManager().requestPersistence();
complete();
}

View file

@ -44,6 +44,7 @@ public class ProcessDepositResponse extends TradeTask {
// set success state
trade.setStateIfValidTransitionTo(Trade.State.ARBITRATOR_PUBLISHED_DEPOSIT_TXS);
trade.addInitProgressStep();
processModel.getTradeManager().requestPersistence();
complete();
} catch (Throwable t) {

View file

@ -76,6 +76,7 @@ public class ProcessInitMultisigRequest extends TradeTask {
// prepare multisig if applicable
boolean updateParticipants = false;
if (trade.getSelf().getPreparedMultisigHex() == null) {
trade.addInitProgressStep();
log.info("Preparing multisig wallet for {} {}", trade.getClass().getSimpleName(), trade.getId());
multisigWallet = trade.createWallet();
trade.getSelf().setPreparedMultisigHex(multisigWallet.prepareMultisig());
@ -217,6 +218,7 @@ public class ProcessInitMultisigRequest extends TradeTask {
}
private void completeAux() {
trade.addInitProgressStep();
complete();
}
}

View file

@ -133,6 +133,7 @@ public class ProcessInitTradeRequest extends TradeTask {
checkArgument(request.getTradeAmount() == trade.getAmount().longValueExact(), "Trade amount does not match request's trade amount");
// persist trade
trade.addInitProgressStep();
processModel.getTradeManager().requestPersistence();
complete();
} catch (Throwable t) {

View file

@ -162,6 +162,7 @@ public class ProcessSignContractRequest extends TradeTask {
}
private void completeAux() {
trade.addInitProgressStep();
trade.setState(State.CONTRACT_SIGNED);
processModel.getTradeManager().requestPersistence();
complete();

View file

@ -104,6 +104,7 @@ public class ProcessSignContractResponse extends TradeTask {
// deposit is requested
trade.setState(Trade.State.SENT_PUBLISH_DEPOSIT_TX_REQUEST);
trade.addInitProgressStep();
processModel.getTradeManager().requestPersistence();
} else {
log.info("Waiting for another contract signatures to send deposit request");

View file

@ -54,6 +54,7 @@ public class TakerReserveTradeFunds extends TradeTask {
processModel.setReserveTx(reserveTx);
processModel.getTaker().setReserveTxKeyImages(reservedKeyImages);
processModel.getTradeManager().requestPersistence();
trade.addInitProgressStep();
complete();
} catch (Throwable t) {
trade.setErrorMessage("An error occurred.\n" +

View file

@ -47,6 +47,7 @@ public class TakerSendInitTradeRequestToArbitrator extends TradeTask {
// send request to signing arbitrator then least used arbitrators until success
sendInitTradeRequests(trade.getOffer().getOfferPayload().getArbitratorSigner(), new HashSet<NodeAddress>(), () -> {
trade.addInitProgressStep();
complete();
}, (errorMessage) -> {
log.warn("Cannot initialize trade with arbitrators: " + errorMessage);

View file

@ -18,7 +18,6 @@
package haveno.desktop.main.overlays.windows;
import com.google.common.base.Joiner;
import haveno.common.UserThread;
import haveno.common.crypto.KeyRing;
import haveno.common.util.Tuple2;
import haveno.common.util.Tuple4;
@ -31,7 +30,6 @@ import haveno.core.payment.PaymentAccount;
import haveno.core.payment.payload.PaymentMethod;
import haveno.core.trade.HavenoUtils;
import haveno.core.trade.Trade;
import haveno.core.trade.Trade.State;
import haveno.core.trade.TradeManager;
import haveno.core.user.User;
import haveno.core.util.FormattingUtils;
@ -85,7 +83,8 @@ public class OfferDetailsWindow extends Overlay<OfferDetailsWindow> {
private Optional<Runnable> takeOfferHandlerOptional = Optional.empty();
private BusyAnimation busyAnimation;
private TradeManager tradeManager;
private Subscription tradeStateSubscription;
private Subscription numTradesSubscription;
private Subscription initProgressSubscription;
///////////////////////////////////////////////////////////////////////////////////////////
// Public API
@ -145,6 +144,16 @@ public class OfferDetailsWindow extends Overlay<OfferDetailsWindow> {
protected void onHidden() {
if (busyAnimation != null)
busyAnimation.stop();
if (numTradesSubscription != null) {
numTradesSubscription.unsubscribe();
numTradesSubscription = null;
}
if (initProgressSubscription != null) {
initProgressSubscription.unsubscribe();
initProgressSubscription = null;
}
}
@Override
@ -407,31 +416,25 @@ public class OfferDetailsWindow extends Overlay<OfferDetailsWindow> {
spinnerInfoLabel.setText(Res.get("createOffer.fundsBox.placeOfferSpinnerInfo"));
placeOfferHandlerOptional.ifPresent(Runnable::run);
} else {
State lastState = Trade.State.ARBITRATOR_PUBLISHED_DEPOSIT_TXS;
spinnerInfoLabel.setText(Res.get("takeOffer.fundsBox.takeOfferSpinnerInfo") + " " + getPercentString(0, lastState.ordinal()));
// subscribe to trade progress
spinnerInfoLabel.setText(Res.get("takeOffer.fundsBox.takeOfferSpinnerInfo") + " 0%");
numTradesSubscription = EasyBind.subscribe(tradeManager.getNumPendingTrades(), newNum -> {
subscribeToProgress(spinnerInfoLabel);
});
takeOfferHandlerOptional.ifPresent(Runnable::run);
// update trade state progress
UserThread.runAfter(() -> {
Trade trade = tradeManager.getTrade(offer.getId());
if (trade == null) return;
tradeStateSubscription = EasyBind.subscribe(trade.stateProperty(), newState -> {
String progress = getPercentString(newState.ordinal(), lastState.ordinal());
spinnerInfoLabel.setText(Res.get("takeOffer.fundsBox.takeOfferSpinnerInfo") + " " + progress);
// unsubscribe when done
if (newState == lastState) {
tradeStateSubscription.unsubscribe();
tradeStateSubscription = null;
}
});
}, 1);
}
}
});
}
private static String getPercentString(int newOrdinal, int lastOrdinal) {
return (int) ((double) newOrdinal / (double) lastOrdinal * 100) + "%";
private void subscribeToProgress(Label spinnerInfoLabel) {
Trade trade = tradeManager.getTrade(offer.getId());
if (trade == null || initProgressSubscription != null) return;
initProgressSubscription = EasyBind.subscribe(trade.initProgressProperty(), newProgress -> {
String progress = (int) (newProgress.doubleValue() * 100.0) + "%";
spinnerInfoLabel.setText(Res.get("takeOffer.fundsBox.takeOfferSpinnerInfo") + " " + progress);
});
}
}