Improve UI, add notification popups, fee warning,...

This commit is contained in:
Manfred Karrer 2016-02-08 20:09:08 +01:00
parent 9438bc818e
commit 1f8b1b0e01
50 changed files with 634 additions and 435 deletions

View file

@ -50,4 +50,6 @@ public class Tuple3<A, B, C> implements Serializable {
result = 31 * result + (third != null ? third.hashCode() : 0);
return result;
}
}

View file

@ -52,5 +52,6 @@
<version>4.8</version>
</dependency>
</dependencies>
</project>

View file

@ -60,6 +60,11 @@ public class FeePolicy {
return FEE_PER_KB;
}
// Some wallets (Mycelium) don't support higher fees
public static Coin getMinFundingFee() {
return Coin.valueOf(20_000);
}
// 0.001 BTC 0.1% of 1 BTC about 0.4 EUR @ 400 EUR/BTC
public static Coin getCreateOfferFee() {

View file

@ -53,7 +53,6 @@ import java.util.*;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import static com.google.common.base.Preconditions.checkNotNull;
@ -90,7 +89,6 @@ public class WalletService {
private final IntegerProperty numPeers = new SimpleIntegerProperty(0);
private final ObjectProperty<List<Peer>> connectedPeers = new SimpleObjectProperty<>();
public final BooleanProperty shutDownDone = new SimpleBooleanProperty();
private ArbitraryTransactionBloomFilter arbitraryTransactionBloomFilter;
///////////////////////////////////////////////////////////////////////////////////////////
@ -705,86 +703,4 @@ public class WalletService {
}
}
}
public void requestTransactionBlockchainProvider(Transaction transaction, Consumer<Coin> resultHandler) {
// TODO use http server over tor to request tx in question
// https://btc.blockr.io/api/v1/tx/info/9a0c37209a45a0e61a50a62fcb7d0f52f3d6ed41faaf0afc044d642ab541b675
}
public void requestTransactionFromBlockChain(Transaction transaction, Consumer<Coin> resultHandler) {
requestTransactionBlockchainProvider(transaction, resultHandler);
/* arbitraryTransactionBloomFilter = new ArbitraryTransactionBloomFilter(transaction, resultHandler);
PeerGroup peerGroup = walletAppKit.peerGroup();
peerGroup.addEventListener(arbitraryTransactionBloomFilter);
peerGroup.addPeerFilterProvider(arbitraryTransactionBloomFilter);
log.debug("transaction=" + transaction);
log.debug("transaction.fee=" + transaction.getFee());*/
}
private class ArbitraryTransactionBloomFilter extends AbstractPeerEventListener implements PeerFilterProvider {
private final Transaction transaction;
private final Consumer<Coin> resultHandler;
private final Set<TransactionOutPoint> transactionOutPoints;
public ArbitraryTransactionBloomFilter(Transaction transaction, Consumer<Coin> resultHandler) {
this.transaction = transaction;
this.resultHandler = resultHandler;
transactionOutPoints = transaction.getInputs().stream()
.map(e -> e.getOutpoint() != null ? e.getOutpoint() : null)
.filter(e -> e != null)
.collect(Collectors.toSet());
log.debug("transaction=" + transaction);
log.debug("transaction.fee=" + transaction.getFee());
log.debug("outpoints=" + transactionOutPoints);
}
@Override
public long getEarliestKeyCreationTime() {
return System.currentTimeMillis() / 1000;
}
@Override
public void beginBloomFilterCalculation() {
}
@Override
public int getBloomFilterElementCount() {
return transactionOutPoints.size();
}
@Override
public BloomFilter getBloomFilter(int size, double falsePositiveRate, long nTweak) {
BloomFilter filter = new BloomFilter(size, falsePositiveRate, nTweak);
for (TransactionOutPoint transactionOutPoint : transactionOutPoints) {
filter.insert(transactionOutPoint.bitcoinSerialize());
}
return filter;
}
@Override
public boolean isRequiringUpdateAllBloomFilter() {
return false;
}
@Override
public void endBloomFilterCalculation() {
}
@Override
public void onTransaction(Peer peer, Transaction tx) {
if (transactionOutPoints.contains(tx))
transactionOutPoints.remove(tx);
if (transactionOutPoints.isEmpty())
resultHandler.accept(transaction.getFee());
log.debug("## onTransaction: transaction=" + tx);
log.debug("## onTransaction: transaction.fee=" + tx.getFee());
}
}
}

View file

@ -0,0 +1,10 @@
package io.bitsquare.btc.http;
import org.bitcoinj.core.Coin;
import java.io.IOException;
import java.io.Serializable;
public interface BlockchainApiProvider extends Serializable {
Coin getFee(String transactionId) throws IOException, HttpException;
}

View file

@ -0,0 +1,44 @@
package io.bitsquare.btc.http;
import com.google.gson.JsonParser;
import io.bitsquare.app.Log;
import org.bitcoinj.core.Coin;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
// TODO route over tor, support several providers
public class BlockrIOProvider implements BlockchainApiProvider {
private static final Logger log = LoggerFactory.getLogger(BlockrIOProvider.class);
private final HttpClient httpClient;
public static void main(String[] args) throws HttpException, IOException {
Coin fee = new BlockrIOProvider()
.getFee("df67414652722d38b43dcbcac6927c97626a65bd4e76a2e2787e22948a7c5c47");
log.debug("fee " + fee.toFriendlyString());
}
public BlockrIOProvider() {
httpClient = new HttpClient("https://btc.blockr.io/api/v1/tx/info/");
}
@Override
public Coin getFee(String transactionId) throws IOException, HttpException {
Log.traceCall("transactionId=" + transactionId);
try {
return Coin.parseCoin(new JsonParser()
.parse(httpClient.requestWithGET(transactionId))
.getAsJsonObject()
.get("data")
.getAsJsonObject()
.get("fee")
.getAsString());
} catch (IOException | HttpException e) {
log.warn("Error at requesting transaction data from block explorer " + httpClient + "\n" +
"Error =" + e.getMessage());
throw e;
}
}
}

View file

@ -0,0 +1,52 @@
package io.bitsquare.btc.http;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
// TODO route over tor
public class HttpClient implements Serializable {
private String baseUrl;
public HttpClient(String baseUrl) {
this.baseUrl = baseUrl;
}
public String requestWithGET(String param) throws IOException, HttpException {
HttpURLConnection connection = null;
try {
URL url = new URL(baseUrl + param);
connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setConnectTimeout(10000);
if (connection.getResponseCode() == 200) {
return convertInputStreamToString(connection.getInputStream());
} else {
connection.getErrorStream().close();
throw new HttpException(convertInputStreamToString(connection.getErrorStream()));
}
} finally {
if (connection != null)
connection.getInputStream().close();
}
}
private String convertInputStreamToString(InputStream inputStream) throws IOException {
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
StringBuilder stringBuilder = new StringBuilder();
String line;
while ((line = bufferedReader.readLine()) != null) {
stringBuilder.append(line);
}
return stringBuilder.toString();
}
@Override
public String toString() {
return "HttpClient{" +
"baseUrl='" + baseUrl + '\'' +
'}';
}
}

View file

@ -0,0 +1,7 @@
package io.bitsquare.btc.http;
public class HttpException extends Exception {
public HttpException(String message) {
super(message);
}
}

View file

