Add notification popups, Cleanup pending trades view

This commit is contained in:
Manfred Karrer 2016-02-14 12:37:53 +01:00
parent 59e4fc6842
commit 615f5570c3
16 changed files with 694 additions and 852 deletions

View file

@ -70,9 +70,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.time.Duration; import java.time.Duration;
import java.util.ArrayList; import java.util.*;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -135,6 +133,8 @@ public class MainViewModel implements ViewModel {
private java.util.Timer numberofBtcPeersTimer; private java.util.Timer numberofBtcPeersTimer;
private java.util.Timer numberofP2PNetworkPeersTimer; private java.util.Timer numberofP2PNetworkPeersTimer;
private Timer startupTimeout; private Timer startupTimeout;
private Set<Subscription> tradeStateSubscriptions = new HashSet<>();
private Set<Subscription> disputeStateSubscriptions = new HashSet<>();
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -375,13 +375,13 @@ public class MainViewModel implements ViewModel {
tradeManager.getTrades().addListener((ListChangeListener<Trade>) change -> { tradeManager.getTrades().addListener((ListChangeListener<Trade>) change -> {
change.next(); change.next();
addDisputeStateListeners(change.getAddedSubList()); setDisputeStateSubscriptions();
addTradeStateListeners(change.getAddedSubList()); setTradeStateSubscriptions();
pendingTradesChanged(); pendingTradesChanged();
}); });
pendingTradesChanged(); pendingTradesChanged();
addDisputeStateListeners(tradeManager.getTrades()); setDisputeStateSubscriptions();
addTradeStateListeners(tradeManager.getTrades()); setTradeStateSubscriptions();
// arbitratorManager // arbitratorManager
@ -533,7 +533,7 @@ public class MainViewModel implements ViewModel {
} }
private void updateP2pNetworkInfoWithPeersChanged(int numPeers) { private void updateP2pNetworkInfoWithPeersChanged(int numPeers) {
p2PNetworkInfo.set("Nr. of connections: " + numPeers); p2PNetworkInfo.set("Nr. of P2P network peers: " + numPeers);
} }
private void displayAlertIfPresent(Alert alert) { private void displayAlertIfPresent(Alert alert) {
@ -652,8 +652,8 @@ public class MainViewModel implements ViewModel {
private void setWalletServiceException(Throwable error) { private void setWalletServiceException(Throwable error) {
setBitcoinNetworkSyncProgress(0); setBitcoinNetworkSyncProgress(0);
btcSplashInfo.set("Nr. of peers: " + numBTCPeers + " / connecting to " + btcNetworkAsString + " failed"); btcSplashInfo.set("Nr. of Bitcoin network peers: " + numBTCPeers + " / connecting to " + btcNetworkAsString + " failed");
btcFooterInfo.set("Nr. of eers: " + numBTCPeers + " / connecting to " + btcNetworkAsString + " failed"); btcFooterInfo.set(btcSplashInfo.get());
if (error instanceof TimeoutException) { if (error instanceof TimeoutException) {
walletServiceErrorMsg.set("Connecting to the bitcoin network failed because of a timeout."); walletServiceErrorMsg.set("Connecting to the bitcoin network failed because of a timeout.");
} else if (error.getCause() instanceof BlockStoreException) { } else if (error.getCause() instanceof BlockStoreException) {
@ -684,7 +684,7 @@ public class MainViewModel implements ViewModel {
break; break;
case HALF_REACHED: case HALF_REACHED:
id = "displayHalfTradePeriodOver" + trade.getId(); id = "displayHalfTradePeriodOver" + trade.getId();
if (preferences.showAgain(id)) { if (preferences.showAgain(id) && !BitsquareApp.DEV_MODE) {
preferences.dontShowAgain(id); preferences.dontShowAgain(id);
new Popup().warning("Your trade with ID " + trade.getShortId() + new Popup().warning("Your trade with ID " + trade.getShortId() +
" has reached the half of the max. allowed trading period and " + " has reached the half of the max. allowed trading period and " +
@ -696,7 +696,7 @@ public class MainViewModel implements ViewModel {
break; break;
case TRADE_PERIOD_OVER: case TRADE_PERIOD_OVER:
id = "displayTradePeriodOver" + trade.getId(); id = "displayTradePeriodOver" + trade.getId();
if (preferences.showAgain(id)) { if (preferences.showAgain(id) && !BitsquareApp.DEV_MODE) {
preferences.dontShowAgain(id); preferences.dontShowAgain(id);
new Popup().warning("Your trade with ID " + trade.getShortId() + new Popup().warning("Your trade with ID " + trade.getShortId() +
" has reached the max. allowed trading period and is " + " has reached the max. allowed trading period and is " +
@ -738,71 +738,21 @@ public class MainViewModel implements ViewModel {
showPendingTradesNotification.set(numPendingTrades > 0); showPendingTradesNotification.set(numPendingTrades > 0);
} }
private void addTradeStateListeners(List<? extends Trade> addedTrades) { private void setTradeStateSubscriptions() {
addedTrades.stream().forEach(trade -> { tradeStateSubscriptions.stream().forEach(Subscription::unsubscribe);
tradeStateSubscriptions.clear();
tradeManager.getTrades().stream().forEach(trade -> {
Subscription tradeStateSubscription = EasyBind.subscribe(trade.stateProperty(), newValue -> { Subscription tradeStateSubscription = EasyBind.subscribe(trade.stateProperty(), newValue -> {
if (newValue != null) { if (newValue != null) {
applyState(trade); applyTradeState(trade);
} }
}); });
tradeStateSubscriptions.add(tradeStateSubscription);
}); });
/* addedTrades.stream()
.forEach(trade -> trade.stateProperty().addListener((observable, oldValue, newValue) -> {
String msg = "";
log.debug("addTradeStateListeners " + newValue);
switch (newValue) {
case PREPARATION:
case TAKER_FEE_PAID:
case DEPOSIT_PUBLISH_REQUESTED:
case DEPOSIT_PUBLISHED:
case DEPOSIT_SEEN_IN_NETWORK:
case DEPOSIT_PUBLISHED_MSG_SENT:
case DEPOSIT_PUBLISHED_MSG_RECEIVED:
break;
case DEPOSIT_CONFIRMED:
msg = newValue.name();
break;
case FIAT_PAYMENT_STARTED:
break;
case FIAT_PAYMENT_STARTED_MSG_SENT:
break;
case FIAT_PAYMENT_STARTED_MSG_RECEIVED:
break;
case FIAT_PAYMENT_RECEIPT:
break;
case FIAT_PAYMENT_RECEIPT_MSG_SENT:
break;
case FIAT_PAYMENT_RECEIPT_MSG_RECEIVED:
break;
case PAYOUT_TX_SENT:
break;
case PAYOUT_TX_RECEIVED:
break;
case PAYOUT_TX_COMMITTED:
break;
case PAYOUT_BROAD_CASTED:
break;
case WITHDRAW_COMPLETED:
break;
default:
log.warn("unhandled processState " + newValue);
break;
} }
//new Popup().information(msg).show(); private void applyTradeState(Trade trade) {
}));*/
}
private void applyState(Trade trade) {
Trade.State state = trade.getState(); Trade.State state = trade.getState();
log.debug("addTradeStateListeners " + state); log.debug("addTradeStateListeners " + state);
boolean isBtcBuyer = tradeManager.isMyOfferInBtcBuyerRole(trade.getOffer()); boolean isBtcBuyer = tradeManager.isMyOfferInBtcBuyerRole(trade.getOffer());
@ -818,6 +768,7 @@ public class MainViewModel implements ViewModel {
case DEPOSIT_CONFIRMED: case DEPOSIT_CONFIRMED:
message = "The deposit transaction of your trade has got the first blockchain confirmation.\n" + message = "The deposit transaction of your trade has got the first blockchain confirmation.\n" +
"You have to start the payment to the bitcoin seller now."; "You have to start the payment to the bitcoin seller now.";
break; break;
/* case FIAT_PAYMENT_RECEIPT_MSG_RECEIVED: /* case FIAT_PAYMENT_RECEIPT_MSG_RECEIVED:
case PAYOUT_TX_COMMITTED: case PAYOUT_TX_COMMITTED:
@ -853,13 +804,13 @@ public class MainViewModel implements ViewModel {
if (message != null) { if (message != null) {
//TODO we get that called initially before the navigation is inited //TODO we get that called initially before the navigation is inited
if (isPendingTradesViewCurrentView || currentPath == null) { if (isPendingTradesViewCurrentView || currentPath == null) {
if (preferences.showAgain(id)) if (preferences.showAgain(id) && !BitsquareApp.DEV_MODE)
new Popup().headLine(headLine) new Popup().headLine(headLine)
.message(message) .message(message)
.show(); .show();
preferences.dontShowAgain(id); preferences.dontShowAgain(id);
} else { } else {
if (preferences.showAgain(id)) if (preferences.showAgain(id) && !BitsquareApp.DEV_MODE)
new Popup().headLine(headLine) new Popup().headLine(headLine)
.message(message) .message(message)
.actionButtonText("Go to \"Portfolio/Open trades\"") .actionButtonText("Go to \"Portfolio/Open trades\"")
@ -874,9 +825,22 @@ public class MainViewModel implements ViewModel {
} }
} }
private void addDisputeStateListeners(List<? extends Trade> addedTrades) { private void setDisputeStateSubscriptions() {
addedTrades.stream().forEach(trade -> trade.disputeStateProperty().addListener((observable, oldValue, newValue) -> { disputeStateSubscriptions.stream().forEach(Subscription::unsubscribe);
switch (newValue) { disputeStateSubscriptions.clear();
tradeManager.getTrades().stream().forEach(trade -> {
Subscription disputeStateSubscription = EasyBind.subscribe(trade.disputeStateProperty(), disputeState -> {
if (disputeState != null) {
applyDisputeState(trade, disputeState);
}
});
disputeStateSubscriptions.add(disputeStateSubscription);
});
}
private void applyDisputeState(Trade trade, Trade.DisputeState disputeState) {
switch (disputeState) {
case NONE: case NONE:
break; break;
case DISPUTE_REQUESTED: case DISPUTE_REQUESTED:
@ -898,15 +862,14 @@ public class MainViewModel implements ViewModel {
new Popup().information("A support ticket for trade with ID " + trade.getShortId() + " has been closed.").show(); new Popup().information("A support ticket for trade with ID " + trade.getShortId() + " has been closed.").show();
break; break;
} }
}));
} }
private void setBitcoinNetworkSyncProgress(double value) { private void setBitcoinNetworkSyncProgress(double value) {
btcSyncProgress.set(value); btcSyncProgress.set(value);
String numPeers = "Nr. of peers: " + numBTCPeers; String numPeers = "Nr. of Bitcoin network peers: " + numBTCPeers;
if (value == 1) { if (value == 1) {
btcSplashInfo.set(numPeers + " / synchronized with " + btcNetworkAsString); btcSplashInfo.set(numPeers + " / synchronized with " + btcNetworkAsString);
btcFooterInfo.set(numPeers + " / synchronized with " + btcNetworkAsString); btcFooterInfo.set(btcSplashInfo.get());
btcSplashSyncIconId.set("image-connection-synced"); btcSplashSyncIconId.set("image-connection-synced");
stopCheckForBtcSyncStateTimer(); stopCheckForBtcSyncStateTimer();
} else if (value > 0.0) { } else if (value > 0.0) {
@ -916,7 +879,7 @@ public class MainViewModel implements ViewModel {
stopCheckForBtcSyncStateTimer(); stopCheckForBtcSyncStateTimer();
} else if (value == -1) { } else if (value == -1) {
btcSplashInfo.set(numPeers + " / connecting to " + btcNetworkAsString); btcSplashInfo.set(numPeers + " / connecting to " + btcNetworkAsString);
btcFooterInfo.set(numPeers + " / connecting to " + btcNetworkAsString); btcFooterInfo.set(btcSplashInfo.get());
} else { } else {
log.error("Not allowed value at setBitcoinNetworkSyncProgress: " + value); log.error("Not allowed value at setBitcoinNetworkSyncProgress: " + value);
} }

View file

@ -17,9 +17,10 @@
package io.bitsquare.gui.main.portfolio.pendingtrades; package io.bitsquare.gui.main.portfolio.pendingtrades;
import io.bitsquare.app.Log;
import io.bitsquare.gui.main.portfolio.pendingtrades.steps.TradeWizardItem; import io.bitsquare.gui.main.portfolio.pendingtrades.steps.TradeWizardItem;
import io.bitsquare.gui.main.portfolio.pendingtrades.steps.buyer.*; import io.bitsquare.gui.main.portfolio.pendingtrades.steps.buyer.*;
import javafx.beans.value.ChangeListener; import org.fxmisc.easybind.EasyBind;
public class BuyerSubView extends TradeSubView { public class BuyerSubView extends TradeSubView {
private TradeWizardItem step1; private TradeWizardItem step1;
@ -28,8 +29,6 @@ public class BuyerSubView extends TradeSubView {
private TradeWizardItem step4; private TradeWizardItem step4;
private TradeWizardItem step5; private TradeWizardItem step5;
private final ChangeListener<PendingTradesViewModel.BuyerState> stateChangeListener;
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Constructor, Initialisation // Constructor, Initialisation
@ -37,20 +36,13 @@ public class BuyerSubView extends TradeSubView {
public BuyerSubView(PendingTradesViewModel model) { public BuyerSubView(PendingTradesViewModel model) {
super(model); super(model);
stateChangeListener = (ov, oldValue, newValue) -> applyState(newValue);
} }
@Override @Override
protected void activate() { protected void activate() {
viewStateSubscription = EasyBind.subscribe(model.getBuyerState(), this::onViewStateChanged);
super.activate(); super.activate();
model.getBuyerState().addListener(stateChangeListener);
applyState(model.getBuyerState().get());
}
@Override
protected void deactivate() {
super.deactivate();
model.getBuyerState().removeListener(stateChangeListener);
} }
@Override @Override
@ -81,8 +73,11 @@ public class BuyerSubView extends TradeSubView {
// State // State
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
private void applyState(PendingTradesViewModel.BuyerState state) { @Override
log.debug("applyState " + state); protected void onViewStateChanged(PendingTradesViewModel.State viewState) {
Log.traceCall(viewState.toString());
if (viewState != null) {
PendingTradesViewModel.BuyerState buyerState = (PendingTradesViewModel.BuyerState) viewState;
step1.setDisabled(); step1.setDisabled();
step2.setDisabled(); step2.setDisabled();
@ -90,13 +85,8 @@ public class BuyerSubView extends TradeSubView {
step4.setDisabled(); step4.setDisabled();
step5.setDisabled(); step5.setDisabled();
if (tradeStepView != null) switch (buyerState) {
tradeStepView.doDeactivate();
switch (state) {
case UNDEFINED: case UNDEFINED:
contentPane.getChildren().clear();
leftVBox.getChildren().clear();
break; break;
case WAIT_FOR_BLOCKCHAIN_CONFIRMATION: case WAIT_FOR_BLOCKCHAIN_CONFIRMATION:
showItem(step1); showItem(step1);
@ -122,23 +112,12 @@ public class BuyerSubView extends TradeSubView {
step3.setCompleted(); step3.setCompleted();
step4.setCompleted(); step4.setCompleted();
showItem(step5); showItem(step5);
BuyerStep5View buyerStep5View = (BuyerStep5View) tradeStepView;
buyerStep5View.setBtcTradeAmountLabelText("You have bought:");
buyerStep5View.setFiatTradeAmountLabelText("You have paid:");
buyerStep5View.setBtcTradeAmountTextFieldText(model.getTradeVolume());
buyerStep5View.setFiatTradeAmountTextFieldText(model.getFiatVolume());
buyerStep5View.setFeesTextFieldText(model.getTotalFees());
buyerStep5View.setSecurityDepositTextFieldText(model.getSecurityDeposit());
buyerStep5View.setWithdrawAmountTextFieldText(model.getPayoutAmount());
break; break;
default: default:
log.warn("unhandled buyerState " + state); log.warn("unhandled buyerState " + buyerState);
break; break;
} }
}
if (tradeStepView != null)
tradeStepView.doActivate();
} }
} }

View file

@ -35,11 +35,17 @@ import io.bitsquare.gui.main.disputes.DisputesView;
import io.bitsquare.gui.popups.SelectDepositTxPopup; import io.bitsquare.gui.popups.SelectDepositTxPopup;
import io.bitsquare.gui.popups.WalletPasswordPopup; import io.bitsquare.gui.popups.WalletPasswordPopup;
import io.bitsquare.payment.PaymentAccountContractData; import io.bitsquare.payment.PaymentAccountContractData;
import io.bitsquare.trade.*; import io.bitsquare.trade.BuyerTrade;
import io.bitsquare.trade.SellerTrade;
import io.bitsquare.trade.Trade;
import io.bitsquare.trade.TradeManager;
import io.bitsquare.trade.offer.Offer; import io.bitsquare.trade.offer.Offer;
import io.bitsquare.user.Preferences; import io.bitsquare.user.Preferences;
import io.bitsquare.user.User; import io.bitsquare.user.User;
import javafx.beans.property.*; import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections; import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener; import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList; import javafx.collections.ObservableList;
@ -48,6 +54,7 @@ import org.bitcoinj.core.Coin;
import org.bitcoinj.core.Transaction; import org.bitcoinj.core.Transaction;
import org.spongycastle.crypto.params.KeyParameter; import org.spongycastle.crypto.params.KeyParameter;
import javax.annotation.Nullable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -56,24 +63,22 @@ import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
public class PendingTradesDataModel extends ActivatableDataModel { public class PendingTradesDataModel extends ActivatableDataModel {
private final TradeManager tradeManager; public final TradeManager tradeManager;
private final WalletService walletService; public final WalletService walletService;
private final TradeWalletService tradeWalletService; private final TradeWalletService tradeWalletService;
private final User user; private final User user;
private final KeyRing keyRing; private final KeyRing keyRing;
private final DisputeManager disputeManager; public final DisputeManager disputeManager;
private final Navigation navigation; private final Navigation navigation;
private final WalletPasswordPopup walletPasswordPopup; private final WalletPasswordPopup walletPasswordPopup;
private final ObservableList<PendingTradesListItem> list = FXCollections.observableArrayList(); final ObservableList<PendingTradesListItem> list = FXCollections.observableArrayList();
private PendingTradesListItem selectedItem;
private final ListChangeListener<Trade> tradesListChangeListener; private final ListChangeListener<Trade> tradesListChangeListener;
private boolean isOfferer; private boolean isOfferer;
private final ObjectProperty<Trade> tradeProperty = new SimpleObjectProperty<>(); final ObjectProperty<PendingTradesListItem> selectedItemProperty = new SimpleObjectProperty<>();
private final StringProperty txId = new SimpleStringProperty(); public final StringProperty txId = new SimpleStringProperty();
private Trade trade; public final Preferences preferences;
private final Preferences preferences;
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -108,88 +113,37 @@ public class PendingTradesDataModel extends ActivatableDataModel {
tradeManager.getTrades().removeListener(tradesListChangeListener); tradeManager.getTrades().removeListener(tradesListChangeListener);
} }
private void onListChanged() {
Log.traceCall();
list.clear();
list.addAll(tradeManager.getTrades().stream().map(PendingTradesListItem::new).collect(Collectors.toList()));
// we sort by date, earliest first
list.sort((o1, o2) -> o2.getTrade().getDate().compareTo(o1.getTrade().getDate()));
// TODO improve selectedItem handling
// selectedItem does not get set to null if we dont have the view visible
// So if the item gets removed form the list, and a new item is added we need to check if the old
// selectedItem is in the new list, if not we know it is an invalid one
if (list.size() == 1)
onSelectTrade(list.get(0));
else if (list.size() > 1 && (selectedItem == null || !list.contains(selectedItem)))
onSelectTrade(list.get(0));
else if (list.size() == 0)
onSelectTrade(null);
}
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// UI actions // UI actions
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
void onSelectTrade(PendingTradesListItem item) { void onSelectItem(PendingTradesListItem item) {
// clean up previous selectedItem doSelectItem(item);
selectedItem = item;
if (item == null) {
trade = null;
tradeProperty.set(null);
} else {
trade = item.getTrade();
tradeProperty.set(trade);
isOfferer = tradeManager.isMyOffer(trade.getOffer());
if (trade.getDepositTx() != null)
txId.set(trade.getDepositTx().getHashAsString());
}
} }
void onFiatPaymentStarted(ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) { public void onPaymentStarted(ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
checkNotNull(trade, "trade must not be null"); checkNotNull(getTrade(), "trade must not be null");
checkArgument(trade instanceof BuyerTrade, "Check failed: trade instanceof BuyerTrade"); checkArgument(getTrade() instanceof BuyerTrade, "Check failed: trade instanceof BuyerTrade");
checkArgument(trade.getDisputeState() == Trade.DisputeState.NONE, "Check failed: trade.getDisputeState() == Trade.DisputeState.NONE"); checkArgument(getTrade().getDisputeState() == Trade.DisputeState.NONE, "Check failed: trade.getDisputeState() == Trade.DisputeState.NONE");
((BuyerTrade) trade).onFiatPaymentStarted(resultHandler, errorMessageHandler); ((BuyerTrade) getTrade()).onFiatPaymentStarted(resultHandler, errorMessageHandler);
} }
void onFiatPaymentReceived() { public void onFiatPaymentReceived() {
checkNotNull(trade, "trade must not be null"); checkNotNull(getTrade(), "trade must not be null");
if (trade instanceof SellerTrade && trade.getDisputeState() == Trade.DisputeState.NONE) checkArgument(getTrade() instanceof SellerTrade, "Check failed: trade instanceof SellerTrade");
((SellerTrade) trade).onFiatPaymentReceived(); if (getTrade().getDisputeState() == Trade.DisputeState.NONE)
((SellerTrade) getTrade()).onFiatPaymentReceived();
} }
public void onWithdrawRequest(String toAddress, ResultHandler resultHandler, FaultHandler faultHandler) { public void onWithdrawRequest(String toAddress, ResultHandler resultHandler, FaultHandler faultHandler) {
checkNotNull(trade, "trade must not be null"); checkNotNull(getTrade(), "trade must not be null");
if (walletService.getWallet().isEncrypted()) { if (walletService.getWallet().isEncrypted()) {
walletPasswordPopup.onAesKey(aesKey -> doWithdrawRequest(toAddress, aesKey, resultHandler, faultHandler)).show(); walletPasswordPopup.onAesKey(aesKey -> doWithdrawRequest(toAddress, aesKey, resultHandler, faultHandler)).show();
} else } else
doWithdrawRequest(toAddress, null, resultHandler, faultHandler); doWithdrawRequest(toAddress, null, resultHandler, faultHandler);
} }
private void doWithdrawRequest(String toAddress, KeyParameter aesKey, ResultHandler resultHandler, FaultHandler faultHandler) {
if (toAddress != null && toAddress.length() > 0) {
tradeManager.onWithdrawRequest(
toAddress,
aesKey,
trade,
() -> {
resultHandler.handleResult();
},
(errorMessage, throwable) -> {
log.error(errorMessage);
faultHandler.handleFault(errorMessage, throwable);
});
} else {
faultHandler.handleFault("No receiver address defined", null);
}
}
public void onOpenDispute() { public void onOpenDispute() {
tryOpenDispute(false); tryOpenDispute(false);
} }
@ -198,11 +152,142 @@ public class PendingTradesDataModel extends ActivatableDataModel {
tryOpenDispute(true); tryOpenDispute(true);
} }
///////////////////////////////////////////////////////////////////////////////////////////
// Getters
///////////////////////////////////////////////////////////////////////////////////////////
@Nullable
public PendingTradesListItem getSelectedItem() {
return selectedItemProperty.get() != null ? selectedItemProperty.get() : null;
}
@Nullable
public Trade getTrade() {
return selectedItemProperty.get() != null ? selectedItemProperty.get().getTrade() : null;
}
@Nullable
Offer getOffer() {
return getTrade() != null ? getTrade().getOffer() : null;
}
boolean isBuyOffer() {
return getOffer() != null && getOffer().getDirection() == Offer.Direction.BUY;
}
boolean isOfferer(Offer offer) {
return tradeManager.isMyOffer(offer);
}
boolean isOfferer() {
return isOfferer;
}
Coin getTotalFees() {
return FeePolicy.getFixedTxFeeForTrades().add(isOfferer() ? FeePolicy.getCreateOfferFee() : FeePolicy.getTakeOfferFee());
}
public String getCurrencyCode() {
return getOffer() != null ? getOffer().getCurrencyCode() : "";
}
public Offer.Direction getDirection(Offer offer) {
isOfferer = tradeManager.isMyOffer(offer);
return isOfferer ? offer.getDirection() : offer.getMirroredDirection();
}
void addBlockChainListener(BlockChainListener blockChainListener) {
tradeWalletService.addBlockChainListener(blockChainListener);
}
void removeBlockChainListener(BlockChainListener blockChainListener) {
tradeWalletService.removeBlockChainListener(blockChainListener);
}
public long getLockTime() {
return getTrade() != null ? getTrade().getLockTimeAsBlockHeight() : 0;
}
public long getOpenDisputeTimeAsBlockHeight() {
return getTrade() != null ? getTrade().getOpenDisputeTimeAsBlockHeight() : 0;
}
public int getBestChainHeight() {
return tradeWalletService.getBestChainHeight();
}
@Nullable
public PaymentAccountContractData getSellersPaymentAccountContractData() {
if (getTrade() != null && getTrade().getContract() != null)
return getTrade().getContract().getSellerPaymentAccountContractData();
else
return null;
}
public String getReference() {
return getOffer() != null ? getOffer().getReferenceText() : "";
}
///////////////////////////////////////////////////////////////////////////////////////////
// Private
///////////////////////////////////////////////////////////////////////////////////////////
private void onListChanged() {
Log.traceCall();
list.clear();
list.addAll(tradeManager.getTrades().stream().map(PendingTradesListItem::new).collect(Collectors.toList()));
// we sort by date, earliest first
list.sort((o1, o2) -> o2.getTrade().getDate().compareTo(o1.getTrade().getDate()));
selectBestItem();
}
private void selectBestItem() {
if (list.size() == 1)
doSelectItem(list.get(0));
else if (list.size() > 1 && (selectedItemProperty.get() == null || !list.contains(selectedItemProperty.get())))
doSelectItem(list.get(0));
else if (list.size() == 0)
doSelectItem(null);
}
private void doSelectItem(PendingTradesListItem item) {
if (item != null) {
Trade trade = item.getTrade();
isOfferer = tradeManager.isMyOffer(trade.getOffer());
if (trade.getDepositTx() != null)
txId.set(trade.getDepositTx().getHashAsString());
}
selectedItemProperty.set(item);
}
private void doWithdrawRequest(String toAddress, KeyParameter aesKey, ResultHandler resultHandler, FaultHandler faultHandler) {
if (toAddress != null && toAddress.length() > 0) {
tradeManager.onWithdrawRequest(
toAddress,
aesKey,
getTrade(),
() -> {
resultHandler.handleResult();
selectBestItem();
},
(errorMessage, throwable) -> {
log.error(errorMessage);
faultHandler.handleFault(errorMessage, throwable);
});
} else {
faultHandler.handleFault("No receiver address defined", null);
}
}
private void tryOpenDispute(boolean isSupportTicket) { private void tryOpenDispute(boolean isSupportTicket) {
if (trade != null) { if (getTrade() != null) {
Transaction depositTx = trade.getDepositTx(); Transaction depositTx = getTrade().getDepositTx();
if (depositTx != null) { if (depositTx != null) {
doOpenDispute(isSupportTicket, trade.getDepositTx()); doOpenDispute(isSupportTicket, getTrade().getDepositTx());
} else { } else {
log.warn("Trade.depositTx is null. We try to find the tx in our wallet."); log.warn("Trade.depositTx is null. We try to find the tx in our wallet.");
List<Transaction> candidates = new ArrayList<>(); List<Transaction> candidates = new ArrayList<>();
@ -220,21 +305,6 @@ public class PendingTradesDataModel extends ActivatableDataModel {
} }
}); });
/*transactions.stream().forEach(transaction -> {
Coin valueSentFromMe = transaction.getValueSentFromMe(walletService.getWallet());
if (!valueSentFromMe.isZero()) {
// spending tx
for (TransactionOutput transactionOutput : transaction.getOutputs()) {
if (!transactionOutput.isMine(walletService.getWallet())) {
if (transactionOutput.getScriptPubKey().isPayToScriptHash()) {
// MS tx
candidates.add(transaction);
}
}
}
}
});*/
if (candidates.size() == 1) if (candidates.size() == 1)
doOpenDispute(isSupportTicket, candidates.get(0)); doOpenDispute(isSupportTicket, candidates.get(0));
else if (candidates.size() > 1) else if (candidates.size() > 1)
@ -262,12 +332,13 @@ public class PendingTradesDataModel extends ActivatableDataModel {
} else { } else {
log.warn("depositTx is null"); log.warn("depositTx is null");
} }
Transaction payoutTx = trade.getPayoutTx(); Transaction payoutTx = getTrade().getPayoutTx();
if (payoutTx != null) { if (payoutTx != null) {
payoutTxSerialized = payoutTx.bitcoinSerialize(); payoutTxSerialized = payoutTx.bitcoinSerialize();
payoutTxHashAsString = payoutTx.getHashAsString(); payoutTxHashAsString = payoutTx.getHashAsString();
} }
Trade trade = getTrade();
Dispute dispute = new Dispute(disputeManager.getDisputeStorage(), Dispute dispute = new Dispute(disputeManager.getDisputeStorage(),
trade.getId(), trade.getId(),
keyRing.getPubKeyRing().hashCode(), // traderId keyRing.getPubKeyRing().hashCode(), // traderId
@ -292,110 +363,5 @@ public class PendingTradesDataModel extends ActivatableDataModel {
disputeManager.sendOpenNewDisputeMessage(dispute); disputeManager.sendOpenNewDisputeMessage(dispute);
navigation.navigateTo(MainView.class, DisputesView.class); navigation.navigateTo(MainView.class, DisputesView.class);
} }
///////////////////////////////////////////////////////////////////////////////////////////
// Getters
///////////////////////////////////////////////////////////////////////////////////////////
ObservableList<PendingTradesListItem> getList() {
return list;
}
boolean isBuyOffer() {
return trade.getOffer().getDirection() == Offer.Direction.BUY;
}
boolean isOfferer(Offer offer) {
return tradeManager.isMyOffer(offer);
}
boolean isOfferer() {
return isOfferer;
}
Coin getTotalFees() {
return FeePolicy.getFixedTxFeeForTrades().add(isOfferer() ? FeePolicy.getCreateOfferFee() : FeePolicy.getTakeOfferFee());
}
PendingTradesListItem getSelectedItem() {
return selectedItem;
}
String getCurrencyCode() {
return trade.getOffer().getCurrencyCode();
}
public Offer.Direction getDirection(Offer offer) {
// gets called earlier than onSelectTrade event handler
isOfferer = tradeManager.isMyOffer(offer);
return isOfferer ? offer.getDirection() : offer.getMirroredDirection();
}
Coin getPayoutAmount() {
return trade.getPayoutAmount();
}
Contract getContract() {
return trade.getContract();
}
public Trade getTrade() {
return trade;
}
ReadOnlyObjectProperty<Trade> getTradeProperty() {
return tradeProperty;
}
ReadOnlyStringProperty getTxId() {
return txId;
}
void addBlockChainListener(BlockChainListener blockChainListener) {
tradeWalletService.addBlockChainListener(blockChainListener);
}
void removeBlockChainListener(BlockChainListener blockChainListener) {
tradeWalletService.removeBlockChainListener(blockChainListener);
}
public long getLockTime() {
return trade.getLockTimeAsBlockHeight();
}
public long getCheckPaymentTimeAsBlockHeight() {
return trade.getCheckPaymentTimeAsBlockHeight();
}
public long getOpenDisputeTimeAsBlockHeight() {
return trade.getOpenDisputeTimeAsBlockHeight();
}
public int getBestChainHeight() {
return tradeWalletService.getBestChainHeight();
}
public PaymentAccountContractData getSellersPaymentAccountContractData() {
if (trade.getContract() != null)
return trade.getContract().getSellerPaymentAccountContractData();
else
return null;
}
public String getReference() {
return trade.getOffer().getReferenceText();
}
public WalletService getWalletService() {
return walletService;
}
public DisputeManager getDisputeManager() {
return disputeManager;
}
public Preferences getPreferences() {
return preferences;
}
} }

View file

@ -52,4 +52,5 @@ public class PendingTradesListItem {
public Fiat getPrice() { public Fiat getPrice() {
return trade.getOffer().getPrice(); return trade.getOffer().getPrice();
} }
} }

View file

@ -17,6 +17,7 @@
package io.bitsquare.gui.main.portfolio.pendingtrades; package io.bitsquare.gui.main.portfolio.pendingtrades;
import io.bitsquare.app.Log;
import io.bitsquare.common.UserThread; import io.bitsquare.common.UserThread;
import io.bitsquare.gui.common.view.ActivatableViewAndModel; import io.bitsquare.gui.common.view.ActivatableViewAndModel;
import io.bitsquare.gui.common.view.FxmlView; import io.bitsquare.gui.common.view.FxmlView;
@ -24,11 +25,7 @@ import io.bitsquare.gui.components.HyperlinkWithIcon;
import io.bitsquare.gui.popups.OpenEmergencyTicketPopup; import io.bitsquare.gui.popups.OpenEmergencyTicketPopup;
import io.bitsquare.gui.popups.TradeDetailsPopup; import io.bitsquare.gui.popups.TradeDetailsPopup;
import io.bitsquare.gui.util.BSFormatter; import io.bitsquare.gui.util.BSFormatter;
import io.bitsquare.trade.Trade;
import javafx.beans.property.ReadOnlyBooleanProperty;
import javafx.beans.property.ReadOnlyObjectWrapper; import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.beans.value.ChangeListener;
import javafx.collections.ListChangeListener;
import javafx.event.EventHandler; import javafx.event.EventHandler;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.scene.Scene; import javafx.scene.Scene;
@ -44,6 +41,8 @@ import javafx.util.Callback;
import javafx.util.StringConverter; import javafx.util.StringConverter;
import org.bitcoinj.core.Coin; import org.bitcoinj.core.Coin;
import org.bitcoinj.utils.Fiat; import org.bitcoinj.utils.Fiat;
import org.fxmisc.easybind.EasyBind;
import org.fxmisc.easybind.Subscription;
import javax.inject.Inject; import javax.inject.Inject;
@ -61,14 +60,12 @@ public class PendingTradesView extends ActivatableViewAndModel<VBox, PendingTrad
@FXML @FXML
TableColumn<PendingTradesListItem, Coin> tradeAmountColumn; TableColumn<PendingTradesListItem, Coin> tradeAmountColumn;
private ChangeListener<PendingTradesListItem> selectedItemChangeListener; private TradeSubView selectedSubView;
private TradeSubView currentSubView;
private ChangeListener<Boolean> appFocusChangeListener;
private ReadOnlyBooleanProperty appFocusProperty;
private ChangeListener<Trade> currentTradeChangeListener;
private EventHandler<KeyEvent> keyEventEventHandler; private EventHandler<KeyEvent> keyEventEventHandler;
private Scene scene; private Scene scene;
private ListChangeListener<PendingTradesListItem> listChangeListener; private Subscription selectedTableItemSubscription;
private Subscription selectedItemSubscription;
private Subscription appFocusSubscription;
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -95,30 +92,6 @@ public class PendingTradesView extends ActivatableViewAndModel<VBox, PendingTrad
table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY); table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
table.setPlaceholder(new Label("No pending trades available")); table.setPlaceholder(new Label("No pending trades available"));
table.setMinHeight(100); table.setMinHeight(100);
selectedItemChangeListener = (ov, oldValue, newValue) -> {
model.onSelectTrade(newValue);
log.debug("selectedItemChangeListener {} ", newValue);
if (newValue != null)
setNewSubView(newValue.getTrade());
};
listChangeListener = c -> updateSelectedItem();
appFocusChangeListener = (observable, oldValue, newValue) -> {
if (newValue && model.getSelectedItem() != null) {
// Focus selectedItem from model
int index = table.getItems().indexOf(model.getSelectedItem());
UserThread.execute(() -> {
//TODO app wide focus
//table.requestFocus();
//UserThread.execute(() -> table.getFocusModel().focus(index));
});
}
};
currentTradeChangeListener = (observable, oldValue, newValue) -> {
log.debug("currentTradeChangeListener {} ", newValue);
// setNewSubView(newValue);
};
// we use a hidden emergency shortcut to open support ticket // we use a hidden emergency shortcut to open support ticket
keyEventEventHandler = event -> { keyEventEventHandler = event -> {
@ -129,51 +102,95 @@ public class PendingTradesView extends ActivatableViewAndModel<VBox, PendingTrad
@Override @Override
protected void activate() { protected void activate() {
Log.traceCall();
scene = root.getScene(); scene = root.getScene();
if (scene != null) { if (scene != null) {
appFocusProperty = scene.getWindow().focusedProperty();
scene.addEventHandler(KeyEvent.KEY_RELEASED, keyEventEventHandler); scene.addEventHandler(KeyEvent.KEY_RELEASED, keyEventEventHandler);
/*appFocusSubscription = EasyBind.subscribe(scene.getWindow().focusedProperty(), isFocused -> {
if (isFocused && model.dataModel.selectedItemProperty.get() != null) {
// Focus selectedItem from model
int index = table.getItems().indexOf(model.dataModel.selectedItemProperty.get());
UserThread.execute(() -> {
//TODO app wide focus
//table.requestFocus();
//UserThread.execute(() -> table.getFocusModel().focus(index));
});
}
});*/
}
table.setItems(model.dataModel.list);
selectedItemSubscription = EasyBind.subscribe(model.dataModel.selectedItemProperty, selectedItem -> {
if (selectedItem != null) {
if (selectedSubView != null)
selectedSubView.deactivate();
if (selectedItem.getTrade() != null) {
// If we are the offerer the direction is like expected
// If we are the taker the direction is mirrored
if (model.dataModel.isOfferer())
selectedSubView = model.dataModel.isBuyOffer() ? new BuyerSubView(model) : new SellerSubView(model);
else
selectedSubView = model.dataModel.isBuyOffer() ? new SellerSubView(model) : new BuyerSubView(model);
selectedSubView.setMinHeight(430);
VBox.setVgrow(selectedSubView, Priority.ALWAYS);
if (root.getChildren().size() == 1)
root.getChildren().add(selectedSubView);
else if (root.getChildren().size() == 2)
root.getChildren().set(1, selectedSubView);
selectedSubView.activate();
} }
appFocusProperty.addListener(appFocusChangeListener); updateTableSelection();
model.currentTrade().addListener(currentTradeChangeListener); } else {
//setNewSubView(model.currentTrade().get()); removeSelectedSubView();
table.setItems(model.getList()); }
table.getSelectionModel().selectedItemProperty().addListener(selectedItemChangeListener);
if (model.getSelectedItem() == null) model.onSelectedItemChanged(selectedItem);
model.getList().addListener(listChangeListener); });
selectedTableItemSubscription = EasyBind.subscribe(table.getSelectionModel().selectedItemProperty(),
selectedItem -> {
if (selectedItem != null && !selectedItem.equals(model.dataModel.selectedItemProperty.get()))
model.dataModel.onSelectItem(selectedItem);
});
updateSelectedItem(); updateTableSelection();
} }
@Override @Override
protected void deactivate() { protected void deactivate() {
table.getSelectionModel().selectedItemProperty().removeListener(selectedItemChangeListener); selectedItemSubscription.unsubscribe();
selectedTableItemSubscription.unsubscribe();
if (appFocusSubscription != null)
appFocusSubscription.unsubscribe();
model.getList().removeListener(listChangeListener); removeSelectedSubView();
if (model.currentTrade() != null)
model.currentTrade().removeListener(currentTradeChangeListener);
if (appFocusProperty != null) {
appFocusProperty.removeListener(appFocusChangeListener);
appFocusProperty = null;
}
if (currentSubView != null)
currentSubView.deactivate();
if (scene != null) if (scene != null)
scene.removeEventHandler(KeyEvent.KEY_RELEASED, keyEventEventHandler); scene.removeEventHandler(KeyEvent.KEY_RELEASED, keyEventEventHandler);
} }
private void updateSelectedItem() { private void removeSelectedSubView() {
PendingTradesListItem selectedItem = model.getSelectedItem(); if (selectedSubView != null) {
if (selectedItem != null) { selectedSubView.deactivate();
root.getChildren().remove(selectedSubView);
selectedSubView = null;
}
}
///////////////////////////////////////////////////////////////////////////////////////////
// Private
///////////////////////////////////////////////////////////////////////////////////////////
private void updateTableSelection() {
PendingTradesListItem selectedItemFromModel = model.dataModel.selectedItemProperty.get();
if (selectedItemFromModel != null) {
// Select and focus selectedItem from model // Select and focus selectedItem from model
int index = table.getItems().indexOf(selectedItem); int index = table.getItems().indexOf(selectedItemFromModel);
UserThread.execute(() -> { UserThread.execute(() -> {
//TODO app wide focus //TODO app wide focus
table.getSelectionModel().select(index); table.getSelectionModel().select(index);
@ -184,37 +201,6 @@ public class PendingTradesView extends ActivatableViewAndModel<VBox, PendingTrad
} }
///////////////////////////////////////////////////////////////////////////////////////////
// Subviews
///////////////////////////////////////////////////////////////////////////////////////////
private void setNewSubView(Trade trade) {
if (currentSubView != null) {
currentSubView.deactivate();
root.getChildren().remove(currentSubView);
}
if (trade != null) {
if (model.isOfferer()) {
if (model.isBuyOffer())
currentSubView = new BuyerSubView(model);
else
currentSubView = new SellerSubView(model);
} else {
if (model.isBuyOffer())
currentSubView = new SellerSubView(model);
else
currentSubView = new BuyerSubView(model);
}
currentSubView.setMinHeight(430);
VBox.setVgrow(currentSubView, Priority.ALWAYS);
root.getChildren().add(1, currentSubView);
currentSubView.activate();
}
}
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// CellFactories // CellFactories
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////

View file

@ -18,34 +18,31 @@
package io.bitsquare.gui.main.portfolio.pendingtrades; package io.bitsquare.gui.main.portfolio.pendingtrades;
import com.google.inject.Inject; import com.google.inject.Inject;
import io.bitsquare.app.Log;
import io.bitsquare.btc.FeePolicy; import io.bitsquare.btc.FeePolicy;
import io.bitsquare.common.handlers.ErrorMessageHandler;
import io.bitsquare.common.handlers.ResultHandler;
import io.bitsquare.gui.common.model.ActivatableWithDataModel; import io.bitsquare.gui.common.model.ActivatableWithDataModel;
import io.bitsquare.gui.common.model.ViewModel; import io.bitsquare.gui.common.model.ViewModel;
import io.bitsquare.gui.util.BSFormatter; import io.bitsquare.gui.util.BSFormatter;
import io.bitsquare.gui.util.validation.*; import io.bitsquare.gui.util.validation.BtcAddressValidator;
import io.bitsquare.locale.BSResources; import io.bitsquare.locale.BSResources;
import io.bitsquare.p2p.P2PService; import io.bitsquare.p2p.P2PService;
import io.bitsquare.payment.PaymentMethod; import io.bitsquare.payment.PaymentMethod;
import io.bitsquare.trade.Contract; import io.bitsquare.trade.Contract;
import io.bitsquare.trade.Trade; import io.bitsquare.trade.Trade;
import io.bitsquare.trade.offer.Offer; import io.bitsquare.trade.offer.Offer;
import io.bitsquare.user.User;
import javafx.beans.property.*; import javafx.beans.property.*;
import javafx.collections.ObservableList; import javafx.beans.value.ChangeListener;
import org.bitcoinj.core.BlockChainListener; import org.bitcoinj.core.BlockChainListener;
import org.fxmisc.easybind.EasyBind; import org.fxmisc.easybind.EasyBind;
import org.fxmisc.easybind.Subscription; import org.fxmisc.easybind.Subscription;
import java.util.Date;
import static com.google.common.base.Preconditions.checkNotNull;
import static io.bitsquare.gui.main.portfolio.pendingtrades.PendingTradesViewModel.SellerState.*; import static io.bitsquare.gui.main.portfolio.pendingtrades.PendingTradesViewModel.SellerState.*;
public class PendingTradesViewModel extends ActivatableWithDataModel<PendingTradesDataModel> implements ViewModel { public class PendingTradesViewModel extends ActivatableWithDataModel<PendingTradesDataModel> implements ViewModel {
private Subscription tradeStateSubscription; private Subscription tradeStateSubscription;
private interface State { interface State {
} }
enum BuyerState implements State { enum BuyerState implements State {
@ -67,20 +64,15 @@ public class PendingTradesViewModel extends ActivatableWithDataModel<PendingTrad
REQUEST_WITHDRAWAL REQUEST_WITHDRAWAL
} }
private final BSFormatter formatter; public final BSFormatter formatter;
private final BtcAddressValidator btcAddressValidator; private final BtcAddressValidator btcAddressValidator;
private final IBANValidator ibanValidator; public final P2PService p2PService;
private final BICValidator bicValidator; public User user;
private final InputValidator inputValidator;
private final OKPayValidator okPayValidator;
private final AltCoinAddressValidator altCoinAddressValidator;
private final P2PService p2PService;
private final ObjectProperty<BuyerState> buyerState = new SimpleObjectProperty<>(PendingTradesViewModel.BuyerState.UNDEFINED); private final ObjectProperty<BuyerState> buyerState = new SimpleObjectProperty<>();
private final ObjectProperty<SellerState> sellerState = new SimpleObjectProperty<>(UNDEFINED); private final ObjectProperty<SellerState> sellerState = new SimpleObjectProperty<>();
private final StringProperty txId = new SimpleStringProperty();
private final BooleanProperty withdrawalButtonDisable = new SimpleBooleanProperty(true); private final BooleanProperty withdrawalButtonDisable = new SimpleBooleanProperty(true);
@ -92,30 +84,30 @@ public class PendingTradesViewModel extends ActivatableWithDataModel<PendingTrad
public PendingTradesViewModel(PendingTradesDataModel dataModel, public PendingTradesViewModel(PendingTradesDataModel dataModel,
BSFormatter formatter, BSFormatter formatter,
BtcAddressValidator btcAddressValidator, BtcAddressValidator btcAddressValidator,
IBANValidator ibanValidator, P2PService p2PService,
BICValidator bicValidator, User user
InputValidator inputValidator,
OKPayValidator okPayValidator,
AltCoinAddressValidator altCoinAddressValidator,
P2PService p2PService
) { ) {
super(dataModel); super(dataModel);
this.formatter = formatter; this.formatter = formatter;
this.btcAddressValidator = btcAddressValidator; this.btcAddressValidator = btcAddressValidator;
this.ibanValidator = ibanValidator;
this.bicValidator = bicValidator;
this.inputValidator = inputValidator;
this.okPayValidator = okPayValidator;
this.altCoinAddressValidator = altCoinAddressValidator;
this.p2PService = p2PService; this.p2PService = p2PService;
this.user = user;
} }
private ChangeListener<Trade.State> tradeStateChangeListener;
@Override @Override
protected void activate() { protected void activate() {
setTradeStateSubscription(); }
txId.bind(dataModel.getTxId()); // Dont set own listener as we need to control the order of the calls
public void onSelectedItemChanged(PendingTradesListItem selectedItem) {
if (tradeStateSubscription != null)
tradeStateSubscription.unsubscribe();
if (selectedItem != null)
tradeStateSubscription = EasyBind.subscribe(selectedItem.getTrade().stateProperty(), this::onTradeStateChanged);
} }
@Override @Override
@ -124,33 +116,6 @@ public class PendingTradesViewModel extends ActivatableWithDataModel<PendingTrad
tradeStateSubscription.unsubscribe(); tradeStateSubscription.unsubscribe();
tradeStateSubscription = null; tradeStateSubscription = null;
} }
txId.unbind();
}
private void setTradeStateSubscription() {
if (tradeStateSubscription != null)
tradeStateSubscription.unsubscribe();
if (dataModel.getTrade() != null) {
tradeStateSubscription = EasyBind.subscribe(dataModel.getTrade().stateProperty(), newValue -> {
log.debug("tradeStateSubscription " + newValue);
if (newValue != null) {
applyState(newValue);
}
});
}
}
///////////////////////////////////////////////////////////////////////////////////////////
// UI actions
///////////////////////////////////////////////////////////////////////////////////////////
void onSelectTrade(PendingTradesListItem item) {
dataModel.onSelectTrade(item);
// call it after dataModel.onSelectTrade as trade is set
setTradeStateSubscription();
} }
@ -166,76 +131,34 @@ public class PendingTradesViewModel extends ActivatableWithDataModel<PendingTrad
return sellerState; return sellerState;
} }
public ReadOnlyStringProperty txIdProperty() {
return txId;
}
public ReadOnlyBooleanProperty getWithdrawalButtonDisable() {
return withdrawalButtonDisable;
}
boolean isBuyOffer() {
return dataModel.isBuyOffer();
}
ReadOnlyObjectProperty<Trade> currentTrade() {
return dataModel.getTradeProperty();
}
public void fiatPaymentStarted(ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
dataModel.onFiatPaymentStarted(resultHandler, errorMessageHandler);
}
public void fiatPaymentReceived() {
dataModel.onFiatPaymentReceived();
}
public void withdrawAddressFocusOut(String text) { public void withdrawAddressFocusOut(String text) {
withdrawalButtonDisable.set(!btcAddressValidator.validate(text).isValid); withdrawalButtonDisable.set(!btcAddressValidator.validate(text).isValid);
} }
public String getPayoutAmount() { public String getPayoutAmount() {
return formatter.formatCoinWithCode(dataModel.getPayoutAmount()); return dataModel.getTrade() != null ? formatter.formatCoinWithCode(dataModel.getTrade().getPayoutAmount()) : "";
}
ObservableList<PendingTradesListItem> getList() {
return dataModel.getList();
}
public boolean isOfferer() {
return dataModel.isOfferer();
}
PendingTradesListItem getSelectedItem() {
return dataModel.getSelectedItem();
}
public String getCurrencyCode() {
return dataModel.getCurrencyCode();
}
public BtcAddressValidator getBtcAddressValidator() {
return btcAddressValidator;
}
public boolean isBootstrapped() {
return p2PService.isBootstrapped();
} }
// columns // columns
public String getRemainingTime() { public String getRemainingTime() {
if (dataModel.getTrade() != null)
return formatter.getPeriodBetweenBlockHeights(getBestChainHeight(), return formatter.getPeriodBetweenBlockHeights(getBestChainHeight(),
dataModel.getTrade().getOpenDisputeTimeAsBlockHeight()); dataModel.getTrade().getOpenDisputeTimeAsBlockHeight());
else
return "";
} }
public double getRemainingTimeAsPercentage() { public double getRemainingTimeAsPercentage() {
if (dataModel.getTrade() != null && dataModel.getOffer() != null) {
double remainingBlocks = dataModel.getTrade().getOpenDisputeTimeAsBlockHeight() - getBestChainHeight(); double remainingBlocks = dataModel.getTrade().getOpenDisputeTimeAsBlockHeight() - getBestChainHeight();
double maxPeriod = dataModel.getTrade().getOffer().getPaymentMethod().getMaxTradePeriod(); double maxPeriod = dataModel.getOffer().getPaymentMethod().getMaxTradePeriod();
if (maxPeriod != 0) if (maxPeriod != 0)
return 1 - remainingBlocks / maxPeriod; return 1 - remainingBlocks / maxPeriod;
else else
return 0; return 0;
} else {
return 0;
}
} }
public boolean showWarning(Trade trade) { public boolean showWarning(Trade trade) {
@ -270,10 +193,6 @@ public class PendingTradesViewModel extends ActivatableWithDataModel<PendingTrad
return result; return result;
} }
String formatDate(Date value) {
return formatter.formatDateTime(value);
}
public void addBlockChainListener(BlockChainListener blockChainListener) { public void addBlockChainListener(BlockChainListener blockChainListener) {
dataModel.addBlockChainListener(blockChainListener); dataModel.addBlockChainListener(blockChainListener);
} }
@ -286,64 +205,40 @@ public class PendingTradesViewModel extends ActivatableWithDataModel<PendingTrad
return dataModel.getLockTime(); return dataModel.getLockTime();
} }
public long getCheckPaymentTimeAsBlockHeight() {
return dataModel.getCheckPaymentTimeAsBlockHeight();
}
private long getOpenDisputeTimeAsBlockHeight() { private long getOpenDisputeTimeAsBlockHeight() {
return dataModel.getOpenDisputeTimeAsBlockHeight(); return dataModel.getOpenDisputeTimeAsBlockHeight();
} }
public int getBestChainHeight() { public int getBestChainHeight() {
return dataModel.getBestChainHeight(); return dataModel.getBestChainHeight();
} }
public String getOpenDisputeTimeAsFormattedDate() { public String getOpenDisputeTimeAsFormattedDate() {
if (dataModel.getOffer() != null)
return formatter.addBlocksToNowDateFormatted(getOpenDisputeTimeAsBlockHeight() - getBestChainHeight() + return formatter.addBlocksToNowDateFormatted(getOpenDisputeTimeAsBlockHeight() - getBestChainHeight() +
(dataModel.getTrade().getOffer().getPaymentMethod().getLockTime())); (dataModel.getOffer().getPaymentMethod().getLockTime()));
} else
return "";
public String getReference() {
return dataModel.getReference();
} }
public String getPaymentMethod() { public String getPaymentMethod() {
checkNotNull(dataModel.getContract(), "dataModel.getContract() must not be null"); if (dataModel.getTrade() != null && dataModel.getTrade().getContract() != null)
return BSResources.get(dataModel.getContract().getPaymentMethodName()); return BSResources.get(dataModel.getTrade().getContract().getPaymentMethodName());
else
return "";
} }
public String getFiatAmount() { public String getFiatAmount() {
return formatter.formatFiatWithCode(dataModel.getTrade().getTradeVolume()); return dataModel.getTrade() != null ? formatter.formatFiatWithCode(dataModel.getTrade().getTradeVolume()) : "";
}
public IBANValidator getIbanValidator() {
return ibanValidator;
}
public AltCoinAddressValidator getAltCoinAddressValidator() {
return altCoinAddressValidator;
}
public BICValidator getBicValidator() {
return bicValidator;
}
public InputValidator getInputValidator() {
return inputValidator;
}
public OKPayValidator getOkPayValidator() {
return okPayValidator;
} }
// summary // summary
public String getTradeVolume() { public String getTradeVolume() {
return formatter.formatCoinWithCode(dataModel.getTrade().getTradeAmount()); return dataModel.getTrade() != null ? formatter.formatCoinWithCode(dataModel.getTrade().getTradeAmount()) : "";
} }
public String getFiatVolume() { public String getFiatVolume() {
return formatter.formatFiatWithCode(dataModel.getTrade().getTradeVolume()); return dataModel.getTrade() != null ? formatter.formatFiatWithCode(dataModel.getTrade().getTradeVolume()) : "";
} }
public String getTotalFees() { public String getTotalFees() {
@ -355,11 +250,7 @@ public class PendingTradesViewModel extends ActivatableWithDataModel<PendingTrad
} }
public boolean isBlockChainMethod() { public boolean isBlockChainMethod() {
return dataModel.getTrade().getOffer().getPaymentMethod().equals(PaymentMethod.BLOCK_CHAINS); return dataModel.getOffer() != null && dataModel.getOffer().getPaymentMethod().equals(PaymentMethod.BLOCK_CHAINS);
}
public Trade getTrade() {
return dataModel.getTrade();
} }
@ -367,8 +258,8 @@ public class PendingTradesViewModel extends ActivatableWithDataModel<PendingTrad
// States // States
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
private void applyState(Trade.State tradeState) { private void onTradeStateChanged(Trade.State tradeState) {
log.debug("updateSellerState (SellerTradeState) " + tradeState); Log.traceCall(tradeState.toString());
switch (tradeState) { switch (tradeState) {
case PREPARATION: case PREPARATION:
sellerState.set(UNDEFINED); sellerState.set(UNDEFINED);
@ -389,10 +280,10 @@ public class PendingTradesViewModel extends ActivatableWithDataModel<PendingTrad
break; break;
case DEPOSIT_CONFIRMED: case DEPOSIT_CONFIRMED:
case FIAT_PAYMENT_STARTED:
sellerState.set(WAIT_FOR_FIAT_PAYMENT_STARTED);
buyerState.set(PendingTradesViewModel.BuyerState.REQUEST_START_FIAT_PAYMENT); buyerState.set(PendingTradesViewModel.BuyerState.REQUEST_START_FIAT_PAYMENT);
sellerState.set(WAIT_FOR_FIAT_PAYMENT_STARTED);
break; break;
case FIAT_PAYMENT_STARTED:
case FIAT_PAYMENT_STARTED_MSG_SENT: case FIAT_PAYMENT_STARTED_MSG_SENT:
buyerState.set(PendingTradesViewModel.BuyerState.WAIT_FOR_FIAT_PAYMENT_RECEIPT); buyerState.set(PendingTradesViewModel.BuyerState.WAIT_FOR_FIAT_PAYMENT_RECEIPT);
break; break;
@ -415,7 +306,6 @@ public class PendingTradesViewModel extends ActivatableWithDataModel<PendingTrad
buyerState.set(PendingTradesViewModel.BuyerState.WAIT_FOR_BROADCAST_AFTER_UNLOCK); buyerState.set(PendingTradesViewModel.BuyerState.WAIT_FOR_BROADCAST_AFTER_UNLOCK);
break; break;
case PAYOUT_TX_RECEIVED: case PAYOUT_TX_RECEIVED:
break;
case PAYOUT_TX_COMMITTED: case PAYOUT_TX_COMMITTED:
sellerState.set(SellerState.WAIT_FOR_BROADCAST_AFTER_UNLOCK); sellerState.set(SellerState.WAIT_FOR_BROADCAST_AFTER_UNLOCK);
break; break;

View file

@ -17,9 +17,10 @@
package io.bitsquare.gui.main.portfolio.pendingtrades; package io.bitsquare.gui.main.portfolio.pendingtrades;
import io.bitsquare.app.Log;
import io.bitsquare.gui.main.portfolio.pendingtrades.steps.TradeWizardItem; import io.bitsquare.gui.main.portfolio.pendingtrades.steps.TradeWizardItem;
import io.bitsquare.gui.main.portfolio.pendingtrades.steps.seller.*; import io.bitsquare.gui.main.portfolio.pendingtrades.steps.seller.*;
import javafx.beans.value.ChangeListener; import org.fxmisc.easybind.EasyBind;
public class SellerSubView extends TradeSubView { public class SellerSubView extends TradeSubView {
private TradeWizardItem step1; private TradeWizardItem step1;
@ -28,8 +29,6 @@ public class SellerSubView extends TradeSubView {
private TradeWizardItem step4; private TradeWizardItem step4;
private TradeWizardItem step5; private TradeWizardItem step5;
private final ChangeListener<PendingTradesViewModel.SellerState> stateChangeListener;
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Constructor, Initialisation // Constructor, Initialisation
@ -37,20 +36,12 @@ public class SellerSubView extends TradeSubView {
public SellerSubView(PendingTradesViewModel model) { public SellerSubView(PendingTradesViewModel model) {
super(model); super(model);
stateChangeListener = (ov, oldValue, newValue) -> applyState(newValue);
} }
@Override @Override
protected void activate() { protected void activate() {
viewStateSubscription = EasyBind.subscribe(model.getSellerState(), this::onViewStateChanged);
super.activate(); super.activate();
model.getSellerState().addListener(stateChangeListener);
applyState(model.getSellerState().get());
}
@Override
protected void deactivate() {
super.deactivate();
model.getSellerState().removeListener(stateChangeListener);
} }
@Override @Override
@ -81,8 +72,11 @@ public class SellerSubView extends TradeSubView {
// State // State
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
private void applyState(PendingTradesViewModel.SellerState viewState) { @Override
log.debug("applyState " + viewState); protected void onViewStateChanged(PendingTradesViewModel.State viewState) {
Log.traceCall(viewState.toString());
if (viewState != null) {
PendingTradesViewModel.SellerState sellerState = (PendingTradesViewModel.SellerState) viewState;
step1.setDisabled(); step1.setDisabled();
step2.setDisabled(); step2.setDisabled();
@ -90,13 +84,8 @@ public class SellerSubView extends TradeSubView {
step4.setDisabled(); step4.setDisabled();
step5.setDisabled(); step5.setDisabled();
if (tradeStepView != null) switch (sellerState) {
tradeStepView.doDeactivate();
switch (viewState) {
case UNDEFINED: case UNDEFINED:
contentPane.getChildren().clear();
leftVBox.getChildren().clear();
break; break;
case WAIT_FOR_BLOCKCHAIN_CONFIRMATION: case WAIT_FOR_BLOCKCHAIN_CONFIRMATION:
showItem(step1); showItem(step1);
@ -131,24 +120,12 @@ public class SellerSubView extends TradeSubView {
step3.setCompleted(); step3.setCompleted();
step4.setCompleted(); step4.setCompleted();
showItem(step5); showItem(step5);
SellerStep5View sellerStep5View = (SellerStep5View) tradeStepView;
sellerStep5View.setBtcTradeAmountLabelText("You have sold:");
sellerStep5View.setFiatTradeAmountLabelText("You have received:");
sellerStep5View.setBtcTradeAmountTextFieldText(model.getTradeVolume());
sellerStep5View.setFiatTradeAmountTextFieldText(model.getFiatVolume());
sellerStep5View.setFeesTextFieldText(model.getTotalFees());
sellerStep5View.setSecurityDepositTextFieldText(model.getSecurityDeposit());
sellerStep5View.setWithdrawAmountTextFieldText(model.getPayoutAmount());
break; break;
default: default:
log.warn("unhandled viewState " + viewState); log.warn("unhandled viewState " + sellerState);
break; break;
} }
}
if (tradeStepView != null)
tradeStepView.doActivate();
} }
} }

View file

@ -25,6 +25,7 @@ import javafx.geometry.Insets;
import javafx.scene.control.Button; import javafx.scene.control.Button;
import javafx.scene.control.Label; import javafx.scene.control.Label;
import javafx.scene.layout.*; import javafx.scene.layout.*;
import org.fxmisc.easybind.Subscription;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -42,6 +43,7 @@ public abstract class TradeSubView extends HBox {
protected GridPane leftGridPane; protected GridPane leftGridPane;
protected TitledGroupBg tradeProcessTitledGroupBg; protected TitledGroupBg tradeProcessTitledGroupBg;
protected int leftGridPaneRowIndex = 0; protected int leftGridPaneRowIndex = 0;
protected Subscription viewStateSubscription;
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -59,11 +61,15 @@ public abstract class TradeSubView extends HBox {
} }
protected void deactivate() { protected void deactivate() {
if (viewStateSubscription != null)
viewStateSubscription.unsubscribe();
if (tradeStepView != null) if (tradeStepView != null)
tradeStepView.doDeactivate(); tradeStepView.deactivate();
if (openDisputeButton != null) if (openDisputeButton != null)
leftGridPane.getChildren().remove(openDisputeButton); leftGridPane.getChildren().remove(openDisputeButton);
if (notificationGroup != null) if (notificationGroup != null)
notificationGroup.removeItselfFrom(leftGridPane); notificationGroup.removeItselfFrom(leftGridPane);
} }
@ -74,9 +80,9 @@ public abstract class TradeSubView extends HBox {
leftGridPane = new GridPane(); leftGridPane = new GridPane();
leftGridPane.setPrefWidth(340); leftGridPane.setPrefWidth(340);
VBox.setMargin(leftGridPane, new Insets(0, 10, 10, 10));
leftGridPane.setHgap(Layout.GRID_GAP); leftGridPane.setHgap(Layout.GRID_GAP);
leftGridPane.setVgap(Layout.GRID_GAP); leftGridPane.setVgap(Layout.GRID_GAP);
VBox.setMargin(leftGridPane, new Insets(0, 10, 10, 10));
leftVBox.getChildren().add(leftGridPane); leftVBox.getChildren().add(leftGridPane);
leftGridPaneRowIndex = 0; leftGridPaneRowIndex = 0;
@ -132,6 +138,8 @@ public abstract class TradeSubView extends HBox {
abstract protected void addWizards(); abstract protected void addWizards();
abstract protected void onViewStateChanged(PendingTradesViewModel.State viewState);
protected void addWizardsToGridPane(TradeWizardItem tradeWizardItem) { protected void addWizardsToGridPane(TradeWizardItem tradeWizardItem) {
if (leftGridPaneRowIndex == 0) if (leftGridPaneRowIndex == 0)
GridPane.setMargin(tradeWizardItem, new Insets(Layout.FIRST_ROW_DISTANCE, 0, 0, 0)); GridPane.setMargin(tradeWizardItem, new Insets(Layout.FIRST_ROW_DISTANCE, 0, 0, 0));
@ -143,12 +151,15 @@ public abstract class TradeSubView extends HBox {
} }
private void createAndAddTradeStepView(Class<? extends TradeStepView> viewClass) { private void createAndAddTradeStepView(Class<? extends TradeStepView> viewClass) {
if (tradeStepView != null)
tradeStepView.deactivate();
try { try {
tradeStepView = viewClass.getDeclaredConstructor(PendingTradesViewModel.class).newInstance(model); tradeStepView = viewClass.getDeclaredConstructor(PendingTradesViewModel.class).newInstance(model);
contentPane.getChildren().setAll(tradeStepView); contentPane.getChildren().setAll(tradeStepView);
tradeStepView.setNotificationGroup(notificationGroup); tradeStepView.setNotificationGroup(notificationGroup);
tradeStepView.activate();
} catch (Exception e) { } catch (Exception e) {
log.error("Creating viewClass {} caused an error {}", viewClass, e.getMessage());
e.printStackTrace(); e.printStackTrace();
} }
} }

View file

@ -17,6 +17,7 @@
package io.bitsquare.gui.main.portfolio.pendingtrades.steps; package io.bitsquare.gui.main.portfolio.pendingtrades.steps;
import io.bitsquare.app.Log;
import io.bitsquare.arbitration.Dispute; import io.bitsquare.arbitration.Dispute;
import io.bitsquare.gui.components.TitledGroupBg; import io.bitsquare.gui.components.TitledGroupBg;
import io.bitsquare.gui.components.TxIdTextField; import io.bitsquare.gui.components.TxIdTextField;
@ -27,7 +28,6 @@ import io.bitsquare.gui.popups.Popup;
import io.bitsquare.gui.util.Layout; import io.bitsquare.gui.util.Layout;
import io.bitsquare.trade.Trade; import io.bitsquare.trade.Trade;
import io.bitsquare.user.Preferences; import io.bitsquare.user.Preferences;
import javafx.beans.value.ChangeListener;
import javafx.scene.control.ProgressBar; import javafx.scene.control.ProgressBar;
import javafx.scene.control.TextField; import javafx.scene.control.TextField;
import javafx.scene.layout.AnchorPane; import javafx.scene.layout.AnchorPane;
@ -42,16 +42,16 @@ import org.slf4j.LoggerFactory;
import java.time.Duration; import java.time.Duration;
import java.util.Optional; import java.util.Optional;
import static com.google.common.base.Preconditions.checkNotNull;
import static io.bitsquare.gui.util.FormBuilder.*; import static io.bitsquare.gui.util.FormBuilder.*;
public abstract class TradeStepView extends AnchorPane { public abstract class TradeStepView extends AnchorPane {
protected final Logger log = LoggerFactory.getLogger(this.getClass()); protected final Logger log = LoggerFactory.getLogger(this.getClass());
protected final PendingTradesViewModel model; protected final PendingTradesViewModel model;
private final Trade trade; protected final Trade trade;
protected final Preferences preferences; protected final Preferences preferences;
protected final GridPane gridPane; protected final GridPane gridPane;
private final ChangeListener<String> txIdChangeListener;
private Subscription errorMessageSubscription; private Subscription errorMessageSubscription;
private Subscription disputeStateSubscription; private Subscription disputeStateSubscription;
@ -63,6 +63,7 @@ public abstract class TradeStepView extends AnchorPane {
private ProgressBar timeLeftProgressBar; private ProgressBar timeLeftProgressBar;
private TxIdTextField txIdTextField; private TxIdTextField txIdTextField;
protected TradeSubView.NotificationGroup notificationGroup; protected TradeSubView.NotificationGroup notificationGroup;
private Subscription txIdSubscription;
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -71,8 +72,9 @@ public abstract class TradeStepView extends AnchorPane {
protected TradeStepView(PendingTradesViewModel model) { protected TradeStepView(PendingTradesViewModel model) {
this.model = model; this.model = model;
preferences = model.dataModel.getPreferences(); preferences = model.dataModel.preferences;
trade = model.getTrade(); trade = model.dataModel.getTrade();
checkNotNull(trade, "trade must not be null at TradeStepView");
gridPane = addGridPane(this); gridPane = addGridPane(this);
@ -81,16 +83,16 @@ public abstract class TradeStepView extends AnchorPane {
AnchorPane.setTopAnchor(this, -10d); AnchorPane.setTopAnchor(this, -10d);
AnchorPane.setBottomAnchor(this, 0d); AnchorPane.setBottomAnchor(this, 0d);
txIdChangeListener = (ov, oldValue, newValue) -> txIdTextField.setup(newValue);
addContent(); addContent();
} }
public void doActivate() { public void activate() {
if (txIdTextField != null) { if (txIdTextField != null) {
txIdTextField.setup(model.txIdProperty().get()); txIdTextField.setup(model.dataModel.txId.get());
if (txIdSubscription != null)
txIdSubscription.unsubscribe();
model.txIdProperty().addListener(txIdChangeListener); txIdSubscription = EasyBind.subscribe(model.dataModel.txId, id -> txIdTextField.setup(id));
} }
errorMessageSubscription = EasyBind.subscribe(trade.errorMessageProperty(), newValue -> { errorMessageSubscription = EasyBind.subscribe(trade.errorMessageProperty(), newValue -> {
@ -111,17 +113,17 @@ public abstract class TradeStepView extends AnchorPane {
} }
}); });
timer = FxTimer.runPeriodically(Duration.ofSeconds(1), this::updateTimeLeft); timer = FxTimer.runPeriodically(Duration.ofMinutes(1), this::updateTimeLeft);
} }
public void doDeactivate() { public void deactivate() {
if (txIdTextField != null) { Log.traceCall();
if (txIdSubscription != null)
txIdSubscription.unsubscribe();
if (txIdTextField != null)
txIdTextField.cleanup(); txIdTextField.cleanup();
model.txIdProperty().removeListener(txIdChangeListener);
}
if (errorMessageSubscription != null) if (errorMessageSubscription != null)
errorMessageSubscription.unsubscribe(); errorMessageSubscription.unsubscribe();
@ -189,7 +191,7 @@ public abstract class TradeStepView extends AnchorPane {
timeLeftProgressBar.setProgress(model.getRemainingTimeAsPercentage()); timeLeftProgressBar.setProgress(model.getRemainingTimeAsPercentage());
if (remainingTime != null) { if (remainingTime != null) {
timeLeftTextField.setText(remainingTime); timeLeftTextField.setText(remainingTime);
if (model.showWarning(model.getTrade()) || model.showDispute(model.getTrade())) { if (model.showWarning(trade) || model.showDispute(trade)) {
timeLeftTextField.setStyle("-fx-text-fill: -bs-error-red"); timeLeftTextField.setStyle("-fx-text-fill: -bs-error-red");
timeLeftProgressBar.setStyle("-fx-accent: -bs-error-red;"); timeLeftProgressBar.setStyle("-fx-accent: -bs-error-red;");
} }
@ -231,44 +233,36 @@ public abstract class TradeStepView extends AnchorPane {
protected void setWarningHeadline() { protected void setWarningHeadline() {
if (notificationGroup != null) { if (notificationGroup != null) {
notificationGroup.titledGroupBg.setText("Warning"); notificationGroup.titledGroupBg.setText("Warning");
//notificationGroup.setId("trade-notification-warning");
} }
} }
protected void setInformationHeadline() { protected void setInformationHeadline() {
if (notificationGroup != null) { if (notificationGroup != null) {
notificationGroup.titledGroupBg.setText("Notification"); notificationGroup.titledGroupBg.setText("Notification");
//notificationGroup.titledGroupBg.setId("titled-group-bg-warn");
//notificationGroup.label.setId("titled-group-bg-label-warn");
//notificationLabel.setId("titled-group-bg-label-warn");
} }
} }
protected void setOpenDisputeHeadline() { protected void setOpenDisputeHeadline() {
if (notificationGroup != null) { if (notificationGroup != null) {
notificationGroup.titledGroupBg.setText("Open a dispute"); notificationGroup.titledGroupBg.setText("Open a dispute");
//notificationGroup.setId("trade-notification-dispute");
} }
} }
protected void setDisputeOpenedHeadline() { protected void setDisputeOpenedHeadline() {
if (notificationGroup != null) { if (notificationGroup != null) {
notificationGroup.titledGroupBg.setText("Dispute opened"); notificationGroup.titledGroupBg.setText("Dispute opened");
//notificationGroup.setId("trade-notification-dispute");
} }
} }
protected void setRequestSupportHeadline() { protected void setRequestSupportHeadline() {
if (notificationGroup != null) { if (notificationGroup != null) {
notificationGroup.titledGroupBg.setText("Open support ticket"); notificationGroup.titledGroupBg.setText("Open support ticket");
//notificationGroup.setId("trade-notification-support");
} }
} }
protected void setSupportOpenedHeadline() { protected void setSupportOpenedHeadline() {
if (notificationGroup != null) { if (notificationGroup != null) {
notificationGroup.titledGroupBg.setText("Support ticket opened"); notificationGroup.titledGroupBg.setText("Support ticket opened");
//notificationGroup.setId("trade-notification-support");
} }
} }
@ -350,7 +344,7 @@ public abstract class TradeStepView extends AnchorPane {
break; break;
case DISPUTE_REQUESTED: case DISPUTE_REQUESTED:
onDisputeOpened(); onDisputeOpened();
ownDispute = model.dataModel.getDisputeManager().findOwnDispute(trade.getId()); ownDispute = model.dataModel.disputeManager.findOwnDispute(trade.getId());
ownDispute.ifPresent(dispute -> { ownDispute.ifPresent(dispute -> {
String msg; String msg;
if (dispute.isSupportTicket()) { if (dispute.isSupportTicket()) {
@ -369,7 +363,7 @@ public abstract class TradeStepView extends AnchorPane {
break; break;
case DISPUTE_STARTED_BY_PEER: case DISPUTE_STARTED_BY_PEER:
onDisputeOpened(); onDisputeOpened();
ownDispute = model.dataModel.getDisputeManager().findOwnDispute(trade.getId()); ownDispute = model.dataModel.disputeManager.findOwnDispute(trade.getId());
ownDispute.ifPresent(dispute -> { ownDispute.ifPresent(dispute -> {
String msg; String msg;
if (dispute.isSupportTicket()) { if (dispute.isSupportTicket()) {

View file

@ -27,13 +27,18 @@ import io.bitsquare.gui.main.portfolio.pendingtrades.steps.TradeStepView;
import io.bitsquare.gui.popups.Popup; import io.bitsquare.gui.popups.Popup;
import io.bitsquare.gui.util.Layout; import io.bitsquare.gui.util.Layout;
import io.bitsquare.locale.BSResources; import io.bitsquare.locale.BSResources;
import io.bitsquare.locale.CurrencyUtil;
import io.bitsquare.payment.BlockChainAccountContractData;
import io.bitsquare.payment.PaymentAccountContractData; import io.bitsquare.payment.PaymentAccountContractData;
import io.bitsquare.payment.PaymentMethod; import io.bitsquare.payment.PaymentMethod;
import io.bitsquare.trade.Trade;
import io.bitsquare.user.PopupId; import io.bitsquare.user.PopupId;
import javafx.scene.control.Button; import javafx.scene.control.Button;
import javafx.scene.control.Label; import javafx.scene.control.Label;
import javafx.scene.control.ProgressIndicator; import javafx.scene.control.ProgressIndicator;
import javafx.scene.layout.GridPane; import javafx.scene.layout.GridPane;
import org.fxmisc.easybind.EasyBind;
import org.fxmisc.easybind.Subscription;
import static io.bitsquare.gui.util.FormBuilder.*; import static io.bitsquare.gui.util.FormBuilder.*;
@ -42,6 +47,7 @@ public class BuyerStep2View extends TradeStepView {
private Button paymentStartedButton; private Button paymentStartedButton;
private Label statusLabel; private Label statusLabel;
private ProgressIndicator statusProgressIndicator; private ProgressIndicator statusProgressIndicator;
private Subscription tradeStatePropertySubscription;
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -53,26 +59,54 @@ public class BuyerStep2View extends TradeStepView {
} }
@Override @Override
public void doActivate() { public void activate() {
super.doActivate(); super.activate();
//TODO we get called twice, check why
/* String id = PopupId.SEND_PAYMENT_INFO; if (tradeStatePropertySubscription == null) {
tradeStatePropertySubscription = EasyBind.subscribe(trade.stateProperty(), state -> {
if (state.equals(Trade.State.DEPOSIT_CONFIRMED)) {
PaymentAccountContractData paymentAccountContractData = model.dataModel.getSellersPaymentAccountContractData();
String id = "StartPaymentPopup_" + trade.getId();
if (preferences.showAgain(id) && !BitsquareApp.DEV_MODE) { if (preferences.showAgain(id) && !BitsquareApp.DEV_MODE) {
//TODO use payment method and trade values String message = "";
new Popup().information("You need to transfer now the agreed amount to your trading partner.\n" + if (paymentAccountContractData instanceof BlockChainAccountContractData)
"Please take care that you use the exact data presented here, including the reference text\n" + message = "Please transfer from your external " +
"Please do not click the \"Payment started\" button before you have completed the transfer.\n" + CurrencyUtil.getNameByCode(trade.getOffer().getCurrencyCode()) + " wallet\n" +
"Make sure that you make the transfer soon to not exceed the trading period.") model.formatter.formatFiatWithCode(trade.getTradeVolume()) + " to the bitcoin seller.\n\n" +
.onClose(() -> preferences.dontShowAgain(id)) "Here are the payment account details of the bitcoin seller:\n" +
"" + paymentAccountContractData.getPaymentDetailsForTradePopup() + ".\n\n" +
"You can copy & paste the receivers address from the main screen after closing that popup.";
else if (paymentAccountContractData != null)
message = "Please go to your online banking web page and pay\n" +
model.formatter.formatFiatWithCode(trade.getTradeVolume()) + " to the bitcoin seller.\n\n" +
"Here are the payment account details of the bitcoin seller:\n" +
"" + paymentAccountContractData.getPaymentDetailsForTradePopup() + ".\n\n" +
"Please don't forget to add the reference text " + trade.getShortId() +
" so the receiver can assign your payment to this trade.\n" +
"DO NOT use any additional notice in the reference text like " +
"Bitcoin, Btc, Trade or Bitsquare.\n\n" +
"You can copy & paste the values from the main screen after closing that popup.";
new Popup().headLine("Notification for trade with ID " + trade.getShortId())
.message(message)
.closeButtonText("I understand")
.dontShowAgainId(id, preferences)
.show(); .show();
}*/ }
}
});
}
} }
@Override @Override
public void doDeactivate() { public void deactivate() {
super.doDeactivate(); super.deactivate();
removeStatusProgressIndicator(); removeStatusProgressIndicator();
if (tradeStatePropertySubscription != null) {
tradeStatePropertySubscription.unsubscribe();
tradeStatePropertySubscription = null;
}
} }
@ -117,11 +151,12 @@ public class BuyerStep2View extends TradeStepView {
log.error("Not supported PaymentMethod: " + paymentMethodName); log.error("Not supported PaymentMethod: " + paymentMethodName);
} }
addLabelTextFieldWithCopyIcon(gridPane, ++gridRow, "Reference text:", model.getReference()); if (!(paymentAccountContractData instanceof BlockChainAccountContractData))
addLabelTextFieldWithCopyIcon(gridPane, ++gridRow, "Reference text:", model.dataModel.getReference());
GridPane.setRowSpan(accountTitledGroupBg, gridRow - 3); GridPane.setRowSpan(accountTitledGroupBg, gridRow - 3);
Tuple3<Button, ProgressIndicator, Label> tuple3 = addButtonWithStatus(gridPane, ++gridRow, "Payment started"); Tuple3<Button, ProgressIndicator, Label> tuple3 = addButtonWithStatusAfterGroup(gridPane, ++gridRow, "Payment started");
paymentStartedButton = tuple3.first; paymentStartedButton = tuple3.first;
paymentStartedButton.setOnAction(e -> onPaymentStarted()); paymentStartedButton.setOnAction(e -> onPaymentStarted());
statusProgressIndicator = tuple3.second; statusProgressIndicator = tuple3.second;
@ -136,7 +171,7 @@ public class BuyerStep2View extends TradeStepView {
@Override @Override
protected String getWarningText() { protected String getWarningText() {
setWarningHeadline(); setWarningHeadline();
return "You still have not done your " + model.getCurrencyCode() + " payment!\n" + return "You still have not done your " + model.dataModel.getCurrencyCode() + " payment!\n" +
"Please note that the trade has to be completed until " + "Please note that the trade has to be completed until " +
model.getOpenDisputeTimeAsFormattedDate() + model.getOpenDisputeTimeAsFormattedDate() +
" otherwise the trade will be investigated by the arbitrator."; " otherwise the trade will be investigated by the arbitrator.";
@ -165,7 +200,7 @@ public class BuyerStep2View extends TradeStepView {
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
private void onPaymentStarted() { private void onPaymentStarted() {
if (model.isBootstrapped()) { if (model.p2PService.isBootstrapped()) {
String key = PopupId.PAYMENT_SENT; String key = PopupId.PAYMENT_SENT;
if (preferences.showAgain(key) && !BitsquareApp.DEV_MODE) { if (preferences.showAgain(key) && !BitsquareApp.DEV_MODE) {
new Popup().headLine("Confirmation") new Popup().headLine("Confirmation")
@ -197,7 +232,7 @@ public class BuyerStep2View extends TradeStepView {
statusLabel.setText("Sending message to your trading partner.\n" + statusLabel.setText("Sending message to your trading partner.\n" +
"Please wait until you get the confirmation that the message has arrived."); "Please wait until you get the confirmation that the message has arrived.");
model.fiatPaymentStarted(() -> { model.dataModel.onPaymentStarted(() -> {
// We would not really need an update as the success triggers a screen change // We would not really need an update as the success triggers a screen change
removeStatusProgressIndicator(); removeStatusProgressIndicator();
statusLabel.setText(""); statusLabel.setText("");

View file

@ -43,7 +43,7 @@ public class BuyerStep3View extends TradeStepView {
@Override @Override
protected String getInfoText() { protected String getInfoText() {
return "Waiting for the bitcoin seller's confirmation " + return "Waiting for the bitcoin seller's confirmation " +
"for the receipt of the " + model.getCurrencyCode() + " payment."; "for the receipt of the " + model.dataModel.getCurrencyCode() + " payment.";
} }
@ -55,7 +55,7 @@ public class BuyerStep3View extends TradeStepView {
protected String getWarningText() { protected String getWarningText() {
setInformationHeadline(); setInformationHeadline();
String substitute = model.isBlockChainMethod() ? String substitute = model.isBlockChainMethod() ?
"on the " + model.getCurrencyCode() + "blockchain" : "on the " + model.dataModel.getCurrencyCode() + "blockchain" :
"at your payment provider (e.g. bank)"; "at your payment provider (e.g. bank)";
return "The seller still has not confirmed your payment!\n" + return "The seller still has not confirmed your payment!\n" +
"Please check " + substitute + " if the payment sending was successful.\n" + "Please check " + substitute + " if the payment sending was successful.\n" +

View file

@ -71,16 +71,16 @@ public class BuyerStep4View extends TradeStepView {
} }
@Override @Override
public void doActivate() { public void activate() {
super.doActivate(); super.activate();
model.addBlockChainListener(blockChainListener); model.addBlockChainListener(blockChainListener);
updateDateFromBlockHeight(model.getBestChainHeight()); updateDateFromBlockHeight(model.getBestChainHeight());
} }
@Override @Override
public void doDeactivate() { public void deactivate() {
super.doDeactivate(); super.deactivate();
model.removeBlockChainListener(blockChainListener); model.removeBlockChainListener(blockChainListener);
} }

View file

@ -18,9 +18,8 @@
package io.bitsquare.gui.main.portfolio.pendingtrades.steps.buyer; package io.bitsquare.gui.main.portfolio.pendingtrades.steps.buyer;
import io.bitsquare.app.BitsquareApp; import io.bitsquare.app.BitsquareApp;
import io.bitsquare.common.UserThread; import io.bitsquare.app.Log;
import io.bitsquare.common.util.Tuple2; import io.bitsquare.common.util.Tuple2;
import io.bitsquare.gui.components.InputTextField;
import io.bitsquare.gui.main.portfolio.pendingtrades.PendingTradesViewModel; import io.bitsquare.gui.main.portfolio.pendingtrades.PendingTradesViewModel;
import io.bitsquare.gui.main.portfolio.pendingtrades.steps.TradeStepView; import io.bitsquare.gui.main.portfolio.pendingtrades.steps.TradeStepView;
import io.bitsquare.gui.popups.Popup; import io.bitsquare.gui.popups.Popup;
@ -35,14 +34,9 @@ import static io.bitsquare.gui.util.FormBuilder.*;
public class BuyerStep5View extends TradeStepView { public class BuyerStep5View extends TradeStepView {
private final ChangeListener<Boolean> focusedPropertyListener; private final ChangeListener<Boolean> focusedPropertyListener;
private Label btcTradeAmountLabel; protected Label btcTradeAmountLabel;
private TextField btcTradeAmountTextField; protected Label fiatTradeAmountLabel;
private Label fiatTradeAmountLabel; private TextField withdrawAddressTextField;
private TextField fiatTradeAmountTextField;
private TextField feesTextField;
private TextField securityDepositTextField;
private InputTextField withdrawAddressTextField;
private TextField withdrawAmountTextField;
private Button withdrawButton; private Button withdrawButton;
@ -60,8 +54,8 @@ public class BuyerStep5View extends TradeStepView {
} }
@Override @Override
public void doActivate() { public void activate() {
super.doActivate(); super.activate();
// TODO valid. handler need improvement // TODO valid. handler need improvement
//withdrawAddressTextField.focusedProperty().addListener(focusedPropertyListener); //withdrawAddressTextField.focusedProperty().addListener(focusedPropertyListener);
@ -70,21 +64,22 @@ public class BuyerStep5View extends TradeStepView {
// We need to handle both cases: Address not set and address already set (when returning from other view) // We need to handle both cases: Address not set and address already set (when returning from other view)
// We get address validation after focus out, so first make sure we loose focus and then set it again as hint for user to put address in // We get address validation after focus out, so first make sure we loose focus and then set it again as hint for user to put address in
UserThread.execute(() -> {
//TODO app wide focus //TODO app wide focus
// withdrawAddressTextField.requestFocus();
/* UserThread.execute(() -> { /* UserThread.execute(() -> {
withdrawAddressTextField.requestFocus();
UserThread.execute(() -> {
this.requestFocus(); this.requestFocus();
UserThread.execute(() -> withdrawAddressTextField.requestFocus()); UserThread.execute(() -> withdrawAddressTextField.requestFocus());
});*/
}); });
});*/
hideNotificationGroup(); hideNotificationGroup();
} }
@Override @Override
public void doDeactivate() { public void deactivate() {
super.doDeactivate(); Log.traceCall();
super.deactivate();
//withdrawAddressTextField.focusedProperty().removeListener(focusedPropertyListener); //withdrawAddressTextField.focusedProperty().removeListener(focusedPropertyListener);
// withdrawButton.disableProperty().unbind(); // withdrawButton.disableProperty().unbind();
} }
@ -97,22 +92,18 @@ public class BuyerStep5View extends TradeStepView {
@Override @Override
protected void addContent() { protected void addContent() {
addTitledGroupBg(gridPane, gridRow, 4, "Summary of completed trade ", 0); addTitledGroupBg(gridPane, gridRow, 4, "Summary of completed trade ", 0);
Tuple2<Label, TextField> btcTradeAmountPair = addLabelTextField(gridPane, gridRow, "You have bought:", "", Layout.FIRST_ROW_DISTANCE); Tuple2<Label, TextField> btcTradeAmountPair = addLabelTextField(gridPane, gridRow, getBtcTradeAmountLabel(), model.getTradeVolume(), Layout.FIRST_ROW_DISTANCE);
btcTradeAmountLabel = btcTradeAmountPair.first; btcTradeAmountLabel = btcTradeAmountPair.first;
btcTradeAmountTextField = btcTradeAmountPair.second;
Tuple2<Label, TextField> fiatTradeAmountPair = addLabelTextField(gridPane, ++gridRow, "You have paid:"); Tuple2<Label, TextField> fiatTradeAmountPair = addLabelTextField(gridPane, ++gridRow, getFiatTradeAmountLabel(), model.getFiatVolume());
fiatTradeAmountLabel = fiatTradeAmountPair.first; fiatTradeAmountLabel = fiatTradeAmountPair.first;
fiatTradeAmountTextField = fiatTradeAmountPair.second;
Tuple2<Label, TextField> feesPair = addLabelTextField(gridPane, ++gridRow, "Total fees paid:"); Tuple2<Label, TextField> feesPair = addLabelTextField(gridPane, ++gridRow, "Total fees paid:", model.getTotalFees());
feesTextField = feesPair.second;
Tuple2<Label, TextField> securityDepositPair = addLabelTextField(gridPane, ++gridRow, "Refunded security deposit:"); Tuple2<Label, TextField> securityDepositPair = addLabelTextField(gridPane, ++gridRow, "Refunded security deposit:", model.getSecurityDeposit());
securityDepositTextField = securityDepositPair.second;
addTitledGroupBg(gridPane, ++gridRow, 2, "Withdraw your bitcoins", Layout.GROUP_DISTANCE); addTitledGroupBg(gridPane, ++gridRow, 2, "Withdraw your bitcoins", Layout.GROUP_DISTANCE);
withdrawAmountTextField = addLabelTextField(gridPane, gridRow, "Amount to withdraw:", "", Layout.FIRST_ROW_AND_GROUP_DISTANCE).second; addLabelTextField(gridPane, gridRow, "Amount to withdraw:", model.getPayoutAmount(), Layout.FIRST_ROW_AND_GROUP_DISTANCE);
withdrawAddressTextField = addLabelInputTextField(gridPane, ++gridRow, "Withdraw to address:").second; withdrawAddressTextField = addLabelInputTextField(gridPane, ++gridRow, "Withdraw to address:").second;
withdrawButton = addButtonAfterGroup(gridPane, ++gridRow, "Withdraw to external wallet"); withdrawButton = addButtonAfterGroup(gridPane, ++gridRow, "Withdraw to external wallet");
withdrawButton.setOnAction(e -> { withdrawButton.setOnAction(e -> {
@ -143,36 +134,11 @@ public class BuyerStep5View extends TradeStepView {
withdrawAddressTextField.setText("mhpVDvMjJT1Gn7da44dkq1HXd3wXdFZpXu"); withdrawAddressTextField.setText("mhpVDvMjJT1Gn7da44dkq1HXd3wXdFZpXu");
} }
protected String getBtcTradeAmountLabel() {
/////////////////////////////////////////////////////////////////////////////////////////// return "You have bought:";
// Setters
///////////////////////////////////////////////////////////////////////////////////////////
public void setBtcTradeAmountLabelText(String text) {
btcTradeAmountLabel.setText(text);
} }
public void setFiatTradeAmountLabelText(String text) { protected String getFiatTradeAmountLabel() {
fiatTradeAmountLabel.setText(text); return "You have paid:";
}
public void setBtcTradeAmountTextFieldText(String text) {
btcTradeAmountTextField.setText(text);
}
public void setFiatTradeAmountTextFieldText(String text) {
fiatTradeAmountTextField.setText(text);
}
public void setFeesTextFieldText(String text) {
feesTextField.setText(text);
}
public void setSecurityDepositTextFieldText(String text) {
securityDepositTextField.setText(text);
}
public void setWithdrawAmountTextFieldText(String text) {
withdrawAmountTextField.setText(text);
} }
} }

View file

@ -43,7 +43,7 @@ public class SellerStep2View extends TradeStepView {
@Override @Override
protected String getInfoText() { protected String getInfoText() {
return "The deposit transaction has at least one blockchain confirmation.\n" + return "The deposit transaction has at least one blockchain confirmation.\n" +
"You need to wait until that the bitcoin buyer starts the \" + model.getCurrencyCode() + \" payment."; "You need to wait until that the bitcoin buyer starts the " + model.dataModel.getCurrencyCode() + " payment.";
} }
@ -54,7 +54,7 @@ public class SellerStep2View extends TradeStepView {
@Override @Override
protected String getWarningText() { protected String getWarningText() {
setInformationHeadline(); setInformationHeadline();
return "The buyer still has not done the " + model.getCurrencyCode() + " payment.\n" + return "The buyer still has not done the " + model.dataModel.getCurrencyCode() + " payment.\n" +
"You need to wait until he starts the payment.\n" + "You need to wait until he starts the payment.\n" +
"If the trade has not been completed on " + "If the trade has not been completed on " +
model.getOpenDisputeTimeAsFormattedDate() + model.getOpenDisputeTimeAsFormattedDate() +

View file

@ -18,24 +18,37 @@
package io.bitsquare.gui.main.portfolio.pendingtrades.steps.seller; package io.bitsquare.gui.main.portfolio.pendingtrades.steps.seller;
import io.bitsquare.app.BitsquareApp; import io.bitsquare.app.BitsquareApp;
import io.bitsquare.common.util.Tuple3;
import io.bitsquare.gui.components.TextFieldWithCopyIcon;
import io.bitsquare.gui.components.TitledGroupBg;
import io.bitsquare.gui.main.portfolio.pendingtrades.PendingTradesViewModel; import io.bitsquare.gui.main.portfolio.pendingtrades.PendingTradesViewModel;
import io.bitsquare.gui.main.portfolio.pendingtrades.steps.TradeStepView; import io.bitsquare.gui.main.portfolio.pendingtrades.steps.TradeStepView;
import io.bitsquare.gui.popups.Popup; import io.bitsquare.gui.popups.Popup;
import io.bitsquare.gui.util.Layout;
import io.bitsquare.locale.CurrencyUtil;
import io.bitsquare.payment.BlockChainAccountContractData;
import io.bitsquare.payment.PaymentAccountContractData;
import io.bitsquare.trade.Contract;
import io.bitsquare.trade.Trade;
import io.bitsquare.user.PopupId; import io.bitsquare.user.PopupId;
import io.bitsquare.user.Preferences; import io.bitsquare.user.Preferences;
import javafx.geometry.HPos;
import javafx.geometry.Insets;
import javafx.scene.control.Button; import javafx.scene.control.Button;
import javafx.scene.control.Label; import javafx.scene.control.Label;
import javafx.scene.control.ProgressIndicator; import javafx.scene.control.ProgressIndicator;
import javafx.scene.control.Tooltip;
import javafx.scene.layout.GridPane; import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox; import org.apache.commons.lang3.StringUtils;
import org.fxmisc.easybind.EasyBind;
import org.fxmisc.easybind.Subscription;
import static io.bitsquare.gui.util.FormBuilder.*;
public class SellerStep3View extends TradeStepView { public class SellerStep3View extends TradeStepView {
private Button confirmFiatReceivedButton; private Button confirmFiatReceivedButton;
private Label statusLabel; private Label statusLabel;
private ProgressIndicator statusProgressIndicator; private ProgressIndicator statusProgressIndicator;
private Subscription tradeStatePropertySubscription;
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -48,45 +61,100 @@ public class SellerStep3View extends TradeStepView {
} }
@Override @Override
public void doActivate() { public void activate() {
super.doActivate(); super.activate();
tradeStatePropertySubscription = EasyBind.subscribe(trade.stateProperty(), state -> {
if (state.equals(Trade.State.FIAT_PAYMENT_STARTED_MSG_RECEIVED)) {
PaymentAccountContractData paymentAccountContractData = model.dataModel.getSellersPaymentAccountContractData();
String id = "ConfirmPaymentPopup_" + trade.getId();
if (preferences.showAgain(id) && !BitsquareApp.DEV_MODE) {
String message;
String tradeAmountWithCode = model.formatter.formatFiatWithCode(trade.getTradeVolume());
if (paymentAccountContractData instanceof BlockChainAccountContractData) {
String nameByCode = CurrencyUtil.getNameByCode(trade.getOffer().getCurrencyCode());
String address = ((BlockChainAccountContractData) paymentAccountContractData).getAddress();
message = "Please check on your favorite " +
nameByCode +
" blockchain explorer if the transaction to your receiving address\n" +
"" + address + "\n" +
"has already " +
"sufficient blockchain confirmations.\n" +
"The payment amount has to be " + tradeAmountWithCode + "\n\n" +
"You can copy & paste your " + nameByCode + " address from the main screen after " +
"closing that popup.";
} else {
message = "Please go to your online banking web page and check if you have received " +
tradeAmountWithCode + " from the bitcoin buyer.\n\n" +
"The reference text of the transaction is: " + trade.getShortId();
}
new Popup().headLine("Notification for trade with ID " + trade.getShortId())
.message(message)
.closeButtonText("I understand")
.dontShowAgainId(id, preferences)
.show();
}
}
});
} }
@Override @Override
public void doDeactivate() { public void deactivate() {
super.doDeactivate(); super.deactivate();
if (tradeStatePropertySubscription != null)
tradeStatePropertySubscription.unsubscribe();
statusProgressIndicator.setProgress(0); statusProgressIndicator.setProgress(0);
} }
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Content // Content
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@Override @Override
protected void addContent() { protected void addContent() {
super.addContent(); addTradeInfoBlock();
HBox hBox = new HBox(); TitledGroupBg titledGroupBg = addTitledGroupBg(gridPane, ++gridRow, 2, "Confirm payment receipt", Layout.GROUP_DISTANCE);
hBox.setSpacing(10);
confirmFiatReceivedButton = new Button("Confirm payment receipt"); TextFieldWithCopyIcon field = addLabelTextFieldWithCopyIcon(gridPane, gridRow, "Amount to receive:",
confirmFiatReceivedButton.setDefaultButton(true); model.getFiatAmount(), Layout.FIRST_ROW_AND_GROUP_DISTANCE).second;
field.setCopyWithoutCurrencyPostFix(true);
String paymentDetails = "";
String title = "";
boolean isBlockChain = false;
String nameByCode = CurrencyUtil.getNameByCode(trade.getOffer().getCurrencyCode());
Contract contract = trade.getContract();
if (contract != null) {
PaymentAccountContractData paymentAccountContractData = contract.getSellerPaymentAccountContractData();
if (paymentAccountContractData instanceof BlockChainAccountContractData) {
paymentDetails = ((BlockChainAccountContractData) paymentAccountContractData).getAddress();
title = "Your " + nameByCode + " address:";
isBlockChain = true;
} else {
paymentDetails = paymentAccountContractData.getPaymentDetails();
title = "Your payment account:";
}
}
TextFieldWithCopyIcon paymentDetailsTextField = addLabelTextFieldWithCopyIcon(gridPane, ++gridRow,
title, StringUtils.abbreviate(paymentDetails, 56)).second;
paymentDetailsTextField.setMouseTransparent(false);
paymentDetailsTextField.setTooltip(new Tooltip(paymentDetails));
if (!isBlockChain) {
addLabelTextFieldWithCopyIcon(gridPane, ++gridRow, "Reference text:", model.dataModel.getReference());
GridPane.setRowSpan(titledGroupBg, 3);
}
Tuple3<Button, ProgressIndicator, Label> tuple = addButtonWithStatusAfterGroup(gridPane, ++gridRow, "Confirm payment receipt");
confirmFiatReceivedButton = tuple.first;
confirmFiatReceivedButton.setOnAction(e -> onPaymentReceived()); confirmFiatReceivedButton.setOnAction(e -> onPaymentReceived());
statusProgressIndicator = tuple.second;
statusProgressIndicator = new ProgressIndicator(0); statusLabel = tuple.third;
statusProgressIndicator.setPrefHeight(24);
statusProgressIndicator.setPrefWidth(24);
statusProgressIndicator.setVisible(false);
statusLabel = new Label();
statusLabel.setPadding(new Insets(5, 0, 0, 0));
hBox.getChildren().addAll(confirmFiatReceivedButton, statusProgressIndicator, statusLabel);
GridPane.setRowIndex(hBox, ++gridRow);
GridPane.setColumnIndex(hBox, 0);
GridPane.setHalignment(hBox, HPos.LEFT);
GridPane.setMargin(hBox, new Insets(15, 0, 0, 0));
gridPane.getChildren().add(hBox);
} }
@ -94,19 +162,15 @@ public class SellerStep3View extends TradeStepView {
// Info // Info
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@Override
protected String getInfoBlockTitle() {
return "Confirm payment receipt";
}
@Override @Override
protected String getInfoText() { protected String getInfoText() {
if (model.isBlockChainMethod()) { if (model.isBlockChainMethod()) {
return "The bitcoin buyer has started the " + model.getCurrencyCode() + " payment.\n" + return "The bitcoin buyer has started the " + model.dataModel.getCurrencyCode() + " payment.\n" +
"Check for blockchain confirmations at your Altcoin wallet or block explorer and " + "Check for blockchain confirmations at your cryptocurrency wallet or block explorer and " +
"confirm the payment when you have sufficient blockchain confirmations."; "confirm the payment when you have sufficient blockchain confirmations.";
} else { } else {
return "The bitcoin buyer has started the " + model.getCurrencyCode() + " payment.\n" + return "The bitcoin buyer has started the " + model.dataModel.getCurrencyCode() + " payment.\n" +
"Check at your payment account (e.g. bank account) and confirm when you have " + "Check at your payment account (e.g. bank account) and confirm when you have " +
"received the payment."; "received the payment.";
} }
@ -121,7 +185,7 @@ public class SellerStep3View extends TradeStepView {
protected String getWarningText() { protected String getWarningText() {
setWarningHeadline(); setWarningHeadline();
String substitute = model.isBlockChainMethod() ? String substitute = model.isBlockChainMethod() ?
"on the " + model.getCurrencyCode() + "blockchain" : "on the " + model.dataModel.getCurrencyCode() + "blockchain" :
"at your payment provider (e.g. bank)"; "at your payment provider (e.g. bank)";
return "You still have not confirmed the receipt of the payment!\n" + return "You still have not confirmed the receipt of the payment!\n" +
"Please check " + substitute + " if you have received the payment.\n" + "Please check " + substitute + " if you have received the payment.\n" +
@ -154,8 +218,8 @@ public class SellerStep3View extends TradeStepView {
private void onPaymentReceived() { private void onPaymentReceived() {
log.debug("onPaymentReceived"); log.debug("onPaymentReceived");
if (model.isBootstrapped()) { if (model.p2PService.isBootstrapped()) {
Preferences preferences = model.dataModel.getPreferences(); Preferences preferences = model.dataModel.preferences;
String key = PopupId.PAYMENT_RECEIVED; String key = PopupId.PAYMENT_RECEIVED;
if (preferences.showAgain(key) && !BitsquareApp.DEV_MODE) { if (preferences.showAgain(key) && !BitsquareApp.DEV_MODE) {
new Popup().headLine("Confirmation") new Popup().headLine("Confirmation")
@ -183,7 +247,7 @@ public class SellerStep3View extends TradeStepView {
statusProgressIndicator.setProgress(-1); statusProgressIndicator.setProgress(-1);
statusLabel.setText("Sending message to trading partner..."); statusLabel.setText("Sending message to trading partner...");
model.fiatPaymentReceived(); model.dataModel.onFiatPaymentReceived();
} }
} }

View file

@ -29,4 +29,14 @@ public class SellerStep5View extends BuyerStep5View {
public SellerStep5View(PendingTradesViewModel model) { public SellerStep5View(PendingTradesViewModel model) {
super(model); super(model);
} }
@Override
protected String getBtcTradeAmountLabel() {
return "You have sold:";
}
@Override
protected String getFiatTradeAmountLabel() {
return "You have received:";
}
} }