mirror of
https://github.com/bisq-network/bisq.git
synced 2024-11-19 09:52:23 +01:00
Handle errors at take offer screen
This commit is contained in:
parent
9a0e174099
commit
e6744fb727
@ -21,13 +21,13 @@ import io.bitsquare.btc.AddressEntry;
|
||||
import io.bitsquare.btc.FeePolicy;
|
||||
import io.bitsquare.btc.WalletService;
|
||||
import io.bitsquare.btc.listeners.BalanceListener;
|
||||
import io.bitsquare.offer.Offer;
|
||||
import io.bitsquare.persistence.Persistence;
|
||||
import io.bitsquare.user.Preferences;
|
||||
import io.bitsquare.trade.Trade;
|
||||
import io.bitsquare.trade.TradeManager;
|
||||
import io.bitsquare.common.viewfx.model.Activatable;
|
||||
import io.bitsquare.common.viewfx.model.DataModel;
|
||||
import io.bitsquare.offer.Offer;
|
||||
import io.bitsquare.persistence.Persistence;
|
||||
import io.bitsquare.trade.Trade;
|
||||
import io.bitsquare.trade.TradeManager;
|
||||
import io.bitsquare.user.Preferences;
|
||||
|
||||
import org.bitcoinj.core.Coin;
|
||||
import org.bitcoinj.utils.ExchangeRate;
|
||||
@ -63,11 +63,8 @@ class TakeOfferDataModel implements Activatable, DataModel {
|
||||
private Offer offer;
|
||||
private AddressEntry addressEntry;
|
||||
|
||||
final StringProperty requestTakeOfferErrorMessage = new SimpleStringProperty();
|
||||
final StringProperty transactionId = new SimpleStringProperty();
|
||||
final StringProperty btcCode = new SimpleStringProperty();
|
||||
|
||||
final BooleanProperty requestTakeOfferSuccess = new SimpleBooleanProperty();
|
||||
final BooleanProperty isWalletFunded = new SimpleBooleanProperty();
|
||||
final BooleanProperty useMBTC = new SimpleBooleanProperty();
|
||||
|
||||
@ -78,8 +75,6 @@ class TakeOfferDataModel implements Activatable, DataModel {
|
||||
final ObjectProperty<Coin> offerFeeAsCoin = new SimpleObjectProperty<>();
|
||||
final ObjectProperty<Coin> networkFeeAsCoin = new SimpleObjectProperty<>();
|
||||
|
||||
final ObjectProperty<Offer.State> offerIsAvailable = new SimpleObjectProperty<>(Offer.State.UNKNOWN);
|
||||
|
||||
@Inject
|
||||
public TakeOfferDataModel(TradeManager tradeManager,
|
||||
WalletService walletService,
|
||||
@ -130,64 +125,11 @@ class TakeOfferDataModel implements Activatable, DataModel {
|
||||
});
|
||||
updateBalance(walletService.getBalanceForAddress(addressEntry.getAddress()));
|
||||
|
||||
offer.stateProperty().addListener((observable, oldValue, newValue) -> {
|
||||
offerIsAvailable.set(newValue);
|
||||
});
|
||||
tradeManager.checkOfferAvailability(offer);
|
||||
}
|
||||
|
||||
void takeOffer() {
|
||||
final Trade trade = tradeManager.takeOffer(amountAsCoin.get(), offer);
|
||||
trade.stateProperty().addListener((ov, oldValue, newValue) -> {
|
||||
log.debug("trade state = " + newValue);
|
||||
String errorMessage = "";
|
||||
if (newValue.getErrorMessage() != null)
|
||||
errorMessage = "\nError message: " + newValue.getErrorMessage();
|
||||
|
||||
switch (newValue) {
|
||||
case OPEN:
|
||||
break;
|
||||
case OFFERER_ACCEPTED:
|
||||
break;
|
||||
case OFFERER_REJECTED:
|
||||
requestTakeOfferErrorMessage.set("Take offer request got rejected. Maybe another trader has taken the offer in the meantime.");
|
||||
break;
|
||||
case TAKE_OFFER_FEE_TX_CREATED:
|
||||
break;
|
||||
case DEPOSIT_PUBLISHED:
|
||||
case DEPOSIT_CONFIRMED:
|
||||
// TODO Check why DEPOSIT_CONFIRMED can happen, refactor state handling
|
||||
// TODO null pointer happened here!
|
||||
if (trade.getDepositTx() != null) {
|
||||
transactionId.set(trade.getDepositTx().getHashAsString());
|
||||
requestTakeOfferSuccess.set(true);
|
||||
}
|
||||
else {
|
||||
log.warn("trade.getDepositTx() = null. at trade state " + newValue +
|
||||
" That should not happen and needs more investigation why it can happen.");
|
||||
}
|
||||
break;
|
||||
case FIAT_PAYMENT_STARTED:
|
||||
break;
|
||||
case TAKE_OFFER_FEE_PUBLISH_FAILED:
|
||||
requestTakeOfferErrorMessage.set("An error occurred when paying the trade fee." + errorMessage);
|
||||
break;
|
||||
case MESSAGE_SENDING_FAILED:
|
||||
requestTakeOfferErrorMessage.set("An error occurred when sending a message to the offerer. Maybe there are connection problems. " +
|
||||
"Please try later again." + errorMessage);
|
||||
break;
|
||||
case PAYOUT_PUBLISHED:
|
||||
break;
|
||||
case FAULT:
|
||||
requestTakeOfferErrorMessage.set(errorMessage);
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
log.error("Unhandled trade state: " + newValue);
|
||||
break;
|
||||
}
|
||||
});
|
||||
Trade takeOffer() {
|
||||
return tradeManager.takeOffer(amountAsCoin.get(), offer);
|
||||
}
|
||||
|
||||
void calculateVolume() {
|
||||
|
@ -136,7 +136,7 @@
|
||||
</ProgressIndicator>
|
||||
<Button fx:id="showPaymentInfoScreenButton" text="%takeOffer.amountPriceBox.next" id="show-details-button"
|
||||
GridPane.columnIndex="1" GridPane.rowIndex="3" defaultButton="true" visible="false"
|
||||
onAction="#onShowPayFundsScreen">
|
||||
onAction="#onShowPaymentScreen">
|
||||
<GridPane.margin>
|
||||
<Insets top="15.0"/>
|
||||
</GridPane.margin>
|
||||
|
@ -18,6 +18,8 @@
|
||||
package io.bitsquare.gui.main.trade.takeoffer;
|
||||
|
||||
|
||||
import io.bitsquare.common.viewfx.view.ActivatableViewAndModel;
|
||||
import io.bitsquare.common.viewfx.view.FxmlView;
|
||||
import io.bitsquare.gui.Navigation;
|
||||
import io.bitsquare.gui.OverlayManager;
|
||||
import io.bitsquare.gui.components.AddressTextField;
|
||||
@ -36,8 +38,6 @@ import io.bitsquare.gui.util.ImageUtil;
|
||||
import io.bitsquare.locale.BSResources;
|
||||
import io.bitsquare.offer.Direction;
|
||||
import io.bitsquare.offer.Offer;
|
||||
import io.bitsquare.common.viewfx.view.ActivatableViewAndModel;
|
||||
import io.bitsquare.common.viewfx.view.FxmlView;
|
||||
|
||||
import org.bitcoinj.core.Coin;
|
||||
|
||||
@ -47,7 +47,6 @@ import java.util.List;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.value.ChangeListener;
|
||||
import javafx.event.ActionEvent;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.geometry.HPos;
|
||||
@ -95,7 +94,6 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
|
||||
|
||||
private final Navigation navigation;
|
||||
private final OverlayManager overlayManager;
|
||||
private ChangeListener<Offer.State> offerIsAvailableChangeListener;
|
||||
private TradeView.CloseHandler closeHandler;
|
||||
|
||||
@Inject
|
||||
@ -115,8 +113,6 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
|
||||
|
||||
@Override
|
||||
protected void doDeactivate() {
|
||||
if (offerIsAvailableChangeListener != null)
|
||||
model.offerIsAvailable.removeListener(offerIsAvailableChangeListener);
|
||||
}
|
||||
|
||||
public void initWithData(Direction direction, Coin amount, Offer offer) {
|
||||
@ -127,13 +123,10 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
|
||||
else
|
||||
imageView.setId("image-sell-large");
|
||||
|
||||
priceDescriptionLabel.setText(BSResources.get("takeOffer.amountPriceBox.priceDescription",
|
||||
model.getFiatCode()));
|
||||
volumeDescriptionLabel.setText(BSResources.get("takeOffer.amountPriceBox.volumeDescription",
|
||||
model.getFiatCode()));
|
||||
priceDescriptionLabel.setText(BSResources.get("takeOffer.amountPriceBox.priceDescription", model.getFiatCode()));
|
||||
volumeDescriptionLabel.setText(BSResources.get("takeOffer.amountPriceBox.volumeDescription", model.getFiatCode()));
|
||||
|
||||
balanceTextField.setup(model.getWalletService(), model.address.get(),
|
||||
model.getFormatter());
|
||||
balanceTextField.setup(model.getWalletService(), model.address.get(), model.getFormatter());
|
||||
|
||||
buyLabel.setText(model.getDirectionLabel());
|
||||
amountRangeTextField.setText(model.getAmountRange());
|
||||
@ -148,113 +141,23 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
|
||||
acceptedLanguagesTextField.setText(model.getAcceptedLanguages());
|
||||
acceptedArbitratorsTextField.setText(model.getAcceptedArbitrators());
|
||||
|
||||
offerIsAvailableChangeListener = (ov, oldValue, newValue) -> handleOfferIsAvailableState(newValue);
|
||||
model.offerIsAvailable.addListener(offerIsAvailableChangeListener);
|
||||
handleOfferIsAvailableState(model.offerIsAvailable.get());
|
||||
showCheckAvailabilityScreen();
|
||||
}
|
||||
|
||||
public void setCloseHandler(TradeView.CloseHandler closeHandler) {
|
||||
this.closeHandler = closeHandler;
|
||||
}
|
||||
|
||||
private void handleOfferIsAvailableState(Offer.State state) {
|
||||
switch (state) {
|
||||
case UNKNOWN:
|
||||
break;
|
||||
case AVAILABLE:
|
||||
isOfferAvailableLabel.setVisible(false);
|
||||
isOfferAvailableLabel.setManaged(false);
|
||||
isOfferAvailableProgressIndicator.setProgress(0);
|
||||
isOfferAvailableProgressIndicator.setVisible(false);
|
||||
isOfferAvailableProgressIndicator.setManaged(false);
|
||||
|
||||
// In case of returning to a canceled or failed take offer request and if trade wallet is sufficient funded we display directly the payment
|
||||
// screen
|
||||
if (!model.isTakeOfferButtonDisabled.get()) {
|
||||
showPayFundsScreen();
|
||||
showPaymentInfoScreenButton.setVisible(false);
|
||||
}
|
||||
else {
|
||||
showPaymentInfoScreenButton.setVisible(true);
|
||||
}
|
||||
break;
|
||||
case OFFERER_OFFLINE:
|
||||
Popups.openWarningPopup("You cannot take that offer", "The offerer is offline.");
|
||||
Platform.runLater(this::close);
|
||||
break;
|
||||
case NOT_AVAILABLE:
|
||||
Popups.openWarningPopup("You cannot take that offer", "The offer was already taken by another trader.");
|
||||
Platform.runLater(this::close);
|
||||
break;
|
||||
case FAULT:
|
||||
Popups.openWarningPopup("You cannot take that offer", "The check for the offer availability failed.");
|
||||
Platform.runLater(this::close);
|
||||
break;
|
||||
case REMOVED:
|
||||
Popups.openWarningPopup("You cannot take that offer", "The offerer has been removed in the meantime.");
|
||||
Platform.runLater(this::close);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@FXML
|
||||
void onTakeOffer() {
|
||||
model.takeOffer();
|
||||
}
|
||||
|
||||
@FXML
|
||||
void onShowPayFundsScreen() {
|
||||
showPayFundsScreen();
|
||||
void onShowPaymentScreen() {
|
||||
model.onShowPaymentScreen();
|
||||
}
|
||||
|
||||
private void showPayFundsScreen() {
|
||||
// TODO deactivate for testing the moment
|
||||
/* if (model.displaySecurityDepositInfo()) {
|
||||
overlayManager.blurContent();
|
||||
List<Action> actions = new ArrayList<>();
|
||||
actions.add(new AbstractAction(BSResources.get("shared.close")) {
|
||||
@Override
|
||||
public void handle(ActionEvent actionEvent) {
|
||||
getProperties().put("type", "CLOSE");
|
||||
Dialog.Actions.CLOSE.handle(actionEvent);
|
||||
}
|
||||
});
|
||||
Popups.openInfoPopup("To ensure that both traders behave fair they need to pay a security deposit.",
|
||||
"The deposit will stay in your local trading wallet until the offer gets accepted by another trader. " +
|
||||
"\nIt will be refunded to you after the trade has successfully completed.",
|
||||
actions);
|
||||
|
||||
model.securityDepositInfoDisplayed();
|
||||
}*/
|
||||
|
||||
priceAmountPane.setInactive();
|
||||
|
||||
showPaymentInfoScreenButton.setVisible(false);
|
||||
|
||||
payFundsPane.setVisible(true);
|
||||
totalToPayLabel.setVisible(true);
|
||||
totalToPayInfoIconLabel.setVisible(true);
|
||||
totalToPayTextField.setVisible(true);
|
||||
addressLabel.setVisible(true);
|
||||
addressTextField.setVisible(true);
|
||||
balanceLabel.setVisible(true);
|
||||
balanceTextField.setVisible(true);
|
||||
fundsBoxInfoDisplay.setVisible(true);
|
||||
|
||||
// for irc demo
|
||||
//showAdvancedSettingsButton.setVisible(true);
|
||||
showAdvancedSettingsButton.setManaged(false);
|
||||
|
||||
if (expand == null) {
|
||||
expand = ImageUtil.getImageViewById(ImageUtil.EXPAND);
|
||||
collapse = ImageUtil.getImageViewById(ImageUtil.COLLAPSE);
|
||||
}
|
||||
showAdvancedSettingsButton.setGraphic(expand);
|
||||
|
||||
setupTotalToPayInfoIconLabel();
|
||||
|
||||
model.onShowPayFundsScreen();
|
||||
}
|
||||
|
||||
@FXML
|
||||
void onToggleShowAdvancedSettings() {
|
||||
@ -300,6 +203,23 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
|
||||
amountTextField.setText(model.amount.get());
|
||||
});
|
||||
|
||||
model.state.addListener((ov, oldValue, newValue) -> {
|
||||
switch (newValue) {
|
||||
case CHECK_AVAILABILITY:
|
||||
showCheckAvailabilityScreen();
|
||||
break;
|
||||
case AMOUNT_SCREEN:
|
||||
showAmountScreen();
|
||||
break;
|
||||
case PAYMENT_SCREEN:
|
||||
setupPaymentScreen();
|
||||
break;
|
||||
case DETAILS_SCREEN:
|
||||
showDetailsScreen();
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
// warnings
|
||||
model.showWarningInvalidBtcDecimalPlaces.addListener((o, oldValue, newValue) -> {
|
||||
if (newValue) {
|
||||
@ -309,12 +229,12 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
|
||||
}
|
||||
});
|
||||
|
||||
model.requestTakeOfferErrorMessage.addListener((o, oldValue, newValue) -> {
|
||||
|
||||
model.errorMessage.addListener((o, oldValue, newValue) -> {
|
||||
if (newValue != null) {
|
||||
Popups.openErrorPopup(BSResources.get("shared.error"),
|
||||
BSResources.get("takeOffer.amountPriceBox.error.message",
|
||||
model.requestTakeOfferErrorMessage.get()));
|
||||
Popups.openErrorPopup(BSResources.get("shared.error"), BSResources.get("takeOffer.error.message", model.errorMessage.get()));
|
||||
Popups.removeBlurContent();
|
||||
Platform.runLater(this::close);
|
||||
}
|
||||
});
|
||||
|
||||
@ -364,8 +284,7 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
|
||||
amountTextField.validationResultProperty().bind(model.amountValidationResult);
|
||||
|
||||
// buttons
|
||||
takeOfferButton.visibleProperty().bind(model.isTakeOfferButtonVisible);
|
||||
takeOfferButton.disableProperty().bind(model.isTakeOfferButtonDisabled);
|
||||
takeOfferButton.disableProperty().bind(model.takeOfferButtonDisabled);
|
||||
|
||||
takeOfferSpinnerInfoLabel.visibleProperty().bind(model.isTakeOfferSpinnerVisible);
|
||||
|
||||
@ -375,6 +294,68 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private void showCheckAvailabilityScreen() {
|
||||
|
||||
}
|
||||
|
||||
private void showAmountScreen() {
|
||||
isOfferAvailableLabel.setVisible(false);
|
||||
isOfferAvailableLabel.setManaged(false);
|
||||
isOfferAvailableProgressIndicator.setProgress(0);
|
||||
isOfferAvailableProgressIndicator.setVisible(false);
|
||||
isOfferAvailableProgressIndicator.setManaged(false);
|
||||
|
||||
showPaymentInfoScreenButton.setVisible(true);
|
||||
}
|
||||
|
||||
private void setupPaymentScreen() {
|
||||
// TODO deactivate for testing the moment
|
||||
/* if (model.displaySecurityDepositInfo()) {
|
||||
overlayManager.blurContent();
|
||||
List<Action> actions = new ArrayList<>();
|
||||
actions.add(new AbstractAction(BSResources.get("shared.close")) {
|
||||
@Override
|
||||
public void handle(ActionEvent actionEvent) {
|
||||
getProperties().put("type", "CLOSE");
|
||||
Dialog.Actions.CLOSE.handle(actionEvent);
|
||||
}
|
||||
});
|
||||
Popups.openInfoPopup("To ensure that both traders behave fair they need to pay a security deposit.",
|
||||
"The deposit will stay in your local trading wallet until the offer gets accepted by another trader. " +
|
||||
"\nIt will be refunded to you after the trade has successfully completed.",
|
||||
actions);
|
||||
|
||||
model.securityDepositInfoDisplayed();
|
||||
}*/
|
||||
|
||||
priceAmountPane.setInactive();
|
||||
takeOfferButton.setVisible(true);
|
||||
showPaymentInfoScreenButton.setVisible(false);
|
||||
|
||||
payFundsPane.setVisible(true);
|
||||
totalToPayLabel.setVisible(true);
|
||||
totalToPayInfoIconLabel.setVisible(true);
|
||||
totalToPayTextField.setVisible(true);
|
||||
addressLabel.setVisible(true);
|
||||
addressTextField.setVisible(true);
|
||||
balanceLabel.setVisible(true);
|
||||
balanceTextField.setVisible(true);
|
||||
fundsBoxInfoDisplay.setVisible(true);
|
||||
|
||||
// for irc demo
|
||||
//showAdvancedSettingsButton.setVisible(true);
|
||||
showAdvancedSettingsButton.setManaged(false);
|
||||
|
||||
if (expand == null) {
|
||||
expand = ImageUtil.getImageViewById(ImageUtil.EXPAND);
|
||||
collapse = ImageUtil.getImageViewById(ImageUtil.COLLAPSE);
|
||||
}
|
||||
showAdvancedSettingsButton.setGraphic(expand);
|
||||
|
||||
setupTotalToPayInfoIconLabel();
|
||||
}
|
||||
|
||||
private void showDetailsScreen() {
|
||||
payFundsPane.setInactive();
|
||||
|
||||
@ -386,6 +367,7 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
|
||||
toggleDetailsScreen(true);
|
||||
}
|
||||
|
||||
|
||||
private void hideDetailsScreen() {
|
||||
payFundsPane.setActive();
|
||||
scrollPane.setVbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
|
||||
|
@ -18,14 +18,15 @@
|
||||
package io.bitsquare.gui.main.trade.takeoffer;
|
||||
|
||||
import io.bitsquare.btc.WalletService;
|
||||
import io.bitsquare.common.viewfx.model.ActivatableWithDataModel;
|
||||
import io.bitsquare.common.viewfx.model.ViewModel;
|
||||
import io.bitsquare.gui.util.BSFormatter;
|
||||
import io.bitsquare.gui.util.validation.BtcValidator;
|
||||
import io.bitsquare.gui.util.validation.InputValidator;
|
||||
import io.bitsquare.locale.BSResources;
|
||||
import io.bitsquare.offer.Direction;
|
||||
import io.bitsquare.offer.Offer;
|
||||
import io.bitsquare.common.viewfx.model.ActivatableWithDataModel;
|
||||
import io.bitsquare.common.viewfx.model.ViewModel;
|
||||
import io.bitsquare.trade.Trade;
|
||||
|
||||
import org.bitcoinj.core.Address;
|
||||
import org.bitcoinj.core.Coin;
|
||||
@ -47,6 +48,13 @@ import static javafx.beans.binding.Bindings.createStringBinding;
|
||||
class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> implements ViewModel {
|
||||
private static final Logger log = LoggerFactory.getLogger(TakeOfferViewModel.class);
|
||||
|
||||
public static enum State {
|
||||
CHECK_AVAILABILITY,
|
||||
AMOUNT_SCREEN,
|
||||
PAYMENT_SCREEN,
|
||||
DETAILS_SCREEN
|
||||
}
|
||||
|
||||
private String fiatCode;
|
||||
private String amountRange;
|
||||
private String price;
|
||||
@ -62,6 +70,7 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
|
||||
|
||||
// Needed for the addressTextField
|
||||
final ObjectProperty<Address> address = new SimpleObjectProperty<>();
|
||||
final ObjectProperty<State> state = new SimpleObjectProperty<>(State.CHECK_AVAILABILITY);
|
||||
|
||||
private final BtcValidator btcValidator;
|
||||
private final BSFormatter formatter;
|
||||
@ -75,22 +84,21 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
|
||||
final StringProperty securityDeposit = new SimpleStringProperty();
|
||||
final StringProperty totalToPay = new SimpleStringProperty();
|
||||
final StringProperty transactionId = new SimpleStringProperty();
|
||||
final StringProperty requestTakeOfferErrorMessage = new SimpleStringProperty();
|
||||
final StringProperty errorMessage = new SimpleStringProperty();
|
||||
final StringProperty btcCode = new SimpleStringProperty();
|
||||
|
||||
|
||||
final BooleanProperty isTakeOfferButtonVisible = new SimpleBooleanProperty(false);
|
||||
final BooleanProperty isTakeOfferButtonDisabled = new SimpleBooleanProperty(true);
|
||||
final BooleanProperty takeOfferButtonDisabled = new SimpleBooleanProperty(false);
|
||||
final BooleanProperty isTakeOfferSpinnerVisible = new SimpleBooleanProperty(false);
|
||||
final BooleanProperty showWarningInvalidBtcDecimalPlaces = new SimpleBooleanProperty();
|
||||
final BooleanProperty showTransactionPublishedScreen = new SimpleBooleanProperty();
|
||||
final ObjectProperty<Offer.State> offerIsAvailable = new SimpleObjectProperty<>(Offer.State.UNKNOWN);
|
||||
|
||||
final ObjectProperty<InputValidator.ValidationResult> amountValidationResult = new SimpleObjectProperty<>();
|
||||
|
||||
// Needed for the addressTextField
|
||||
final ObjectProperty<Coin> totalToPayAsCoin = new SimpleObjectProperty<>();
|
||||
|
||||
private boolean takeOfferRequested;
|
||||
private boolean isAmountAndPriceValidAndWalletFunded;
|
||||
|
||||
@Inject
|
||||
public TakeOfferViewModel(TakeOfferDataModel dataModel, BtcValidator btcValidator, BSFormatter formatter) {
|
||||
@ -104,6 +112,7 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
|
||||
|
||||
setupBindings();
|
||||
setupListeners();
|
||||
applyTakeOfferRequestResult(false);
|
||||
}
|
||||
|
||||
// setOfferBookFilter is a one time call
|
||||
@ -119,8 +128,6 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
|
||||
BSResources.get("takeOffer.validation.amountSmallerThanMinAmount")));
|
||||
}
|
||||
|
||||
updateButtonDisableState();
|
||||
|
||||
//model.volumeAsFiat.set(offer.getVolumeByAmount(model.amountAsCoin.get()));
|
||||
|
||||
amountRange = formatter.formatCoinWithCode(offer.getMinAmount()) + " - " +
|
||||
@ -139,16 +146,120 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
|
||||
bankAccountType = BSResources.get(offer.getFiatAccountType().toString());
|
||||
bankAccountCurrency = BSResources.get(offer.getCurrency().getDisplayName());
|
||||
bankAccountCounty = BSResources.get(offer.getBankAccountCountry().getName());
|
||||
|
||||
offer.stateProperty().addListener((ov, oldValue, newValue) -> {
|
||||
log.debug("offer state = " + newValue);
|
||||
|
||||
switch (newValue) {
|
||||
case UNKNOWN:
|
||||
break;
|
||||
case AVAILABLE:
|
||||
state.set(State.AMOUNT_SCREEN);
|
||||
break;
|
||||
case OFFERER_OFFLINE:
|
||||
if (takeOfferRequested)
|
||||
errorMessage.set("Take offer request failed because offerer is not online anymore.");
|
||||
else
|
||||
errorMessage.set("You cannot take that offer because the offerer is offline.");
|
||||
takeOfferRequested = false;
|
||||
break;
|
||||
case NOT_AVAILABLE:
|
||||
if (takeOfferRequested)
|
||||
errorMessage.set("Take offer request failed because offer is not available anymore. " +
|
||||
"Maybe another trader has taken the offer in the meantime.");
|
||||
else
|
||||
errorMessage.set("You cannot take that offer because the offer was already taken by another trader.");
|
||||
takeOfferRequested = false;
|
||||
break;
|
||||
case FAULT:
|
||||
if (takeOfferRequested)
|
||||
errorMessage.set("Take offer request failed.");
|
||||
else
|
||||
errorMessage.set("The check for the offer availability failed.");
|
||||
|
||||
takeOfferRequested = false;
|
||||
break;
|
||||
case REMOVED:
|
||||
if (!takeOfferRequested)
|
||||
errorMessage.set("You cannot take that offer because the offer has been removed in the meantime.");
|
||||
|
||||
takeOfferRequested = false;
|
||||
break;
|
||||
default:
|
||||
log.error("Unhandled offer state: " + newValue);
|
||||
break;
|
||||
}
|
||||
|
||||
if (errorMessage.get() != null) {
|
||||
isTakeOfferSpinnerVisible.set(false);
|
||||
}
|
||||
|
||||
evaluateState();
|
||||
});
|
||||
|
||||
evaluateState();
|
||||
}
|
||||
|
||||
void takeOffer() {
|
||||
dataModel.requestTakeOfferErrorMessage.set(null);
|
||||
dataModel.requestTakeOfferSuccess.set(false);
|
||||
errorMessage.set(null);
|
||||
takeOfferRequested = true;
|
||||
applyTakeOfferRequestResult(false);
|
||||
|
||||
isTakeOfferButtonDisabled.set(true);
|
||||
isTakeOfferSpinnerVisible.set(true);
|
||||
|
||||
dataModel.takeOffer();
|
||||
Trade trade = dataModel.takeOffer();
|
||||
|
||||
trade.stateProperty().addListener((ov, oldValue, newValue) -> {
|
||||
log.debug("trade state = " + newValue);
|
||||
String msg = "";
|
||||
if (newValue.getErrorMessage() != null)
|
||||
msg = "\nError message: " + newValue.getErrorMessage();
|
||||
|
||||
switch (newValue) {
|
||||
case OPEN:
|
||||
break;
|
||||
case OFFERER_ACCEPTED:
|
||||
break;
|
||||
case OFFERER_REJECTED:
|
||||
errorMessage.set("Your take offer request got rejected. Maybe another trader has taken the offer in the meantime.");
|
||||
takeOfferRequested = false;
|
||||
break;
|
||||
case TAKE_OFFER_FEE_TX_CREATED:
|
||||
break;
|
||||
case DEPOSIT_PUBLISHED:
|
||||
case DEPOSIT_CONFIRMED:
|
||||
transactionId.set(trade.getDepositTx().getHashAsString());
|
||||
applyTakeOfferRequestResult(true);
|
||||
break;
|
||||
case FIAT_PAYMENT_STARTED:
|
||||
break;
|
||||
case TAKE_OFFER_FEE_PUBLISH_FAILED:
|
||||
errorMessage.set("An error occurred when paying the trade fee." + msg);
|
||||
takeOfferRequested = false;
|
||||
break;
|
||||
case MESSAGE_SENDING_FAILED:
|
||||
errorMessage.set("An error occurred when sending a message to the offerer. Maybe there are connection problems. " +
|
||||
"Please try later again." + msg);
|
||||
takeOfferRequested = false;
|
||||
break;
|
||||
case PAYOUT_PUBLISHED:
|
||||
break;
|
||||
case FAULT:
|
||||
errorMessage.set(msg);
|
||||
takeOfferRequested = false;
|
||||
break;
|
||||
default:
|
||||
log.error("Unhandled trade state: " + newValue);
|
||||
break;
|
||||
}
|
||||
|
||||
if (errorMessage.get() != null) {
|
||||
isAmountAndPriceValidAndWalletFunded = false;
|
||||
isTakeOfferSpinnerVisible.set(false);
|
||||
}
|
||||
});
|
||||
|
||||
evaluateState();
|
||||
}
|
||||
|
||||
void securityDepositInfoDisplayed() {
|
||||
@ -156,8 +267,8 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
|
||||
}
|
||||
|
||||
|
||||
void onShowPayFundsScreen() {
|
||||
isTakeOfferButtonVisible.set(true);
|
||||
void onShowPaymentScreen() {
|
||||
state.set(State.PAYMENT_SCREEN);
|
||||
}
|
||||
|
||||
// On focus out we do validation and apply the data to the model
|
||||
@ -268,26 +379,20 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
|
||||
calculateVolume();
|
||||
dataModel.calculateTotalToPay();
|
||||
}
|
||||
updateButtonDisableState();
|
||||
evaluateState();
|
||||
});
|
||||
|
||||
dataModel.isWalletFunded.addListener((ov, oldValue, newValue) -> {
|
||||
updateButtonDisableState();
|
||||
evaluateState();
|
||||
});
|
||||
|
||||
// Binding with Bindings.createObjectBinding does not work because of bi-directional binding
|
||||
dataModel.amountAsCoin.addListener((ov, oldValue, newValue) -> amount.set(formatter.formatCoin(newValue)));
|
||||
}
|
||||
|
||||
dataModel.requestTakeOfferErrorMessage.addListener((ov, oldValue, newValue) -> {
|
||||
if (newValue != null) {
|
||||
isTakeOfferButtonDisabled.set(false);
|
||||
isTakeOfferSpinnerVisible.set(false);
|
||||
}
|
||||
});
|
||||
dataModel.requestTakeOfferSuccess.addListener((ov, oldValue, newValue) -> {
|
||||
isTakeOfferButtonVisible.set(!newValue);
|
||||
isTakeOfferSpinnerVisible.set(false);
|
||||
});
|
||||
private void applyTakeOfferRequestResult(boolean success) {
|
||||
isTakeOfferSpinnerVisible.set(false);
|
||||
showTransactionPublishedScreen.set(success);
|
||||
}
|
||||
|
||||
private void setupBindings() {
|
||||
@ -301,11 +406,6 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
|
||||
|
||||
totalToPayAsCoin.bind(dataModel.totalToPayAsCoin);
|
||||
|
||||
requestTakeOfferErrorMessage.bind(dataModel.requestTakeOfferErrorMessage);
|
||||
showTransactionPublishedScreen.bind(dataModel.requestTakeOfferSuccess);
|
||||
transactionId.bind(dataModel.transactionId);
|
||||
offerIsAvailable.bind(dataModel.offerIsAvailable);
|
||||
|
||||
btcCode.bind(dataModel.btcCode);
|
||||
}
|
||||
|
||||
@ -318,12 +418,16 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
|
||||
dataModel.amountAsCoin.set(formatter.parseToCoinWith4Decimals(amount.get()));
|
||||
}
|
||||
|
||||
private void updateButtonDisableState() {
|
||||
isTakeOfferButtonDisabled.set(!(isBtcInputValid(amount.get()).isValid &&
|
||||
dataModel.isMinAmountLessOrEqualAmount() &&
|
||||
!dataModel.isAmountLargerThanOfferAmount() &&
|
||||
dataModel.isWalletFunded.get())
|
||||
);
|
||||
private void evaluateState() {
|
||||
isAmountAndPriceValidAndWalletFunded = isBtcInputValid(amount.get()).isValid &&
|
||||
dataModel.isMinAmountLessOrEqualAmount() &&
|
||||
!dataModel.isAmountLargerThanOfferAmount() &&
|
||||
dataModel.isWalletFunded.get();
|
||||
|
||||
if (isAmountAndPriceValidAndWalletFunded && state.get() != State.CHECK_AVAILABILITY)
|
||||
state.set(State.PAYMENT_SCREEN);
|
||||
|
||||
takeOfferButtonDisabled.set(!isAmountAndPriceValidAndWalletFunded || takeOfferRequested);
|
||||
}
|
||||
|
||||
private InputValidator.ValidationResult isBtcInputValid(String input) {
|
||||
|
@ -103,6 +103,7 @@ public class Transitions {
|
||||
if (removeBlurTimeLine != null)
|
||||
removeBlurTimeLine.stop();
|
||||
|
||||
node.setMouseTransparent(true);
|
||||
GaussianBlur blur = new GaussianBlur(0.0);
|
||||
Timeline timeline = new Timeline();
|
||||
KeyValue kv1 = new KeyValue(blur.radiusProperty(), 15.0);
|
||||
@ -134,6 +135,7 @@ public class Transitions {
|
||||
public void removeBlur(Node node, int duration, boolean useDarken) {
|
||||
if (preferences.getUseEffects()) {
|
||||
if (node != null) {
|
||||
node.setMouseTransparent(false);
|
||||
GaussianBlur blur = (GaussianBlur) node.getEffect();
|
||||
if (blur != null) {
|
||||
removeBlurTimeLine = new Timeline();
|
||||
|
@ -55,7 +55,17 @@ public class Offer implements Serializable {
|
||||
AVAILABLE,
|
||||
NOT_AVAILABLE,
|
||||
FAULT,
|
||||
REMOVED
|
||||
REMOVED;
|
||||
|
||||
private String errorMessage;
|
||||
|
||||
public void setErrorMessage(String errorMessage) {
|
||||
this.errorMessage = errorMessage;
|
||||
}
|
||||
|
||||
public String getErrorMessage() {
|
||||
return errorMessage;
|
||||
}
|
||||
}
|
||||
|
||||
// key attributes for lookup
|
||||
|
@ -17,15 +17,14 @@
|
||||
|
||||
package io.bitsquare.trade.protocol.availability;
|
||||
|
||||
import io.bitsquare.common.taskrunner.TaskRunner;
|
||||
import io.bitsquare.network.Message;
|
||||
import io.bitsquare.network.Peer;
|
||||
import io.bitsquare.offer.Offer;
|
||||
import io.bitsquare.trade.handlers.MessageHandler;
|
||||
import io.bitsquare.trade.protocol.availability.messages.ReportOfferAvailabilityMessage;
|
||||
import io.bitsquare.trade.protocol.availability.tasks.GetPeerAddress;
|
||||
import io.bitsquare.trade.protocol.availability.tasks.ProcessReportOfferAvailabilityMessage;
|
||||
import io.bitsquare.trade.protocol.availability.tasks.RequestIsOfferAvailable;
|
||||
import io.bitsquare.common.taskrunner.TaskRunner;
|
||||
|
||||
import javafx.application.Platform;
|
||||
|
||||
@ -82,7 +81,6 @@ public class CheckOfferAvailabilityProtocol {
|
||||
public void cancel() {
|
||||
isCanceled = true;
|
||||
taskRunner.cancel();
|
||||
model.getOffer().setState(Offer.State.UNKNOWN);
|
||||
}
|
||||
|
||||
|
||||
|
@ -19,6 +19,7 @@ package io.bitsquare.trade.protocol.trade.taker;
|
||||
|
||||
import io.bitsquare.network.Message;
|
||||
import io.bitsquare.network.Peer;
|
||||
import io.bitsquare.offer.Offer;
|
||||
import io.bitsquare.trade.Trade;
|
||||
import io.bitsquare.trade.handlers.MessageHandler;
|
||||
import io.bitsquare.trade.protocol.trade.TradeMessage;
|
||||
@ -43,6 +44,11 @@ import io.bitsquare.trade.protocol.trade.taker.tasks.TakerCommitDepositTx;
|
||||
import io.bitsquare.trade.protocol.trade.taker.tasks.TakerCreatesAndSignsDepositTx;
|
||||
import io.bitsquare.trade.protocol.trade.taker.tasks.VerifyOfferFeePayment;
|
||||
import io.bitsquare.trade.protocol.trade.taker.tasks.VerifyOffererAccount;
|
||||
import io.bitsquare.util.Utilities;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
import javafx.animation.AnimationTimer;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@ -51,9 +57,11 @@ import static io.bitsquare.util.Validator.nonEmptyStringOf;
|
||||
|
||||
public class SellerAsTakerProtocol {
|
||||
private static final Logger log = LoggerFactory.getLogger(SellerAsTakerProtocol.class);
|
||||
public static final int TIMEOUT_DELAY = 10000;
|
||||
|
||||
private final SellerAsTakerModel model;
|
||||
private final MessageHandler messageHandler;
|
||||
private AnimationTimer timeoutTimer;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Constructor
|
||||
@ -78,7 +86,15 @@ public class SellerAsTakerProtocol {
|
||||
|
||||
SellerAsTakerTaskRunner<SellerAsTakerModel> taskRunner = new SellerAsTakerTaskRunner<>(model,
|
||||
() -> {
|
||||
log.debug("taskRunner at handleRequestTakeOfferUIEvent completed");
|
||||
log.debug("taskRunner at takeOffer completed");
|
||||
|
||||
startTimeout(animationTimer -> {
|
||||
Offer.State offerState = Offer.State.FAULT;
|
||||
offerState.setErrorMessage("We did not get any reply for the take offer request. " +
|
||||
"Seems that there are connection problems to your peer.");
|
||||
model.getOffer().setState(Offer.State.FAULT);
|
||||
return null;
|
||||
});
|
||||
},
|
||||
(errorMessage) -> {
|
||||
log.error(errorMessage);
|
||||
@ -97,6 +113,8 @@ public class SellerAsTakerProtocol {
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void handleRespondToTakeOfferRequestMessage(RespondToTakeOfferRequestMessage tradeMessage) {
|
||||
stopTimeout();
|
||||
|
||||
model.setTradeMessage(tradeMessage);
|
||||
|
||||
SellerAsTakerTaskRunner<SellerAsTakerModel> taskRunner = new SellerAsTakerTaskRunner<>(model,
|
||||
@ -223,4 +241,23 @@ public class SellerAsTakerProtocol {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Timeout
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void startTimeout(Function<AnimationTimer, Void> callback) {
|
||||
stopTimeout();
|
||||
timeoutTimer = Utilities.setTimeout(TIMEOUT_DELAY, callback);
|
||||
timeoutTimer.start();
|
||||
}
|
||||
|
||||
private void stopTimeout() {
|
||||
if (timeoutTimer != null) {
|
||||
timeoutTimer.stop();
|
||||
timeoutTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -91,7 +91,6 @@ takeOffer.amountPriceBox.amountRangeDescription=Possible amount range
|
||||
takeOffer.amountPriceBox.info=Enter the amount of Bitcoin you want to sell. You can choose an amount between the minimum amount and the amount.
|
||||
takeOffer.amountPriceBox.next=Next step
|
||||
takeOffer.amountPriceBox.warning.invalidBtcDecimalPlaces=The amount you have entered exceeds the number of allowed decimal places.\nThe amount has been adjusted to 4 decimal places.
|
||||
takeOffer.amountPriceBox.error.message=An error occurred when taking the offer:\n\n {0}
|
||||
takeOffer.validation.amountSmallerThanMinAmount=Amount cannot be smaller than minimum amount defined in the offer.
|
||||
takeOffer.validation.amountLargerThanOfferAmount=Input amount cannot be higher than the amount defined in the offer.
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user