@ -61,7 +61,7 @@ public class PaymentMethod implements Serializable, Comparable {
public static final List<PaymentMethod> ALL_VALUES = new ArrayList<>(Arrays.asList(
OK_PAY = new PaymentMethod(OK_PAY_ID, 0, DAY), // tx instant so min. wait time
PERFECT_MONEY = new PaymentMethod(PERFECT_MONEY_ID, 0, DAY),
SEPA = new PaymentMethod(SEPA_ID, 0, 7 * DAY), // sepa takes 1-3 business days. We use 7 days to include safety for holidays
SEPA = new PaymentMethod(SEPA_ID, 0, 8 * DAY), // sepa takes 1-3 business days. We use 8 days to include safety for holidays
SWISH = new PaymentMethod(SWISH_ID, 0, DAY),
ALI_PAY = new PaymentMethod(ALI_PAY_ID, 0, DAY),
/* FED_WIRE = new PaymentMethod(FED_WIRE_ID, 0, DAY),*/

View file

@ -147,6 +147,7 @@ abstract public class Trade implements Tradable, Model, Serializable {
// Mutable
private DecryptedMsgWithPubKey decryptedMsgWithPubKey;
private Date takeOfferDate = new Date(0); // in some error cases the date is not set and cause null pointers, so we set a default
private int takeOfferDateAsBlockHeight;
private Coin tradeAmount;
private NodeAddress tradingPeerNodeAddress;
protected State state;
@ -164,8 +165,6 @@ abstract public class Trade implements Tradable, Model, Serializable {
private int checkPaymentTimeAsBlockHeight;
private NodeAddress arbitratorNodeAddress;
private String takerPaymentAccountId;
private boolean halfTradePeriodReachedWarningDisplayed;
private boolean tradePeriodOverWarningDisplayed;
private String errorMessage;
transient private StringProperty errorMessageProperty;
transient private ObjectProperty<Coin> tradeAmountProperty;
@ -418,6 +417,14 @@ abstract public class Trade implements Tradable, Model, Serializable {
this.takeOfferDate = takeOfferDate;
}
public int getTakeOfferDateAsBlockHeight() {
return takeOfferDateAsBlockHeight;
}
public void setTakeOfferDateAsBlockHeight(int blockHeight) {
takeOfferDateAsBlockHeight = blockHeight;
}
public void setTradingPeerNodeAddress(NodeAddress tradingPeerNodeAddress) {
if (tradingPeerNodeAddress == null)
log.error("tradingPeerAddress=null");
@ -536,24 +543,6 @@ abstract public class Trade implements Tradable, Model, Serializable {
this.takerPaymentAccountId = takerPaymentAccountId;
}
public void setHalfTradePeriodReachedWarningDisplayed(boolean halfTradePeriodReachedWarningDisplayed) {
this.halfTradePeriodReachedWarningDisplayed = halfTradePeriodReachedWarningDisplayed;
persist();
}
public boolean isHalfTradePeriodReachedWarningDisplayed() {
return halfTradePeriodReachedWarningDisplayed;
}
public void setTradePeriodOverWarningDisplayed(boolean tradePeriodOverWarningDisplayed) {
this.tradePeriodOverWarningDisplayed = tradePeriodOverWarningDisplayed;
persist();
}
public boolean isTradePeriodOverWarningDisplayed() {
return tradePeriodOverWarningDisplayed;
}
public void setContractHash(byte[] contractHash) {
this.contractHash = contractHash;
}
@ -638,8 +627,6 @@ abstract public class Trade implements Tradable, Model, Serializable {
"\n\tcheckPaymentTimeAsBlockHeight=" + checkPaymentTimeAsBlockHeight +
"\n\tarbitratorNodeAddress=" + arbitratorNodeAddress +
"\n\ttakerPaymentAccountId='" + takerPaymentAccountId + '\'' +
"\n\thalfTradePeriodReachedWarningDisplayed=" + halfTradePeriodReachedWarningDisplayed +
"\n\ttradePeriodOverWarningDisplayed=" + tradePeriodOverWarningDisplayed +
"\n\terrorMessage='" + errorMessage + '\'' +
'}';
}

View file

@ -281,6 +281,7 @@ public class TradeManager {
trade = new BuyerAsTakerTrade(offer, amount, model.getPeerNodeAddress(), tradableListStorage);
trade.setTakeOfferDate(new Date());
trade.setTakeOfferDateAsBlockHeight(tradeWalletService.getBestChainHeight());
trade.setTakerPaymentAccountId(paymentAccountId);
initTrade(trade);
@ -382,8 +383,11 @@ public class TradeManager {
return offer.isMyOffer(keyRing);
}
public boolean isMyOfferInBtcBuyerRole(Offer offer) {
return !(isMyOffer(offer) ^ offer.getDirection() == Offer.Direction.BUY);
}
public Optional<Trade> getTradeById(String tradeId) {
return trades.stream().filter(e -> e.getId().equals(tradeId)).findFirst();
}
}

View file

@ -133,10 +133,7 @@ public class BuyerAsOffererProtocol extends TradeProtocol implements BuyerProtoc
TradeTaskRunner taskRunner = new TradeTaskRunner(buyerAsOffererTrade,
() -> handleTaskRunnerSuccess("handle DepositTxPublishedMessage"),
this::handleTaskRunnerFault);
taskRunner.addTasks(
ProcessDepositTxPublishedMessage.class,
AddDepositTxToWallet.class
);
taskRunner.addTasks(ProcessDepositTxPublishedMessage.class);
taskRunner.run();
}

View file

@ -132,10 +132,7 @@ public class SellerAsOffererProtocol extends TradeProtocol implements SellerProt
() -> handleTaskRunnerSuccess("DepositTxPublishedMessage"),
this::handleTaskRunnerFault);
taskRunner.addTasks(
ProcessDepositTxPublishedMessage.class,
AddDepositTxToWallet.class
);
taskRunner.addTasks(ProcessDepositTxPublishedMessage.class);
taskRunner.run();
}

View file

@ -65,6 +65,7 @@ public class SignAndPublishDepositTxAsBuyer extends TradeTask {
trade.setDepositTx(transaction);
trade.setTakeOfferDate(new Date());
trade.setTakeOfferDateAsBlockHeight(processModel.getTradeWalletService().getBestChainHeight());
trade.setState(Trade.State.DEPOSIT_PUBLISHED);
complete();

View file

@ -1,48 +0,0 @@
/*
* This file is part of Bitsquare.
*
* Bitsquare is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
*/
package io.bitsquare.trade.protocol.trade.tasks.offerer;
import io.bitsquare.common.taskrunner.TaskRunner;
import io.bitsquare.trade.Trade;
import io.bitsquare.trade.protocol.trade.tasks.TradeTask;
import org.bitcoinj.core.Transaction;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class AddDepositTxToWallet extends TradeTask {
private static final Logger log = LoggerFactory.getLogger(AddDepositTxToWallet.class);
public AddDepositTxToWallet(TaskRunner taskHandler, Trade trade) {
super(taskHandler, trade);
}
@Override
protected void run() {
try {
runInterceptHook();
// To access tx confidence we need to add that tx into our wallet.
Transaction depositTx = processModel.getTradeWalletService().addTransactionToWallet(trade.getDepositTx());
// update with full tx
trade.setDepositTx(depositTx);
complete();
} catch (Throwable t) {
failed(t);
}
}
}

View file

@ -22,6 +22,7 @@ import io.bitsquare.trade.OffererTrade;
import io.bitsquare.trade.Trade;
import io.bitsquare.trade.protocol.trade.messages.DepositTxPublishedMessage;
import io.bitsquare.trade.protocol.trade.tasks.TradeTask;
import org.bitcoinj.core.Transaction;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -47,9 +48,15 @@ public class ProcessDepositTxPublishedMessage extends TradeTask {
checkTradeId(processModel.getId(), message);
checkNotNull(message);
checkArgument(message.depositTx != null);
trade.setDepositTx(processModel.getWalletService().getTransactionFromSerializedTx(message.depositTx));
// To access tx confidence we need to add that tx into our wallet.
Transaction transactionFromSerializedTx = processModel.getWalletService().getTransactionFromSerializedTx(message.depositTx);
// update with full tx
trade.setDepositTx(processModel.getTradeWalletService().addTransactionToWallet(transactionFromSerializedTx));
trade.setState(Trade.State.DEPOSIT_PUBLISHED_MSG_RECEIVED);
trade.setTakeOfferDate(new Date());
trade.setTakeOfferDateAsBlockHeight(processModel.getTradeWalletService().getBestChainHeight());
if (trade instanceof OffererTrade)
processModel.getOpenOfferManager().closeOpenOffer(trade.getOffer());

View file

@ -63,6 +63,7 @@ public class SignAndPublishDepositTxAsSeller extends TradeTask {
trade.setDepositTx(transaction);
trade.setTakeOfferDate(new Date());
trade.setTakeOfferDateAsBlockHeight(processModel.getTradeWalletService().getBestChainHeight());
trade.setState(Trade.State.DEPOSIT_PUBLISHED);
complete();

View file

@ -21,6 +21,8 @@ import io.bitsquare.app.BitsquareEnvironment;
import io.bitsquare.app.Version;
import io.bitsquare.btc.BitcoinNetwork;
import io.bitsquare.btc.FeePolicy;
import io.bitsquare.btc.http.BlockchainApiProvider;
import io.bitsquare.btc.http.BlockrIOProvider;
import io.bitsquare.locale.CountryUtil;
import io.bitsquare.locale.CurrencyUtil;
import io.bitsquare.locale.TradeCurrency;
@ -65,12 +67,15 @@ public class Preferences implements Serializable {
new BlockChainExplorer("Blockr.io", "https://btc.blockr.io/tx/info/", "https://btc.blockr.io/address/info/"),
new BlockChainExplorer("Biteasy", "https://www.biteasy.com/transactions/", "https://www.biteasy.com/addresses/")
));
private BlockchainApiProvider blockchainApiProvider;
public static List<String> getBtcDenominations() {
return BTC_DENOMINATIONS;
}
private static Locale defaultLocale = Locale.getDefault();
//TODO test with other locales
//private static Locale defaultLocale = Locale.US;
public static Locale getDefaultLocale() {
return defaultLocale;
@ -91,7 +96,6 @@ public class Preferences implements Serializable {
private String btcDenomination = MonetaryFormat.CODE_BTC;
private boolean useAnimations = true;
private boolean useEffects = true;
private boolean displaySecurityDepositInfo = true;
private final ArrayList<TradeCurrency> tradeCurrencies;
private BlockChainExplorer blockChainExplorerMainNet;
private BlockChainExplorer blockChainExplorerTestNet;
@ -130,7 +134,6 @@ public class Preferences implements Serializable {
setUseEffects(persisted.useEffects);
setTradeCurrencies(persisted.tradeCurrencies);
tradeCurrencies = new ArrayList<>(tradeCurrenciesAsObservable);
displaySecurityDepositInfo = persisted.getDisplaySecurityDepositInfo();
setBlockChainExplorerTestNet(persisted.getBlockChainExplorerTestNet());
setBlockChainExplorerMainNet(persisted.getBlockChainExplorerMainNet());
@ -153,6 +156,8 @@ public class Preferences implements Serializable {
defaultTradeCurrency = preferredTradeCurrency;
useTorForBitcoinJ = persisted.getUseTorForBitcoinJ();
blockchainApiProvider = persisted.getBlockchainApiProvider();
try {
setTxFeePerKB(persisted.getTxFeePerKB());
} catch (Exception e) {
@ -175,6 +180,8 @@ public class Preferences implements Serializable {
preferredLocale = getDefaultLocale();
preferredTradeCurrency = getDefaultTradeCurrency();
blockchainApiProvider = new BlockrIOProvider();
storage.queueUpForSave();
}
@ -223,11 +230,6 @@ public class Preferences implements Serializable {
this.useEffectsProperty.set(useEffectsProperty);
}
public void setDisplaySecurityDepositInfo(boolean displaySecurityDepositInfo) {
this.displaySecurityDepositInfo = displaySecurityDepositInfo;
storage.queueUpForSave(2000);
}
public void setBitcoinNetwork(BitcoinNetwork bitcoinNetwork) {
if (this.bitcoinNetwork != bitcoinNetwork)
bitsquareEnvironment.saveBitcoinNetwork(bitcoinNetwork);
@ -302,6 +304,10 @@ public class Preferences implements Serializable {
storage.queueUpForSave();
}
public void setBlockchainApiProvider(BlockchainApiProvider blockchainApiProvider) {
this.blockchainApiProvider = blockchainApiProvider;
storage.queueUpForSave();
}
///////////////////////////////////////////////////////////////////////////////////////////
// Getter
@ -319,10 +325,6 @@ public class Preferences implements Serializable {
return useAnimationsProperty.get();
}
public boolean getDisplaySecurityDepositInfo() {
return displaySecurityDepositInfo;
}
public StringProperty btcDenominationProperty() {
return btcDenominationProperty;
}
@ -425,4 +427,9 @@ public class Preferences implements Serializable {
public boolean getUseTorForBitcoinJ() {
return useTorForBitcoinJ;
}
public BlockchainApiProvider getBlockchainApiProvider() {
return blockchainApiProvider;
}
}

View file

@ -75,7 +75,7 @@ import static io.bitsquare.app.BitsquareEnvironment.APP_NAME_KEY;
public class BitsquareApp extends Application {
private static final Logger log = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(BitsquareApp.class);
public static final boolean DEV_MODE = true;
public static final boolean DEV_MODE = false;
public static final boolean IS_RELEASE_VERSION = !DEV_MODE && true;
private static Environment env;

View file

@ -585,12 +585,14 @@ textfield */
-fx-text-fill: -bs-grey;
-fx-body-color: linear-gradient(to bottom, -bs-content-bg-grey, #F0F0F0);
-fx-outer-border: linear-gradient(to bottom, -bs-bg-grey, #ccc);
/* -fx-body-color: #F0F0F0;
-fx-outer-border: #ccc;*/
-fx-background-color: -fx-shadow-highlight-color,
-fx-outer-border,
-fx-inner-border,
-fx-body-color;
-fx-background-insets: 0 0 -1 0, 0, 1, 2;
-fx-background-radius: 3px, 3px, 2px, 1px;
-fx-background-radius: 1px, 1px, 1px, 1px;
}
#trade-wizard-item-background-active {
@ -598,23 +600,27 @@ textfield */
-fx-font-size: 14;
-fx-body-color: linear-gradient(to bottom, #f1f6f7, #e7f5f9);
-fx-outer-border: linear-gradient(to bottom, #b5e1ef, #6aa4b6);
/* -fx-body-color: #e7f5f9;
-fx-outer-border: #6aa4b6;*/
-fx-background-color: -fx-shadow-highlight-color,
-fx-outer-border,
-fx-inner-border,
-fx-body-color;
-fx-background-insets: 0 0 -1 0, 0, 1, 2;
-fx-background-radius: 3px, 3px, 2px, 1px;
-fx-background-radius: 1px, 1px, 1px, 1px;
}
#trade-wizard-item-background-completed {
-fx-body-color: linear-gradient(to bottom, -bs-content-bg-grey, #E1E9E1);
-fx-outer-border: linear-gradient(to bottom, #99ba9c, #619865);
/* -fx-body-color: #def6df;
-fx-outer-border: #7db581;*/
-fx-background-color: -fx-shadow-highlight-color,
-fx-outer-border,
-fx-inner-border,
-fx-body-color;
-fx-background-insets: 0 0 -1 0, 0, 1, 2;
-fx-background-radius: 3px, 3px, 2px, 1px;
-fx-background-radius: 1px, 1px, 1px, 1px;
}
#open-support-button {

View file

@ -30,7 +30,6 @@ public class HyperlinkWithIcon extends HBox {
icon = new Label();
icon.getStyleClass().add("external-link-icon");
AwesomeDude.setIcon(icon, awesomeIcon);
icon.setMinWidth(20);
icon.setOpacity(0.7);

View file

@ -23,7 +23,6 @@ import io.bitsquare.common.UserThread;
import io.bitsquare.common.util.Tuple2;
import io.bitsquare.gui.Navigation;
import io.bitsquare.gui.common.view.*;
import io.bitsquare.gui.components.SystemNotification;
import io.bitsquare.gui.main.account.AccountView;
import io.bitsquare.gui.main.disputes.DisputesView;
import io.bitsquare.gui.main.funds.FundsView;
@ -454,10 +453,10 @@ public class MainView extends InitializableView<StackPane, MainViewModel> {
notification.visibleProperty().bind(model.showPendingTradesNotification);
buttonHolder.getChildren().add(notification);
model.showPendingTradesNotification.addListener((ov, oldValue, newValue) -> {
/* model.showPendingTradesNotification.addListener((ov, oldValue, newValue) -> {
if (newValue)
SystemNotification.openInfoNotification(title, "You received a new trade message.");
});
});*/
}
private void setupDisputesIcon(Pane buttonHolder) {
@ -478,10 +477,10 @@ public class MainView extends InitializableView<StackPane, MainViewModel> {
notification.visibleProperty().bind(model.showOpenDisputesNotification);
buttonHolder.getChildren().add(notification);
model.showOpenDisputesNotification.addListener((ov, oldValue, newValue) -> {
/* model.showOpenDisputesNotification.addListener((ov, oldValue, newValue) -> {
if (newValue)
SystemNotification.openInfoNotification(title, "You received a dispute message.");
});
});*/
}
private class NavButton extends ToggleButton {

View file

@ -29,10 +29,14 @@ import io.bitsquare.arbitration.DisputeManager;
import io.bitsquare.btc.*;
import io.bitsquare.btc.listeners.BalanceListener;
import io.bitsquare.common.UserThread;
import io.bitsquare.gui.Navigation;
import io.bitsquare.gui.common.model.ViewModel;
import io.bitsquare.gui.common.view.ViewPath;
import io.bitsquare.gui.components.BalanceTextField;
import io.bitsquare.gui.components.BalanceWithConfirmationTextField;
import io.bitsquare.gui.components.TxIdTextField;
import io.bitsquare.gui.main.portfolio.PortfolioView;
import io.bitsquare.gui.main.portfolio.pendingtrades.PendingTradesView;
import io.bitsquare.gui.popups.DisplayAlertMessagePopup;
import io.bitsquare.gui.popups.Popup;
import io.bitsquare.gui.popups.WalletPasswordPopup;
@ -57,6 +61,7 @@ import javafx.collections.ListChangeListener;
import org.bitcoinj.core.*;
import org.bitcoinj.store.BlockStoreException;
import org.fxmisc.easybind.EasyBind;
import org.fxmisc.easybind.Subscription;
import org.fxmisc.easybind.monadic.MonadicBinding;
import org.reactfx.util.FxTimer;
import org.reactfx.util.Timer;
@ -85,6 +90,7 @@ public class MainViewModel implements ViewModel {
private final Preferences preferences;
private final AlertManager alertManager;
private final WalletPasswordPopup walletPasswordPopup;
private Navigation navigation;
private final BSFormatter formatter;
// BTC network
@ -135,7 +141,7 @@ public class MainViewModel implements ViewModel {
ArbitratorManager arbitratorManager, P2PService p2PService, TradeManager tradeManager,
OpenOfferManager openOfferManager, DisputeManager disputeManager, Preferences preferences,
User user, AlertManager alertManager, WalletPasswordPopup walletPasswordPopup,
BSFormatter formatter) {
Navigation navigation, BSFormatter formatter) {
this.user = user;
this.walletService = walletService;
this.tradeWalletService = tradeWalletService;
@ -147,6 +153,7 @@ public class MainViewModel implements ViewModel {
this.preferences = preferences;
this.alertManager = alertManager;
this.walletPasswordPopup = walletPasswordPopup;
this.navigation = navigation;
this.formatter = formatter;
btcNetworkAsString = formatter.formatBitcoinNetwork(preferences.getBitcoinNetwork()) +
@ -362,10 +369,12 @@ public class MainViewModel implements ViewModel {
tradeManager.getTrades().addListener((ListChangeListener<Trade>) change -> {
change.next();
addDisputeStateListeners(change.getAddedSubList());
addTradeStateListeners(change.getAddedSubList());
pendingTradesChanged();
});
pendingTradesChanged();
addDisputeStateListeners(tradeManager.getTrades());
addTradeStateListeners(tradeManager.getTrades());
// arbitratorManager
@ -653,25 +662,25 @@ public class MainViewModel implements ViewModel {
case HALF_REACHED:
id = "displayHalfTradePeriodOver" + trade.getId();
if (preferences.showAgain(id)) {
preferences.dontShowAgain(id);
new Popup().warning("Your trade with ID " + trade.getShortId() +
" has reached the half of the max. allowed trading period and " +
"is still not completed.\n\n" +
"The trade period ends on " + limitDate + "\n\n" +
"Please check your trade state at \"Portfolio/Open trades\" for further information.")
.onClose(() -> preferences.dontShowAgain(id))
.show();
}
break;
case TRADE_PERIOD_OVER:
id = "displayTradePeriodOver" + trade.getId();
if (preferences.showAgain(id)) {
preferences.dontShowAgain(id);
new Popup().warning("Your trade with ID " + trade.getShortId() +
" has reached the max. allowed trading period and is " +
"not completed.\n\n" +
"The trade period ended on " + limitDate + "\n\n" +
"Please check your trade at \"Portfolio/Open trades\" for contacting " +
"the arbitrator.")
.onClose(() -> preferences.dontShowAgain(id))
.show();
}
break;
@ -706,6 +715,142 @@ public class MainViewModel implements ViewModel {
showPendingTradesNotification.set(numPendingTrades > 0);
}
private void addTradeStateListeners(List<? extends Trade> addedTrades) {
addedTrades.stream().forEach(trade -> {
Subscription tradeStateSubscription = EasyBind.subscribe(trade.stateProperty(), newValue -> {
if (newValue != null) {
applyState(trade);
}
});
});
/* 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 applyState(Trade trade) {
Trade.State state = trade.getState();
log.debug("addTradeStateListeners " + state);
boolean isBtcBuyer = tradeManager.isMyOfferInBtcBuyerRole(trade.getOffer());
String headLine = "Notification for trade with ID " + trade.getShortId();
String message = null;
String id = "notificationPopup_" + state + trade.getId();
if (isBtcBuyer) {
switch (state) {
case DEPOSIT_PUBLISHED_MSG_RECEIVED:
message = "Your offer has been accepted by a seller.\n" +
"You need to wait for one blockchain confirmation before starting the payment.";
break;
case DEPOSIT_CONFIRMED:
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.";
break;
/* case FIAT_PAYMENT_RECEIPT_MSG_RECEIVED:
case PAYOUT_TX_COMMITTED:
case PAYOUT_TX_SENT:*/
case PAYOUT_BROAD_CASTED:
message = "The bitcoin seller has confirmed the receipt of your payment and the payout transaction has been published.\n" +
"The trade is now completed and you can withdraw your funds.";
break;
}
} else {
switch (state) {
case DEPOSIT_PUBLISHED_MSG_RECEIVED:
message = "Your offer has been accepted by a buyer.\n" +
"You need to wait for one blockchain confirmation before starting the payment.";
break;
case FIAT_PAYMENT_STARTED_MSG_RECEIVED:
message = "The bitcoin buyer has started the payment.\n" +
"Please check your payment account if you have received his payment.";
break;
/* case FIAT_PAYMENT_RECEIPT_MSG_SENT:
case PAYOUT_TX_RECEIVED:
case PAYOUT_TX_COMMITTED:*/
case PAYOUT_BROAD_CASTED:
message = "The payout transaction has been published.\n" +
"The trade is now completed and you can withdraw your funds.";
}
}
ViewPath currentPath = navigation.getCurrentPath();
boolean isPendingTradesViewCurrentView = currentPath != null &&
currentPath.size() == 3 &&
currentPath.get(2).equals(PendingTradesView.class);
if (message != null) {
//TODO we get that called initially before the navigation is inited
if (isPendingTradesViewCurrentView || currentPath == null) {
if (preferences.showAgain(id))
new Popup().headLine(headLine)
.message(message)
.show();
preferences.dontShowAgain(id);
} else {
if (preferences.showAgain(id))
new Popup().headLine(headLine)
.message(message)
.actionButtonText("Go to \"Portfolio/Open trades\"")
.onAction(() -> {
FxTimer.runLater(Duration.ofMillis(100),
() -> navigation.navigateTo(MainView.class, PortfolioView.class, PendingTradesView.class)
);
})
.show();
preferences.dontShowAgain(id);
}
}
}
private void addDisputeStateListeners(List<? extends Trade> addedTrades) {
addedTrades.stream().forEach(trade -> trade.disputeStateProperty().addListener((observable, oldValue, newValue) -> {
switch (newValue) {

View file

@ -89,7 +89,7 @@ public class FundsView extends ActivatableViewAndModel<TabPane, Activatable> {
String text = "Bitsquare does not use a single application wallet, but dedicated wallets for every trade.\n" +
"Funding of the wallet will be done when needed, for instance when you create or take an offer.\n" +
"Withdrawing funds can be done after a trade is completed.\n" +
"Dedicated wallets help protect user privacy and prevent leaking information of previous trades to other\n" +
"Dedicated wallets help protect user privacy and prevent leaking information of previous trades to other" +
"traders.\n\n" +
"For more background information please see the Bitsquare FAQ on our web page.";
if (preferences.showAgain(key) && !BitsquareApp.DEV_MODE)

View file

@ -346,7 +346,7 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
withdrawToTextField.setPromptText("Fill in your destination address");
if (BitsquareApp.DEV_MODE)
withdrawToTextField.setText("mxAkWWaQBqwqcYstKzqLku3kzR6pbu2zHq");
withdrawToTextField.setText("mhpVDvMjJT1Gn7da44dkq1HXd3wXdFZpXu");
}
private Optional<Tradable> getTradable(WithdrawalListItem item) {

View file

@ -33,12 +33,12 @@ public class BuyOfferView extends OfferView {
@Override
protected String getCreateOfferTabName() {
return "Create offer for buying bitcoin";
return "Create offer";
}
@Override
protected String getTakeOfferTabName() {
return "Take offer for buying bitcoin";
return "Take offer";
}
}

View file

@ -33,12 +33,12 @@ public class SellOfferView extends OfferView {
@Override
protected String getCreateOfferTabName() {
return "Create offer for selling bitcoin";
return "Create offer";
}
@Override
protected String getTakeOfferTabName() {
return "Take offer for selling bitcoin";
return "Take offer";
}
}

View file

@ -165,11 +165,8 @@ class CreateOfferDataModel extends ActivatableDataModel {
boolean isFeeFromFundingTxSufficient() {
// if fee was never set because of api provider not available we check with default value and return true
log.debug("FeePolicy.getFeePerKb() " + FeePolicy.getFeePerKb());
log.debug("feeFromFundingTxProperty " + feeFromFundingTxProperty);
log.debug(">? " + (feeFromFundingTxProperty.get().compareTo(FeePolicy.getFeePerKb()) >= 0));
return feeFromFundingTxProperty.get().equals(Coin.NEGATIVE_SATOSHI) ||
feeFromFundingTxProperty.get().compareTo(FeePolicy.getFeePerKb()) >= 0;
feeFromFundingTxProperty.get().compareTo(FeePolicy.getMinFundingFee()) >= 0;
}
private void addListeners() {
@ -177,7 +174,7 @@ class CreateOfferDataModel extends ActivatableDataModel {
walletService.getWallet().addEventListener(new WalletEventListener() {
@Override
public void onCoinsReceived(Wallet wallet, Transaction tx, Coin prevBalance, Coin newBalance) {
requestFee(tx.getHashAsString());
requestFeeFromBlockchain(tx.getHashAsString());
}
@Override
@ -207,7 +204,7 @@ class CreateOfferDataModel extends ActivatableDataModel {
user.getPaymentAccountsAsObservable().addListener(paymentAccountsChangeListener);
}
private void requestFee(String transactionId) {
private void requestFeeFromBlockchain(String transactionId) {
try {
feeFromFundingTxProperty.set(preferences.getBlockchainApiProvider().getFee(transactionId));
} catch (IOException | HttpException e) {
@ -216,7 +213,7 @@ class CreateOfferDataModel extends ActivatableDataModel {
retryRequestFeeCounter++;
log.warn("We try again after 5 seconds");
// TODO if we have more providers, try another one
UserThread.runAfter(() -> requestFee(transactionId), 5);
UserThread.runAfter(() -> requestFeeFromBlockchain(transactionId), 5);
}
}
}
@ -292,11 +289,6 @@ class CreateOfferDataModel extends ActivatableDataModel {
);
}
void onSecurityDepositInfoDisplayed() {
preferences.setDisplaySecurityDepositInfo(false);
}
public void onPaymentAccountSelected(PaymentAccount paymentAccount) {
if (paymentAccount != null)
this.paymentAccount = paymentAccount;
@ -336,10 +328,6 @@ class CreateOfferDataModel extends ActivatableDataModel {
return addressEntry;
}
boolean getDisplaySecurityDepositInfo() {
return preferences.getDisplaySecurityDepositInfo();
}
public TradeCurrency getTradeCurrency() {
return tradeCurrency;
}

View file

@ -229,16 +229,24 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
private void onShowFundsScreen() {
if (!BitsquareApp.DEV_MODE) {
if (model.getDisplaySecurityDepositInfo()) {
String id = "tradeWalletInfoPopup";
if (model.dataModel.getPreferences().showAgain(id)) {
new Popup().information("To ensure that both traders follow the trade protocol they need to pay a security deposit.\n\n" +
"The deposit will stay in your local trading wallet until the offer gets accepted by another trader.\n" +
"It will be refunded to you after the trade has successfully completed.\n\n" +
"You need to pay in the exact amount displayed to you from your external bitcoin wallet into the " +
"Bitsquare trade wallet. The amount is the sum of the security deposit, the trading fee and " +
"Bitsquare trade wallet.\n" +
"The amount is the sum of the security deposit, the trading fee and " +
"the bitcoin mining fee.\n" +
"You can see the details when you move the mouse over the question mark.").show();
model.onSecurityDepositInfoDisplayed();
"You can see the details when you move the mouse over the question mark.\n\n" +
"Important notice!\n" +
"Please take care that you use a mining fee of at least " +
model.formatter.formatCoinWithCode(FeePolicy.getMinFundingFee()) + " when you transfer bitcoin from your external " +
"wallet to ensure the trade transactions will get into the blockchain.\n" +
"A too low mining fee might result in a delayed trade and will be rejected!")
.closeButtonText("I understand")
.show();
model.dataModel.getPreferences().dontShowAgain(id);
}
}
@ -440,7 +448,7 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
if (!model.dataModel.isFeeFromFundingTxSufficient()) {
new Popup().warning("The mining fee from your funding transaction is not sufficiently high.\n\n" +
"You need to use at least a mining fee of " +
model.formatCoin(FeePolicy.getFeePerKb()) + ".\n\n" +
model.formatCoin(FeePolicy.getMinFundingFee()) + ".\n\n" +
"The fee used in your funding transaction was only " + model.formatCoin(newValue) + ".\n\n" +
"The trade transactions might take too much time to be included in " +
"a block if the fee is too low.\n" +

View file

@ -45,7 +45,7 @@ import static javafx.beans.binding.Bindings.createStringBinding;
class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel> implements ViewModel {
private final BtcValidator btcValidator;
private final P2PService p2PService;
private final BSFormatter formatter;
final BSFormatter formatter;
private final FiatValidator fiatValidator;
private String amountDescription;
@ -322,10 +322,6 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
isPlaceOfferButtonVisible.set(true);
}
void onSecurityDepositInfoDisplayed() {
dataModel.onSecurityDepositInfoDisplayed();
}
public void onPaymentAccountSelected(PaymentAccount paymentAccount) {
dataModel.onPaymentAccountSelected(paymentAccount);
}
@ -431,10 +427,6 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
return formatter;
}
boolean getDisplaySecurityDepositInfo() {
return dataModel.getDisplaySecurityDepositInfo();
}
boolean isSellOffer() {
return dataModel.getDirection() == Offer.Direction.SELL;
}

View file

@ -184,10 +184,6 @@ class TakeOfferDataModel extends ActivatableDataModel {
);
}
void onSecurityDepositInfoDisplayed() {
preferences.setDisplaySecurityDepositInfo(false);
}
public void onPaymentAccountSelected(PaymentAccount paymentAccount) {
if (paymentAccount != null)
this.paymentAccount = paymentAccount;
@ -202,10 +198,6 @@ class TakeOfferDataModel extends ActivatableDataModel {
return offer.getDirection();
}
boolean getDisplaySecurityDepositInfo() {
return preferences.getDisplaySecurityDepositInfo();
}
public Offer getOffer() {
return offer;
}

View file

@ -20,6 +20,7 @@ package io.bitsquare.gui.main.offer.takeoffer;
import de.jensd.fx.fontawesome.AwesomeDude;
import de.jensd.fx.fontawesome.AwesomeIcon;
import io.bitsquare.app.BitsquareApp;
import io.bitsquare.btc.FeePolicy;
import io.bitsquare.common.UserThread;
import io.bitsquare.common.util.Tuple2;
import io.bitsquare.common.util.Tuple3;
@ -214,7 +215,7 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
FxTimer.runLater(Duration.ofMillis(100),
() -> {
new Popup().information(BSResources.get("takeOffer.success.info"))
.actionButtonText("Go to \"Open trades\"")
.actionButtonText("Go to \"Portfolio/Open trades\"")
.onAction(() -> {
close();
FxTimer.runLater(Duration.ofMillis(100),
@ -335,17 +336,23 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
private void onShowPayFundsScreen() {
if (!BitsquareApp.DEV_MODE) {
if (model.getDisplaySecurityDepositInfo()) {
MainView.blur();
String id = "tradeWalletInfoPopup";
if (model.dataModel.getPreferences().showAgain(id)) {
new Popup().information("To ensure that both traders follow the trade protocol they need to pay a security deposit.\n\n" +
"The security deposit will be refunded to you after the trade has successfully completed.\n\n" +
"The deposit will be refunded to you after the trade has successfully completed.\n\n" +
"You need to pay in the exact amount displayed to you from your external bitcoin wallet into the " +
"Bitsquare trade wallet. In case you over pay, you will get it refunded after the trade.\n\n" +
"The amount needed for funding is the sum of the trade amount, the security deposit, " +
"the trading fee and the bitcoin mining fee.\n" +
"You can see the details when you move the mouse over the question mark.").show();
model.onSecurityDepositInfoDisplayed();
"Bitsquare trade wallet.\n" +
"The amount is the sum of the trade amount, the security deposit, the trading fee and " +
"the bitcoin mining fee.\n" +
"You can see the details when you move the mouse over the question mark.\n\n" +
"Important notice!\n" +
"Please take care that you use a mining fee of at least " +
model.formatter.formatCoinWithCode(FeePolicy.getMinFundingFee()) + " when you transfer bitcoin from your external " +
"wallet to ensure the trade transactions will get into the blockchain.\n" +
"A too low mining fee might result in a delayed trade and will be rejected!")
.closeButtonText("I understand")
.show();
model.dataModel.getPreferences().dontShowAgain(id);
}
}

View file

@ -49,7 +49,7 @@ import static javafx.beans.binding.Bindings.createStringBinding;
class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> implements ViewModel {
private final BtcValidator btcValidator;
private P2PService p2PService;
private final BSFormatter formatter;
final BSFormatter formatter;
private String amountRange;
private String addressAsString;
@ -194,10 +194,6 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
dataModel.onPaymentAccountSelected(paymentAccount);
}
void onSecurityDepositInfoDisplayed() {
dataModel.onSecurityDepositInfoDisplayed();
}
///////////////////////////////////////////////////////////////////////////////////////////
// Handle focus
@ -471,10 +467,6 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
return formatter;
}
boolean getDisplaySecurityDepositInfo() {
return dataModel.getDisplaySecurityDepositInfo();
}
boolean isSeller() {
return dataModel.getDirection() == Offer.Direction.BUY;
}

View file

@ -61,10 +61,19 @@ public class BuyerSubView extends TradeSubView {
step4 = new TradeWizardItem(BuyerStep4View.class, "Wait for payout unlock");
step5 = new TradeWizardItem(BuyerStep5View.class, "Completed");
if (model.getLockTime() > 0)
leftVBox.getChildren().setAll(step1, step2, step3, step4, step5);
else
leftVBox.getChildren().setAll(step1, step2, step3, step5);
if (model.getLockTime() > 0) {
addWizardsToGridPane(step1);
addWizardsToGridPane(step2);
addWizardsToGridPane(step3);
addWizardsToGridPane(step4);
addWizardsToGridPane(step5);
} else {
addWizardsToGridPane(step1);
addWizardsToGridPane(step2);
addWizardsToGridPane(step3);
addWizardsToGridPane(step5);
}
}

View file

@ -24,17 +24,14 @@ import io.bitsquare.arbitration.DisputeManager;
import io.bitsquare.btc.FeePolicy;
import io.bitsquare.btc.TradeWalletService;
import io.bitsquare.btc.WalletService;
import io.bitsquare.common.UserThread;
import io.bitsquare.common.crypto.KeyRing;
import io.bitsquare.common.handlers.ErrorMessageHandler;
import io.bitsquare.common.handlers.FaultHandler;
import io.bitsquare.common.handlers.ResultHandler;
import io.bitsquare.gui.Navigation;
import io.bitsquare.gui.common.model.ActivatableDataModel;
import io.bitsquare.gui.main.MainView;
import io.bitsquare.gui.main.disputes.DisputesView;
import io.bitsquare.gui.main.portfolio.PortfolioView;
import io.bitsquare.gui.main.portfolio.closedtrades.ClosedTradesView;
import io.bitsquare.gui.popups.Popup;
import io.bitsquare.gui.popups.SelectDepositTxPopup;
import io.bitsquare.gui.popups.WalletPasswordPopup;
import io.bitsquare.payment.PaymentAccountContractData;
@ -167,25 +164,29 @@ public class PendingTradesDataModel extends ActivatableDataModel {
((SellerTrade) trade).onFiatPaymentReceived();
}
void onWithdrawRequest(String toAddress) {
public void onWithdrawRequest(String toAddress, ResultHandler resultHandler, FaultHandler faultHandler) {
checkNotNull(trade, "trade must not be null");
if (walletService.getWallet().isEncrypted()) {
walletPasswordPopup.onAesKey(aesKey -> doWithdrawRequest(toAddress, aesKey)).show();
walletPasswordPopup.onAesKey(aesKey -> doWithdrawRequest(toAddress, aesKey, resultHandler, faultHandler)).show();
} else
doWithdrawRequest(toAddress, null);
doWithdrawRequest(toAddress, null, resultHandler, faultHandler);
}
private void doWithdrawRequest(String toAddress, KeyParameter aesKey) {
private void doWithdrawRequest(String toAddress, KeyParameter aesKey, ResultHandler resultHandler, FaultHandler faultHandler) {
if (toAddress != null && toAddress.length() > 0) {
tradeManager.onWithdrawRequest(
toAddress,
aesKey,
trade,
() -> UserThread.execute(() -> navigation.navigateTo(MainView.class, PortfolioView.class, ClosedTradesView.class)),
() -> {
resultHandler.handleResult();
},
(errorMessage, throwable) -> {
log.error(errorMessage);
new Popup().error("An error occurred:\n" + throwable.getMessage()).show();
faultHandler.handleFault(errorMessage, throwable);
});
} else {
faultHandler.handleFault("No receiver address defined", null);
}
}

View file

@ -135,6 +135,7 @@ public class PendingTradesViewModel extends ActivatableWithDataModel<PendingTrad
if (dataModel.getTrade() != null) {
tradeStateSubscription = EasyBind.subscribe(dataModel.getTrade().stateProperty(), newValue -> {
log.debug("tradeStateSubscription " + newValue);
if (newValue != null) {
applyState(newValue);
}
@ -191,10 +192,6 @@ public class PendingTradesViewModel extends ActivatableWithDataModel<PendingTrad
dataModel.onFiatPaymentReceived();
}
public void onWithdrawRequest(String withdrawToAddress) {
dataModel.onWithdrawRequest(withdrawToAddress);
}
public void withdrawAddressFocusOut(String text) {
withdrawalButtonDisable.set(!btcAddressValidator.validate(text).isValid);
}
@ -324,7 +321,7 @@ public class PendingTradesViewModel extends ActivatableWithDataModel<PendingTrad
}
public String getOpenDisputeTimeAsFormattedDate() {
return formatter.addBlocksToNowDateFormatted(getOpenDisputeTimeAsBlockHeight() - getBestChainHeight());
return formatter.addBlocksToNowDateFormatted(getOpenDisputeTimeAsBlockHeight() - getBestChainHeight() + (getLockTime() - getBestChainHeight()));
}
public String getReference() {
@ -448,9 +445,13 @@ public class PendingTradesViewModel extends ActivatableWithDataModel<PendingTrad
break;
case WITHDRAW_COMPLETED:
sellerState.set(UNDEFINED);
buyerState.set(PendingTradesViewModel.BuyerState.UNDEFINED);
break;
default:
sellerState.set(UNDEFINED);
buyerState.set(PendingTradesViewModel.BuyerState.UNDEFINED);
log.warn("unhandled processState " + tradeState);
break;
}

View file

@ -20,6 +20,7 @@ package io.bitsquare.gui.main.portfolio.pendingtrades;
import io.bitsquare.gui.main.portfolio.pendingtrades.steps.TradeWizardItem;
import io.bitsquare.gui.main.portfolio.pendingtrades.steps.seller.*;
import javafx.beans.value.ChangeListener;
import javafx.scene.layout.GridPane;
public class SellerSubView extends TradeSubView {
private TradeWizardItem step1;
@ -61,10 +62,20 @@ public class SellerSubView extends TradeSubView {
step4 = new TradeWizardItem(SellerStep4aView.class, "Wait for payout unlock");
step5 = new TradeWizardItem(SellerStep5View.class, "Completed");
if (model.getLockTime() > 0)
leftVBox.getChildren().setAll(step1, step2, step3, step4, step5);
else
leftVBox.getChildren().setAll(step1, step2, step3, step5);
if (model.getLockTime() > 0) {
addWizardsToGridPane(step1);
addWizardsToGridPane(step2);
addWizardsToGridPane(step3);
addWizardsToGridPane(step4);
addWizardsToGridPane(step5);
} else {
addWizardsToGridPane(step1);
addWizardsToGridPane(step2);
addWizardsToGridPane(step3);
addWizardsToGridPane(step5);
GridPane.setRowSpan(tradeProcessTitledGroupBg, 4);
}
}

View file

@ -17,21 +17,18 @@
package io.bitsquare.gui.main.portfolio.pendingtrades;
import io.bitsquare.common.util.Tuple3;
import io.bitsquare.gui.components.TitledGroupBg;
import io.bitsquare.gui.main.portfolio.pendingtrades.steps.TradeStepView;
import io.bitsquare.gui.main.portfolio.pendingtrades.steps.TradeWizardItem;
import io.bitsquare.gui.util.Layout;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static io.bitsquare.gui.util.FormBuilder.addMultilineLabel;
import static io.bitsquare.gui.util.FormBuilder.addTitledGroupBg;
import static io.bitsquare.gui.util.FormBuilder.*;
public abstract class TradeSubView extends HBox {
protected final Logger log = LoggerFactory.getLogger(this.getClass());
@ -41,7 +38,10 @@ public abstract class TradeSubView extends HBox {
protected AnchorPane contentPane;
protected TradeStepView tradeStepView;
private Button openDisputeButton;
private Tuple3<GridPane, TitledGroupBg, Label> notificationTuple;
private NotificationGroup notificationGroup;
protected GridPane leftGridPane;
protected TitledGroupBg tradeProcessTitledGroupBg;
protected int leftGridPaneRowIndex = 0;
///////////////////////////////////////////////////////////////////////////////////////////
@ -63,41 +63,66 @@ public abstract class TradeSubView extends HBox {
tradeStepView.doDeactivate();
if (openDisputeButton != null)
leftVBox.getChildren().remove(openDisputeButton);
if (notificationTuple != null)
leftVBox.getChildren().remove(notificationTuple.first);
leftGridPane.getChildren().remove(openDisputeButton);
if (notificationGroup != null)
notificationGroup.removeItselfFrom(leftGridPane);
}
private void buildViews() {
addLeftBox();
addContentPane();
leftGridPane = new GridPane();
leftGridPane.setPrefWidth(340);
VBox.setMargin(leftGridPane, new Insets(0, 10, 10, 10));
leftGridPane.setHgap(Layout.GRID_GAP);
leftGridPane.setVgap(Layout.GRID_GAP);
leftVBox.getChildren().add(leftGridPane);
leftGridPaneRowIndex = 0;
tradeProcessTitledGroupBg = addTitledGroupBg(leftGridPane, leftGridPaneRowIndex, 1, "Trade process");
addWizards();
openDisputeButton = new Button("Open Dispute");
openDisputeButton.setPrefHeight(40);
openDisputeButton.setPrefWidth(360);
openDisputeButton.setPadding(new Insets(0, 20, 0, 10));
openDisputeButton.setAlignment(Pos.CENTER);
openDisputeButton.setDefaultButton(true);
TitledGroupBg noticeTitledGroupBg = addTitledGroupBg(leftGridPane, leftGridPaneRowIndex, 1, "", Layout.GROUP_DISTANCE);
Label label = addMultilineLabel(leftGridPane, leftGridPaneRowIndex, "", Layout.FIRST_ROW_AND_GROUP_DISTANCE);
openDisputeButton = addButtonAfterGroup(leftGridPane, ++leftGridPaneRowIndex, "Open Dispute");
GridPane.setColumnIndex(openDisputeButton, 0);
openDisputeButton.setId("open-dispute-button");
openDisputeButton.setVisible(false);
openDisputeButton.setManaged(false);
leftVBox.getChildren().add(openDisputeButton);
VBox.setMargin(openDisputeButton, new Insets(10, 0, 0, 0));
// notification fields
GridPane gridPane = new GridPane();
gridPane.setPrefWidth(340);
VBox.setMargin(gridPane, new Insets(10, 10, 10, 10));
gridPane.setHgap(Layout.GRID_GAP);
gridPane.setVgap(Layout.GRID_GAP);
gridPane.setVisible(false);
gridPane.setManaged(false);
leftVBox.getChildren().add(gridPane);
notificationGroup = new NotificationGroup(noticeTitledGroupBg, label, openDisputeButton);
notificationGroup.setLabelAndHeadlineVisible(false);
notificationGroup.setButtonVisible(false);
}
TitledGroupBg titledGroupBg = addTitledGroupBg(gridPane, 0, 4, "Important notice", 20);
Label label = addMultilineLabel(gridPane, 0, Layout.FIRST_ROW_DISTANCE + 20);
notificationTuple = new Tuple3<>(gridPane, titledGroupBg, label);
public static class NotificationGroup {
public final TitledGroupBg titledGroupBg;
public final Label label;
public final Button button;
public NotificationGroup(TitledGroupBg titledGroupBg, Label label, Button button) {
this.titledGroupBg = titledGroupBg;
this.label = label;
this.button = button;
}
public void setLabelAndHeadlineVisible(boolean isVisible) {
titledGroupBg.setVisible(isVisible);
label.setVisible(isVisible);
titledGroupBg.setManaged(isVisible);
label.setManaged(isVisible);
}
public void setButtonVisible(boolean isVisible) {
button.setVisible(isVisible);
button.setManaged(isVisible);
}
public void removeItselfFrom(GridPane leftGridPane) {
leftGridPane.getChildren().remove(titledGroupBg);
leftGridPane.getChildren().remove(label);
leftGridPane.getChildren().remove(button);
}
}
protected void showItem(TradeWizardItem item) {
@ -107,13 +132,22 @@ public abstract class TradeSubView extends HBox {
abstract protected void addWizards();
protected void addWizardsToGridPane(TradeWizardItem tradeWizardItem) {
if (leftGridPaneRowIndex == 0)
GridPane.setMargin(tradeWizardItem, new Insets(Layout.FIRST_ROW_DISTANCE, 0, 0, 0));
GridPane.setRowIndex(tradeWizardItem, leftGridPaneRowIndex++);
leftGridPane.getChildren().add(tradeWizardItem);
GridPane.setRowSpan(tradeProcessTitledGroupBg, leftGridPaneRowIndex);
GridPane.setFillWidth(tradeWizardItem, true);
}
private void createAndAddTradeStepView(Class<? extends TradeStepView> viewClass) {
try {
tradeStepView = viewClass.getDeclaredConstructor(PendingTradesViewModel.class).newInstance(model);
contentPane.getChildren().setAll(tradeStepView);
tradeStepView.setNotificationFields(notificationTuple);
tradeStepView.setOpenDisputeButton(openDisputeButton);
tradeStepView.setNotificationGroup(notificationGroup);
} catch (Exception e) {
e.printStackTrace();
}

View file

@ -18,18 +18,16 @@
package io.bitsquare.gui.main.portfolio.pendingtrades.steps;
import io.bitsquare.arbitration.Dispute;
import io.bitsquare.common.util.Tuple3;
import io.bitsquare.gui.components.TitledGroupBg;
import io.bitsquare.gui.components.TxIdTextField;
import io.bitsquare.gui.components.paymentmethods.PaymentMethodForm;
import io.bitsquare.gui.main.portfolio.pendingtrades.PendingTradesViewModel;
import io.bitsquare.gui.main.portfolio.pendingtrades.TradeSubView;
import io.bitsquare.gui.popups.Popup;
import io.bitsquare.gui.util.Layout;
import io.bitsquare.trade.Trade;
import io.bitsquare.user.Preferences;
import javafx.beans.value.ChangeListener;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressBar;
import javafx.scene.control.TextField;
import javafx.scene.layout.AnchorPane;
@ -63,11 +61,8 @@ public abstract class TradeStepView extends AnchorPane {
protected TitledGroupBg tradeInfoTitledGroupBg;
private TextField timeLeftTextField;
private ProgressBar timeLeftProgressBar;
private GridPane notificationGridPane;
private Label notificationLabel;
private TitledGroupBg notificationTitledGroupBg;
protected Button openDisputeButton;
private TxIdTextField txIdTextField;
protected TradeSubView.NotificationGroup notificationGroup;
///////////////////////////////////////////////////////////////////////////////////////////
@ -135,8 +130,8 @@ public abstract class TradeStepView extends AnchorPane {
if (tradePeriodStateSubscription != null)
tradePeriodStateSubscription.unsubscribe();
if (openDisputeButton != null)
openDisputeButton.setOnAction(null);
if (notificationGroup != null)
notificationGroup.button.setOnAction(null);
if (timer != null)
timer.stop();
@ -212,63 +207,68 @@ public abstract class TradeStepView extends AnchorPane {
// We have the dispute button and text field on the left side, but we handle the content here as it
// is trade state specific
public void setNotificationFields(Tuple3<GridPane, TitledGroupBg, Label> notificationTuple) {
this.notificationGridPane = notificationTuple.first;
this.notificationTitledGroupBg = notificationTuple.second;
this.notificationLabel = notificationTuple.third;
}
public void setOpenDisputeButton(Button openDisputeButton) {
this.openDisputeButton = openDisputeButton;
public void setNotificationGroup(TradeSubView.NotificationGroup notificationGroup) {
this.notificationGroup = notificationGroup;
}
private void showDisputeInfoLabel() {
if (notificationGridPane != null) {
notificationGridPane.setVisible(true);
notificationGridPane.setManaged(true);
if (notificationGroup != null) {
notificationGroup.setLabelAndHeadlineVisible(true);
}
}
private void showOpenDisputeButton() {
if (openDisputeButton != null) {
openDisputeButton.setVisible(true);
openDisputeButton.setManaged(true);
openDisputeButton.setOnAction(e -> {
openDisputeButton.setDisable(true);
if (notificationGroup != null) {
notificationGroup.setButtonVisible(true);
notificationGroup.button.setOnAction(e -> {
notificationGroup.button.setDisable(true);
onDisputeOpened();
setDisputeState();
model.dataModel.onOpenDispute();
});
}
}
protected void setWarningState() {
if (notificationGridPane != null) {
notificationTitledGroupBg.setText("Warning");
//notificationGridPane.setId("trade-notification-warning");
protected void setWarningHeadline() {
if (notificationGroup != null) {
notificationGroup.titledGroupBg.setText("Warning");
//notificationGroup.setId("trade-notification-warning");
}
}
protected void setInformationState() {
if (notificationGridPane != null) {
notificationTitledGroupBg.setText("Notification");
notificationTitledGroupBg.setId("titled-group-bg-warn");
notificationTitledGroupBg.getLabel().setId("titled-group-bg-label-warn");
protected void setInformationHeadline() {
if (notificationGroup != null) {
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 setDisputeState() {
if (notificationGridPane != null) {
notificationTitledGroupBg.setText("Dispute opened");
//notificationGridPane.setId("trade-notification-dispute");
protected void setOpenDisputeHeadline() {
if (notificationGroup != null) {
notificationGroup.titledGroupBg.setText("Open a dispute");
//notificationGroup.setId("trade-notification-dispute");
}
}
protected void setSupportState() {
if (notificationGridPane != null) {
notificationTitledGroupBg.setText("Support ticket opened");
//notificationGridPane.setId("trade-notification-support");
protected void setDisputeOpenedHeadline() {
if (notificationGroup != null) {
notificationGroup.titledGroupBg.setText("Dispute opened");
//notificationGroup.setId("trade-notification-dispute");
}
}
protected void setRequestSupportHeadline() {
if (notificationGroup != null) {
notificationGroup.titledGroupBg.setText("Open support ticket");
//notificationGroup.setId("trade-notification-support");
}
}
protected void setSupportOpenedHeadline() {
if (notificationGroup != null) {
notificationGroup.titledGroupBg.setText("Support ticket opened");
//notificationGroup.setId("trade-notification-support");
}
}
@ -277,10 +277,10 @@ public abstract class TradeStepView extends AnchorPane {
///////////////////////////////////////////////////////////////////////////////////////////
private void showSupportFields() {
if (openDisputeButton != null) {
openDisputeButton.setText("Request support");
openDisputeButton.setId("open-support-button");
openDisputeButton.setOnAction(e -> model.dataModel.onOpenSupportTicket());
if (notificationGroup != null) {
notificationGroup.button.setText("Request support");
notificationGroup.button.setId("open-support-button");
notificationGroup.button.setOnAction(e -> model.dataModel.onOpenSupportTicket());
}
new Popup().warning(trade.errorMessageProperty().getValue()
+ "\n\nPlease report the problem to your arbitrator.\n\n" +
@ -299,8 +299,8 @@ public abstract class TradeStepView extends AnchorPane {
private void showWarning() {
showDisputeInfoLabel();
if (notificationLabel != null)
notificationLabel.setText(getWarningText());
if (notificationGroup != null)
notificationGroup.label.setText(getWarningText());
}
protected String getWarningText() {
@ -315,19 +315,20 @@ public abstract class TradeStepView extends AnchorPane {
private void onOpenForDispute() {
showDisputeInfoLabel();
showOpenDisputeButton();
setOpenDisputeHeadline();
if (notificationLabel != null)
notificationLabel.setText(getOpenForDisputeText());
if (notificationGroup != null)
notificationGroup.label.setText(getOpenForDisputeText());
}
private void onDisputeOpened() {
showDisputeInfoLabel();
showOpenDisputeButton();
applyOnDisputeOpened();
setDisputeOpenedHeadline();
if (openDisputeButton != null)
openDisputeButton.setDisable(true);
if (notificationGroup != null)
notificationGroup.button.setDisable(true);
}
protected String getOpenForDisputeText() {
@ -337,6 +338,11 @@ public abstract class TradeStepView extends AnchorPane {
protected void applyOnDisputeOpened() {
}
protected void hideNotificationGroup() {
notificationGroup.setLabelAndHeadlineVisible(false);
notificationGroup.setButtonVisible(false);
}
private void updateDisputeState(Trade.DisputeState disputeState) {
Optional<Dispute> ownDispute;
switch (disputeState) {
@ -348,16 +354,16 @@ public abstract class TradeStepView extends AnchorPane {
ownDispute.ifPresent(dispute -> {
String msg;
if (dispute.isSupportTicket()) {
setSupportState();
setSupportOpenedHeadline();
msg = "You opened already a support ticket.\n" +
"Please communicate in the support section with the arbitrator.";
} else {
setDisputeState();
setDisputeOpenedHeadline();
msg = "You opened already a dispute.\n" +
"Please communicate in the support section with the arbitrator.";
}
if (notificationLabel != null)
notificationLabel.setText(msg);
if (notificationGroup != null)
notificationGroup.label.setText(msg);
});
break;
@ -367,16 +373,16 @@ public abstract class TradeStepView extends AnchorPane {
ownDispute.ifPresent(dispute -> {
String msg;
if (dispute.isSupportTicket()) {
setSupportState();
setSupportOpenedHeadline();
msg = "Your trading peer opened a support ticket due technical problems.\n" +
"Please communicate in the support section with the arbitrator.";
} else {
setDisputeState();
setDisputeOpenedHeadline();
msg = "Your trading peer opened a dispute.\n" +
"Please communicate in the support section with the arbitrator.";
}
if (notificationLabel != null)
notificationLabel.setText(msg);
if (notificationGroup != null)
notificationGroup.label.setText(msg);
});
break;
case DISPUTE_CLOSED:

View file

@ -39,7 +39,6 @@ public class TradeWizardItem extends Button {
setText(title);
setPrefHeight(40);
setPrefWidth(360);
setPadding(new Insets(0, 20, 0, 10));
setAlignment(Pos.CENTER_LEFT);
setDisabled();
}

View file

@ -56,7 +56,7 @@ public class BuyerStep2View extends TradeStepView {
public void doActivate() {
super.doActivate();
String id = PopupId.SEND_PAYMENT_INFO;
/* String id = PopupId.SEND_PAYMENT_INFO;
if (preferences.showAgain(id) && !BitsquareApp.DEV_MODE) {
//TODO use payment method and trade values
new Popup().information("You need to transfer now the agreed amount to your trading partner.\n" +
@ -65,7 +65,7 @@ public class BuyerStep2View extends TradeStepView {
"Make sure that you make the transfer soon to not exceed the trading period.")
.onClose(() -> preferences.dontShowAgain(id))
.show();
}
}*/
}
@Override
@ -135,9 +135,9 @@ public class BuyerStep2View extends TradeStepView {
@Override
protected String getWarningText() {
setWarningState();
setWarningHeadline();
return "You still have not done your " + model.getCurrencyCode() + " payment!\n" +
"Please not that the trade has to be completed until " +
"Please note that the trade has to be completed until " +
model.getOpenDisputeTimeAsFormattedDate() +
" otherwise the trade will be investigated by the arbitrator.";
}
@ -150,9 +150,8 @@ public class BuyerStep2View extends TradeStepView {
@Override
protected String getOpenForDisputeText() {
return "You have not completed your payment!\n" +
"The max. period for the trade has elapsed (" +
model.getOpenDisputeTimeAsFormattedDate() + ")." +
"\nPlease contact now the arbitrator for opening a dispute.";
"The max. period for the trade has elapsed.\n" +
"\nPlease contact the arbitrator for opening a dispute.";
}
@Override
@ -205,10 +204,10 @@ public class BuyerStep2View extends TradeStepView {
// In case the first send failed we got the support button displayed.
// If it succeeds at a second try we remove the support button again.
if (openDisputeButton != null) {
openDisputeButton.setVisible(false);
openDisputeButton.setManaged(false);
}
//TODO check for support. in case of a dispute we dont want to hid ethe button
/*if (notificationGroup != null) {
notificationGroup.setButtonVisible(false);
}*/
}, errorMessage -> {
removeStatusProgressIndicator();
statusLabel.setText("Sending message to your trading partner failed.\n" +

View file

@ -55,7 +55,7 @@ public class BuyerStep3View extends TradeStepView {
@Override
protected String getWarningText() {
setInformationState();
setInformationHeadline();
String substitute = model.isBlockChainMethod() ?
"on the " + model.getCurrencyCode() + "blockchain" :
"at your payment provider (e.g. bank)";
@ -74,9 +74,8 @@ public class BuyerStep3View extends TradeStepView {
@Override
protected String getOpenForDisputeText() {
return "The seller has not confirmed your payment!\n" +
"The max. period for the trade has elapsed (" +
model.getOpenDisputeTimeAsFormattedDate() +
") and you need to contact now the arbitrator to investigate the problem.";
"The max. period for the trade has elapsed.\n" +
"Please contact the arbitrator for opening a dispute.";
}
@Override

View file

@ -19,7 +19,6 @@ package io.bitsquare.gui.main.portfolio.pendingtrades.steps.buyer;
import io.bitsquare.gui.main.portfolio.pendingtrades.PendingTradesViewModel;
import io.bitsquare.gui.main.portfolio.pendingtrades.steps.TradeStepView;
import io.bitsquare.gui.util.Layout;
import javafx.scene.control.TextField;
import javafx.scene.layout.GridPane;
import org.bitcoinj.core.*;
@ -76,6 +75,7 @@ public class BuyerStep4View extends TradeStepView {
super.doActivate();
model.addBlockChainListener(blockChainListener);
updateDateFromBlockHeight(model.getBestChainHeight());
}
@Override
@ -93,9 +93,9 @@ public class BuyerStep4View extends TradeStepView {
@Override
protected void addContent() {
addTradeInfoBlock();
blockTextField = addLabelTextField(gridPane, gridRow, "Block(s) to wait until lock time elapsed:", "", Layout.FIRST_ROW_DISTANCE).second;
blockTextField = addLabelTextField(gridPane, gridRow, "Block(s) to wait until lock time elapsed:", "").second;
timeTextField = addLabelTextField(gridPane, ++gridRow, "Approx. date when payout gets unlocked:").second;
GridPane.setRowSpan(tradeInfoTitledGroupBg, 4);
GridPane.setRowSpan(tradeInfoTitledGroupBg, 5);
addInfoBlock();
}
@ -125,7 +125,7 @@ public class BuyerStep4View extends TradeStepView {
@Override
protected String getWarningText() {
setInformationState();
setInformationHeadline();
return "The payout transaction is still blocked by the lock time!\n" +
"If the trade has not been completed on " +
model.getOpenDisputeTimeAsFormattedDate() +

View file

@ -23,6 +23,7 @@ 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.steps.TradeStepView;
import io.bitsquare.gui.popups.Popup;
import io.bitsquare.gui.util.Layout;
import javafx.beans.value.ChangeListener;
import javafx.scene.control.Button;
@ -77,6 +78,8 @@ public class BuyerStep5View extends TradeStepView {
UserThread.execute(() -> withdrawAddressTextField.requestFocus());
});*/
});
hideNotificationGroup();
}
@Override
@ -113,22 +116,31 @@ public class BuyerStep5View extends TradeStepView {
withdrawAddressTextField = addLabelInputTextField(gridPane, ++gridRow, "Withdraw to address:").second;
withdrawButton = addButtonAfterGroup(gridPane, ++gridRow, "Withdraw to external wallet");
withdrawButton.setOnAction(e -> {
model.onWithdrawRequest(withdrawAddressTextField.getText());
withdrawButton.setDisable(true);
model.dataModel.onWithdrawRequest(withdrawAddressTextField.getText(),
() -> {
String id = "TradeCompletedInfoPopup";
if (preferences.showAgain(id)) {
new Popup()
.information("You can review your completed trades under \"Portfolio/History\" or " +
"review your transactions under \"Funds/Transactions\"")
.dontShowAgainId(id, preferences)
.show();
}
withdrawButton.setDisable(true);
},
(errorMessage, throwable) -> {
withdrawButton.setDisable(false);
if (throwable == null)
new Popup().warning(errorMessage).show();
else
new Popup().error("An error occurred:\n" + throwable.getMessage()).show();
});
});
if (BitsquareApp.DEV_MODE)
withdrawAddressTextField.setText("mxAkWWaQBqwqcYstKzqLku3kzR6pbu2zHq");
}
///////////////////////////////////////////////////////////////////////////////////////////
// Warning
///////////////////////////////////////////////////////////////////////////////////////////
@Override
protected String getWarningText() {
setInformationState();
return "The trade took a bit longer as expected but has been completed successfully in the allowed trade period.";
withdrawAddressTextField.setText("mhpVDvMjJT1Gn7da44dkq1HXd3wXdFZpXu");
}

View file

@ -56,8 +56,8 @@ public class SellerStep2View extends TradeStepView {
@Override
protected String getWarningText() {
setInformationState();
return "The buyer still has not done the " + model.getCurrencyCode() + " payment!\n" +
setInformationHeadline();
return "The buyer still has not done the " + model.getCurrencyCode() + " payment.\n" +
"You need to wait until he starts the payment.\n" +
"If the trade has not been completed on " +
model.getOpenDisputeTimeAsFormattedDate() +

View file

@ -122,7 +122,7 @@ public class SellerStep3View extends TradeStepView {
@Override
protected String getWarningText() {
setWarningState();
setWarningHeadline();
String substitute = model.isBlockChainMethod() ?
"on the " + model.getCurrencyCode() + "blockchain" :
"at your payment provider (e.g. bank)";
@ -141,9 +141,8 @@ public class SellerStep3View extends TradeStepView {
@Override
protected String getOpenForDisputeText() {
return "You have not confirmed the receipt of the payment!\n" +
"The max. period for the trade has elapsed (" +
model.getOpenDisputeTimeAsFormattedDate() + ")." +
"\nPlease contact now the arbitrator for opening a dispute.";
"The max. period for the trade has elapsed.\n" +
"Please contact the arbitrator for opening a dispute.";
}
@Override

View file

@ -54,7 +54,7 @@ public class SellerStep4bView extends TradeStepView {
@Override
protected String getWarningText() {
setInformationState();
setInformationHeadline();
return "The trading peer has not finalized the payout transaction!\n" +
"He might be offline. You need to wait until he finalizes the payout transaction.\n" +
"If the trade has not been completed on " +
@ -70,9 +70,8 @@ public class SellerStep4bView extends TradeStepView {
@Override
protected String getOpenForDisputeText() {
return "The trading peer has not finalized the payout transaction!\n" +
"The max. period for the trade has elapsed (" +
model.getOpenDisputeTimeAsFormattedDate() + ").\n" +
"Please contact now the arbitrator for opening a dispute.";
"The max. period for the trade has elapsed.\n" +
"Please contact the arbitrator for opening a dispute.";
}
@Override

View file

@ -17,6 +17,7 @@
package io.bitsquare.gui.popups;
import io.bitsquare.common.crypto.KeyRing;
import io.bitsquare.common.util.Tuple2;
import io.bitsquare.gui.Navigation;
import io.bitsquare.gui.main.MainView;
@ -52,6 +53,7 @@ public class OfferDetailsPopup extends Popup {
private final BSFormatter formatter;
private final Preferences preferences;
private final User user;
private KeyRing keyRing;
private final Navigation navigation;
private Offer offer;
private Coin tradeAmount;
@ -64,10 +66,11 @@ public class OfferDetailsPopup extends Popup {
///////////////////////////////////////////////////////////////////////////////////////////
@Inject
public OfferDetailsPopup(BSFormatter formatter, Preferences preferences, User user, Navigation navigation) {
public OfferDetailsPopup(BSFormatter formatter, Preferences preferences, User user, KeyRing keyRing, Navigation navigation) {
this.formatter = formatter;
this.preferences = preferences;
this.user = user;
this.keyRing = keyRing;
this.navigation = navigation;
}
@ -139,6 +142,9 @@ public class OfferDetailsPopup extends Popup {
addLabelTextField(gridPane, ++rowIndex, "Amount:", formatter.formatCoinWithCode(offer.getAmount()));
addLabelTextField(gridPane, ++rowIndex, "Min. amount:", formatter.formatCoinWithCode(offer.getMinAmount()));
}
if (offer.isMyOffer(keyRing) && user.getPaymentAccount(offer.getOffererPaymentAccountId()) != null)
addLabelTextField(gridPane, ++rowIndex, "Payment account:", user.getPaymentAccount(offer.getOffererPaymentAccountId()).getAccountName());
else
addLabelTextField(gridPane, ++rowIndex, "Payment method:", BSResources.get(offer.getPaymentMethod().getId()));
rows = 3;

View file

@ -118,6 +118,14 @@ public class Popup {
return this;
}
public Popup notification(String message) {
// TODO use icons
this.headLine = "Notification";
this.message = message;
setTruncatedMessage();
return this;
}
public Popup information(String message) {
this.headLine = "Information";
this.message = message;
@ -219,7 +227,7 @@ public class Popup {
scene.getStylesheets().setAll(owner.getScene().getStylesheets());
scene.setFill(Color.TRANSPARENT);
stage.setScene(scene);
stage.initModality(Modality.APPLICATION_MODAL);
stage.initModality(Modality.WINDOW_MODAL);
stage.initStyle(StageStyle.TRANSPARENT);
stage.initOwner(owner.getScene().getWindow());
stage.show();
@ -317,10 +325,11 @@ public class Popup {
private void addDontShowAgainCheckBox() {
if (dontShowAgainId != null && preferences != null) {
CheckBox dontShowAgain = addCheckBox(gridPane, ++rowIndex, "Don't show again", 10);
GridPane.setHalignment(dontShowAgain, HPos.RIGHT);
dontShowAgain.setOnAction(e -> {
if (dontShowAgain.isSelected())
CheckBox dontShowAgainCheckBox = addCheckBox(gridPane, rowIndex, "Don't show again", 30);
GridPane.setColumnIndex(dontShowAgainCheckBox, 0);
GridPane.setHalignment(dontShowAgainCheckBox, HPos.LEFT);
dontShowAgainCheckBox.setOnAction(e -> {
if (dontShowAgainCheckBox.isSelected())
preferences.dontShowAgain(dontShowAgainId);
});
}
@ -352,7 +361,7 @@ public class Popup {
GridPane.setHalignment(hBox, HPos.RIGHT);
GridPane.setRowIndex(hBox, ++rowIndex);
GridPane.setColumnSpan(hBox, 2);
GridPane.setMargin(hBox, new Insets(20, 0, 0, 0));
GridPane.setMargin(hBox, new Insets(30, 0, 0, 0));
gridPane.getChildren().add(hBox);
} else {
closeButton.setDefaultButton(true);
@ -366,8 +375,8 @@ public class Popup {
}
protected void setTruncatedMessage() {
if (message != null && message.length() > 800)
truncatedMessage = StringUtils.abbreviate(message, 800);
if (message != null && message.length() > 900)
truncatedMessage = StringUtils.abbreviate(message, 900);
else
truncatedMessage = message;
}

View file

@ -577,23 +577,20 @@ public class FormBuilder {
///////////////////////////////////////////////////////////////////////////////////////////
public static Button addButton(GridPane gridPane, int rowIndex, String title) {
Button button = new Button(title);
button.setDefaultButton(true);
GridPane.setRowIndex(button, rowIndex);
GridPane.setColumnIndex(button, 1);
gridPane.getChildren().add(button);
return button;
return addButton(gridPane, rowIndex, title, 0);
}
public static Button addButtonAfterGroup(GridPane gridPane,
int rowIndex,
String title) {
public static Button addButtonAfterGroup(GridPane gridPane, int rowIndex, String title) {
return addButton(gridPane, rowIndex, title, 15);
}
public static Button addButton(GridPane gridPane, int rowIndex, String title, double top) {
Button button = new Button(title);
button.setDefaultButton(true);
GridPane.setRowIndex(button, rowIndex);
GridPane.setColumnIndex(button, 1);
GridPane.setMargin(button, new Insets(15, 0, 0, 0));
gridPane.getChildren().add(button);
GridPane.setMargin(button, new Insets(top, 0, 0, 0));
return button;
}