mirror of
https://github.com/haveno-dex/haveno.git
synced 2024-12-22 11:39:29 +00:00
enable cancel button while placing an offer
This commit is contained in:
parent
35f275805b
commit
68a4c21b17
9 changed files with 106 additions and 43 deletions
|
@ -246,6 +246,10 @@ public final class OpenOffer implements Tradable {
|
|||
return state == State.DEACTIVATED;
|
||||
}
|
||||
|
||||
public boolean isCanceled() {
|
||||
return state == State.CANCELED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "OpenOffer{" +
|
||||
|
|
|
@ -218,6 +218,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
|||
this.persistenceManager = persistenceManager;
|
||||
this.signedOfferPersistenceManager = signedOfferPersistenceManager;
|
||||
this.accountAgeWitnessService = accountAgeWitnessService;
|
||||
HavenoUtils.openOfferManager = this;
|
||||
|
||||
this.persistenceManager.initialize(openOffers, "OpenOffers", PersistenceManager.Source.PRIVATE);
|
||||
this.signedOfferPersistenceManager.initialize(signedOffers, "SignedOffers", PersistenceManager.Source.PRIVATE); // arbitrator stores reserve tx for signed offers
|
||||
|
@ -548,11 +549,14 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
|||
latch.countDown();
|
||||
resultHandler.handleResult(transaction);
|
||||
}, (errorMessage) -> {
|
||||
log.warn("Error processing unposted offer {}: {}", openOffer.getId(), errorMessage);
|
||||
onCancelled(openOffer);
|
||||
offer.setErrorMessage(errorMessage);
|
||||
latch.countDown();
|
||||
errorMessageHandler.handleErrorMessage(errorMessage);
|
||||
if (openOffer.isCanceled()) latch.countDown();
|
||||
else {
|
||||
log.warn("Error processing unposted offer {}: {}", openOffer.getId(), errorMessage);
|
||||
doCancel(openOffer);
|
||||
offer.setErrorMessage(errorMessage);
|
||||
latch.countDown();
|
||||
errorMessageHandler.handleErrorMessage(errorMessage);
|
||||
}
|
||||
});
|
||||
HavenoUtils.awaitLatch(latch);
|
||||
}
|
||||
|
@ -612,21 +616,22 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
|||
public void cancelOpenOffer(OpenOffer openOffer,
|
||||
ResultHandler resultHandler,
|
||||
ErrorMessageHandler errorMessageHandler) {
|
||||
log.info("Canceling open offer: {}", openOffer.getId());
|
||||
if (!offersToBeEdited.containsKey(openOffer.getId())) {
|
||||
if (openOffer.isDeactivated()) {
|
||||
ThreadUtils.execute(() -> {
|
||||
onCancelled(openOffer);
|
||||
resultHandler.handleResult();
|
||||
}, THREAD_ID);
|
||||
} else {
|
||||
if (openOffer.isAvailable()) {
|
||||
offerBookService.removeOffer(openOffer.getOffer().getOfferPayload(),
|
||||
() -> {
|
||||
ThreadUtils.execute(() -> { // TODO: this runs off thread and then shows popup when done. should show overlay spinner until done
|
||||
onCancelled(openOffer);
|
||||
ThreadUtils.submitToPool(() -> { // TODO: this runs off thread and then shows popup when done. should show overlay spinner until done
|
||||
doCancel(openOffer);
|
||||
resultHandler.handleResult();
|
||||
}, THREAD_ID);
|
||||
});
|
||||
},
|
||||
errorMessageHandler);
|
||||
} else {
|
||||
ThreadUtils.submitToPool(() -> {
|
||||
doCancel(openOffer);
|
||||
resultHandler.handleResult();
|
||||
});
|
||||
}
|
||||
} else {
|
||||
errorMessageHandler.handleErrorMessage("You can't remove an offer that is currently edited.");
|
||||
|
@ -703,12 +708,12 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
|||
}
|
||||
|
||||
// remove open offer which thaws its key images
|
||||
private void onCancelled(@NotNull OpenOffer openOffer) {
|
||||
private void doCancel(@NotNull OpenOffer openOffer) {
|
||||
Offer offer = openOffer.getOffer();
|
||||
offer.setState(Offer.State.REMOVED);
|
||||
openOffer.setState(OpenOffer.State.CANCELED);
|
||||
removeOpenOffer(openOffer);
|
||||
closedTradableManager.add(openOffer);
|
||||
closedTradableManager.add(openOffer); // TODO: don't add these to closed tradables?
|
||||
xmrWalletService.resetAddressEntriesForOpenOffer(offer.getId());
|
||||
requestPersistence();
|
||||
xmrWalletService.thawOutputs(offer.getOfferPayload().getReserveTxKeyImages());
|
||||
|
@ -785,6 +790,10 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
|||
}
|
||||
}
|
||||
|
||||
public boolean hasOpenOffer(String offerId) {
|
||||
return getOpenOfferById(offerId).isPresent();
|
||||
}
|
||||
|
||||
public Optional<SignedOffer> getSignedOfferById(String offerId) {
|
||||
synchronized (signedOffers) {
|
||||
return signedOffers.stream().filter(e -> e.getOfferId().equals(offerId)).findFirst();
|
||||
|
@ -803,6 +812,10 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
|||
synchronized (openOffers) {
|
||||
openOffers.remove(openOffer);
|
||||
}
|
||||
synchronized (placeOfferProtocols) {
|
||||
PlaceOfferProtocol protocol = placeOfferProtocols.remove(openOffer.getId());
|
||||
if (protocol != null) protocol.cancelOffer();
|
||||
}
|
||||
}
|
||||
|
||||
private void addSignedOffer(SignedOffer signedOffer) {
|
||||
|
@ -856,13 +869,15 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
|||
processUnpostedOffer(openOffers, scheduledOffer, (transaction) -> {
|
||||
latch.countDown();
|
||||
}, errorMessage -> {
|
||||
log.warn("Error processing unposted offer, offerId={}, attempt={}/{}, error={}", scheduledOffer.getId(), scheduledOffer.getNumProcessingAttempts(), MAX_PROCESS_ATTEMPTS, errorMessage);
|
||||
if (scheduledOffer.getNumProcessingAttempts() >= MAX_PROCESS_ATTEMPTS) {
|
||||
log.warn("Offer canceled after {} attempts, offerId={}, error={}", scheduledOffer.getNumProcessingAttempts(), scheduledOffer.getId(), errorMessage);
|
||||
HavenoUtils.havenoSetup.getTopErrorMsg().set("Offer canceled after " + scheduledOffer.getNumProcessingAttempts() + " attempts. Please switch to a better Monero connection and try again.\n\nOffer ID: " + scheduledOffer.getId() + "\nError: " + errorMessage);
|
||||
onCancelled(scheduledOffer);
|
||||
if (!scheduledOffer.isCanceled()) {
|
||||
log.warn("Error processing unposted offer, offerId={}, attempt={}/{}, error={}", scheduledOffer.getId(), scheduledOffer.getNumProcessingAttempts(), MAX_PROCESS_ATTEMPTS, errorMessage);
|
||||
if (scheduledOffer.getNumProcessingAttempts() >= MAX_PROCESS_ATTEMPTS) {
|
||||
log.warn("Offer canceled after {} attempts, offerId={}, error={}", scheduledOffer.getNumProcessingAttempts(), scheduledOffer.getId(), errorMessage);
|
||||
HavenoUtils.havenoSetup.getTopErrorMsg().set("Offer canceled after " + scheduledOffer.getNumProcessingAttempts() + " attempts. Please switch to a better Monero connection and try again.\n\nOffer ID: " + scheduledOffer.getId() + "\nError: " + errorMessage);
|
||||
doCancel(scheduledOffer);
|
||||
}
|
||||
errorMessages.add(errorMessage);
|
||||
}
|
||||
errorMessages.add(errorMessage);
|
||||
latch.countDown();
|
||||
});
|
||||
HavenoUtils.awaitLatch(latch);
|
||||
|
@ -941,7 +956,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
|||
// handle result
|
||||
resultHandler.handleResult(null);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
if (!openOffer.isCanceled()) e.printStackTrace();
|
||||
errorMessageHandler.handleErrorMessage(e.getMessage());
|
||||
}
|
||||
}).start();
|
||||
|
@ -1737,7 +1752,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
|||
} else {
|
||||
|
||||
// cancel and recreate offer
|
||||
onCancelled(openOffer);
|
||||
doCancel(openOffer);
|
||||
Offer updatedOffer = new Offer(openOffer.getOffer().getOfferPayload());
|
||||
updatedOffer.setPriceFeedService(priceFeedService);
|
||||
OpenOffer updatedOpenOffer = new OpenOffer(updatedOffer, openOffer.getTriggerPrice());
|
||||
|
@ -1751,9 +1766,11 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
|||
latch.countDown();
|
||||
if (completeHandler != null) completeHandler.run();
|
||||
}, (errorMessage) -> {
|
||||
log.warn("Error reposting offer {}: {}", updatedOpenOffer.getId(), errorMessage);
|
||||
onCancelled(updatedOpenOffer);
|
||||
updatedOffer.setErrorMessage(errorMessage);
|
||||
if (!updatedOpenOffer.isCanceled()) {
|
||||
log.warn("Error reposting offer {}: {}", updatedOpenOffer.getId(), errorMessage);
|
||||
doCancel(updatedOpenOffer);
|
||||
updatedOffer.setErrorMessage(errorMessage);
|
||||
}
|
||||
latch.countDown();
|
||||
if (completeHandler != null) completeHandler.run();
|
||||
});
|
||||
|
|
|
@ -84,6 +84,10 @@ public class PlaceOfferProtocol {
|
|||
|
||||
taskRunner.run();
|
||||
}
|
||||
|
||||
public void cancelOffer() {
|
||||
handleError("Offer was canceled: " + model.getOpenOffer().getOffer().getId()); // cancel is treated as error for callers to handle
|
||||
}
|
||||
|
||||
// TODO (woodser): switch to fluent
|
||||
public void handleSignOfferResponse(SignOfferResponse response, NodeAddress sender) {
|
||||
|
@ -147,9 +151,11 @@ public class PlaceOfferProtocol {
|
|||
private void handleError(String errorMessage) {
|
||||
if (timeoutTimer != null) {
|
||||
taskRunner.cancel();
|
||||
log.error(errorMessage);
|
||||
if (!model.getOpenOffer().isCanceled()) {
|
||||
log.error(errorMessage);
|
||||
model.getOpenOffer().getOffer().setErrorMessage(errorMessage);
|
||||
}
|
||||
stopTimeoutTimer();
|
||||
model.getOpenOffer().getOffer().setErrorMessage(errorMessage);
|
||||
errorMessageHandler.handleErrorMessage(errorMessage);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -66,7 +66,7 @@ public class MakerReserveOfferFunds extends Task<PlaceOfferModel> {
|
|||
synchronized (XmrWalletService.WALLET_LOCK) {
|
||||
|
||||
// reset protocol timeout
|
||||
verifyOpen();
|
||||
verifyScheduled();
|
||||
model.getProtocol().startTimeoutTimer();
|
||||
|
||||
// collect relevant info
|
||||
|
@ -92,7 +92,7 @@ public class MakerReserveOfferFunds extends Task<PlaceOfferModel> {
|
|||
}
|
||||
|
||||
// verify still open
|
||||
verifyOpen();
|
||||
verifyScheduled();
|
||||
if (reserveTx != null) break;
|
||||
}
|
||||
}
|
||||
|
@ -119,11 +119,7 @@ public class MakerReserveOfferFunds extends Task<PlaceOfferModel> {
|
|||
}
|
||||
}
|
||||
|
||||
public void verifyOpen() {
|
||||
if (!isOpen()) throw new RuntimeException("Offer " + model.getOpenOffer().getOffer().getId() + " is no longer open");
|
||||
}
|
||||
|
||||
public boolean isOpen() {
|
||||
return model.getOpenOfferManager().getOpenOfferById(model.getOpenOffer().getId()).isPresent();
|
||||
public void verifyScheduled() {
|
||||
if (!model.getOpenOffer().isScheduled()) throw new RuntimeException("Offer " + model.getOpenOffer().getOffer().getId() + " is canceled");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -137,6 +137,12 @@ public class MakerSendSignOfferRequest extends Task<PlaceOfferModel> {
|
|||
log.warn("Arbitrator unavailable: address={}, error={}", arbitratorNodeAddress, errorMessage);
|
||||
excludedArbitrators.add(arbitratorNodeAddress);
|
||||
|
||||
// check if offer still scheduled
|
||||
if (!model.getOpenOffer().isScheduled()) {
|
||||
errorMessageHandler.handleErrorMessage("Offer is no longer scheduled, offerId=" + model.getOpenOffer().getId());
|
||||
return;
|
||||
}
|
||||
|
||||
// get alternative arbitrator
|
||||
Arbitrator altArbitrator = DisputeAgentSelection.getRandomArbitrator(model.getArbitratorManager(), excludedArbitrators);
|
||||
if (altArbitrator == null) {
|
||||
|
|
|
@ -30,6 +30,7 @@ import haveno.common.crypto.Sig;
|
|||
import haveno.common.util.Utilities;
|
||||
import haveno.core.app.HavenoSetup;
|
||||
import haveno.core.offer.OfferPayload;
|
||||
import haveno.core.offer.OpenOfferManager;
|
||||
import haveno.core.support.dispute.arbitration.ArbitrationManager;
|
||||
import haveno.core.support.dispute.arbitration.arbitrator.Arbitrator;
|
||||
import haveno.core.trade.messages.PaymentReceivedMessage;
|
||||
|
@ -97,9 +98,11 @@ public class HavenoUtils {
|
|||
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");
|
||||
|
||||
// TODO: better way to share references?
|
||||
public static HavenoSetup havenoSetup;
|
||||
public static ArbitrationManager arbitrationManager; // TODO: better way to share references?
|
||||
public static ArbitrationManager arbitrationManager;
|
||||
public static XmrWalletService xmrWalletService;
|
||||
public static OpenOfferManager openOfferManager;
|
||||
|
||||
public static boolean isSeedNode() {
|
||||
return havenoSetup == null;
|
||||
|
|
|
@ -348,9 +348,12 @@ public abstract class MutableOfferView<M extends MutableOfferViewModel<?>> exten
|
|||
if (model.getDataModel().canPlaceOffer()) {
|
||||
Offer offer = model.createAndGetOffer();
|
||||
if (!DevEnv.isDevMode()) {
|
||||
offerDetailsWindow.onPlaceOffer(() ->
|
||||
model.onPlaceOffer(offer, offerDetailsWindow::hide))
|
||||
.show(offer);
|
||||
offerDetailsWindow.onPlaceOffer(() -> {
|
||||
model.onPlaceOffer(offer, offerDetailsWindow::hide);
|
||||
}).show(offer);
|
||||
offerDetailsWindow.onClose(() -> {
|
||||
model.onCancelOffer(null, null);
|
||||
});
|
||||
} else {
|
||||
balanceSubscription.unsubscribe();
|
||||
model.onPlaceOffer(offer, () -> {
|
||||
|
|
|
@ -21,6 +21,8 @@ import com.google.inject.Inject;
|
|||
import com.google.inject.name.Named;
|
||||
import haveno.common.UserThread;
|
||||
import haveno.common.app.DevEnv;
|
||||
import haveno.common.handlers.ErrorMessageHandler;
|
||||
import haveno.common.handlers.ResultHandler;
|
||||
import haveno.common.util.MathUtils;
|
||||
import haveno.core.account.witness.AccountAgeWitnessService;
|
||||
import haveno.core.locale.CurrencyUtil;
|
||||
|
@ -34,6 +36,8 @@ import haveno.core.offer.Offer;
|
|||
import haveno.core.offer.OfferDirection;
|
||||
import haveno.core.offer.OfferRestrictions;
|
||||
import haveno.core.offer.OfferUtil;
|
||||
import haveno.core.offer.OpenOffer;
|
||||
import haveno.core.offer.OpenOfferManager;
|
||||
import haveno.core.payment.PaymentAccount;
|
||||
import haveno.core.payment.payload.PaymentMethod;
|
||||
import haveno.core.payment.validation.FiatVolumeValidator;
|
||||
|
@ -68,6 +72,7 @@ import java.math.BigInteger;
|
|||
import java.text.SimpleDateFormat;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import static javafx.beans.binding.Bindings.createStringBinding;
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
|
@ -617,7 +622,6 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
|
|||
updateButtonDisableState();
|
||||
updateSpinnerInfo();
|
||||
resultHandler.run();
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -625,6 +629,30 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
|
|||
updateSpinnerInfo();
|
||||
}
|
||||
|
||||
public void onCancelOffer(ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
|
||||
createOfferRequested = false;
|
||||
OpenOfferManager openOfferManager = HavenoUtils.openOfferManager;
|
||||
Optional<OpenOffer> openOffer = openOfferManager.getOpenOfferById(offer.getId());
|
||||
if (openOffer.isPresent()) {
|
||||
openOfferManager.cancelOpenOffer(openOffer.get(), () -> {
|
||||
UserThread.execute(() -> {
|
||||
updateButtonDisableState();
|
||||
updateSpinnerInfo();
|
||||
});
|
||||
if (resultHandler != null) resultHandler.handleResult();
|
||||
}, errorMessage -> {
|
||||
UserThread.execute(() -> {
|
||||
updateButtonDisableState();
|
||||
updateSpinnerInfo();
|
||||
if (errorMessageHandler != null) errorMessageHandler.handleErrorMessage(errorMessage);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
if (resultHandler != null) resultHandler.handleResult();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public void onPaymentAccountSelected(PaymentAccount paymentAccount) {
|
||||
dataModel.onPaymentAccountSelected(paymentAccount);
|
||||
if (amount.get() != null)
|
||||
|
|
|
@ -417,7 +417,7 @@ public class OfferDetailsWindow extends Overlay<OfferDetailsWindow> {
|
|||
button.setOnAction(e -> {
|
||||
if (GUIUtil.canCreateOrTakeOfferOrShowPopup(user, navigation)) {
|
||||
button.setDisable(true);
|
||||
cancelButton.setDisable(true);
|
||||
cancelButton.setDisable(isPlaceOffer ? false : true); // TODO: enable cancel button for taking an offer until messages sent
|
||||
// temporarily disabled due to high CPU usage (per issue #4649)
|
||||
// busyAnimation.play();
|
||||
if (isPlaceOffer) {
|
||||
|
|
Loading…
Reference in a new issue