Handle errors at take offer screen

This commit is contained in:
Manfred Karrer 2015-03-17 13:25:15 +01:00
parent 9a0e174099
commit e6744fb727
9 changed files with 295 additions and 221 deletions

View File

@ -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() {

View File

@ -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>

View File

@ -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);

View File

@ -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);
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 &&
private void evaluateState() {
isAmountAndPriceValidAndWalletFunded = isBtcInputValid(amount.get()).isValid &&
dataModel.isMinAmountLessOrEqualAmount() &&
!dataModel.isAmountLargerThanOfferAmount() &&
dataModel.isWalletFunded.get())
);
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) {

View File

@ -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();

View File

@ -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

View File

@ -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);
}

View File

@ -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;
}
}
}

View File

@ -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.