impl. feedback from aaron. improve popups, add warning for remove offer, deactivate mainnet,...

This commit is contained in:
Manfred Karrer 2015-11-17 20:54:03 +01:00
parent 2950f6e347
commit f9a31f4b8a
37 changed files with 425 additions and 281 deletions

View File

@ -150,7 +150,7 @@ public class CountryUtil {
}
public static String getNamesByCodesString(List<String> countryCodes) {
return getNamesByCodes(countryCodes).stream().collect(Collectors.joining(", "));
return getNamesByCodes(countryCodes).stream().collect(Collectors.joining(",\n"));
}
private static final String[] countryCodes = new String[]{"AE", "AL", "AR", "AT", "AU", "BA", "BE", "BG", "BH",

View File

@ -37,11 +37,14 @@ public class SepaAccountContractData extends PaymentAccountContractData implemen
private String holderName;
private String iban;
private String bic;
private Set<String> acceptedCountryCodes;
// Dont use a set here as we need a deterministic ordering, otherwise the contract hash does not match
private ArrayList<String> acceptedCountryCodes;
public SepaAccountContractData(String paymentMethod, String id, int maxTradePeriod) {
super(paymentMethod, id, maxTradePeriod);
acceptedCountryCodes = CountryUtil.getAllSepaCountries().stream().map(e -> e.code).collect(Collectors.toSet());
Set<String> acceptedCountryCodesAsSet = CountryUtil.getAllSepaCountries().stream().map(e -> e.code).collect(Collectors.toSet());
acceptedCountryCodes = new ArrayList<>(acceptedCountryCodesAsSet);
acceptedCountryCodes.sort((a, b) -> a.compareTo(b));
}
public void setHolderName(String holderName) {
@ -69,17 +72,17 @@ public class SepaAccountContractData extends PaymentAccountContractData implemen
}
public void addAcceptedCountry(String countryCode) {
acceptedCountryCodes.add(countryCode);
if (!acceptedCountryCodes.contains(countryCode))
acceptedCountryCodes.add(countryCode);
}
public void removeAcceptedCountry(String countryCode) {
acceptedCountryCodes.remove(countryCode);
if (acceptedCountryCodes.contains(countryCode))
acceptedCountryCodes.remove(countryCode);
}
public List<String> getAcceptedCountryCodes() {
List<String> sortedList = new ArrayList<>(acceptedCountryCodes);
sortedList.sort((a, b) -> a.compareTo(b));
return sortedList;
return acceptedCountryCodes;
}
@Override

View File

@ -38,11 +38,13 @@ public class Contract implements Serializable {
@JsonExclude
public static final long serialVersionUID = Version.NETWORK_PROTOCOL_VERSION;
public static final String TAC = "I commit to the trade conditions as defined above.";
public final Offer offer;
private final long tradeAmount;
public final String takeOfferFeeTxID;
public final Address arbitratorAddress;
private final boolean isBuyerOffererOrSellerTaker;
private final boolean isBuyerOffererAndSellerTaker;
private final String offererAccountId;
private final String takerAccountId;
private final PaymentAccountContractData offererPaymentAccountContractData;
@ -62,17 +64,13 @@ public class Contract implements Serializable {
@JsonExclude
private final byte[] takerBtcPubKey;
// TODO some basic TAC
public final String tac = "With my signature I commit to the trading agreement of Bitsquare and to fulfill the trade as defined there.";
public Contract(Offer offer,
Coin tradeAmount,
String takeOfferFeeTxID,
Address buyerAddress,
Address sellerAddress,
Address arbitratorAddress,
boolean isBuyerOffererOrSellerTaker,
boolean isBuyerOffererAndSellerTaker,
String offererAccountId,
String takerAccountId,
PaymentAccountContractData offererPaymentAccountContractData,
@ -89,7 +87,7 @@ public class Contract implements Serializable {
this.tradeAmount = tradeAmount.value;
this.takeOfferFeeTxID = takeOfferFeeTxID;
this.arbitratorAddress = arbitratorAddress;
this.isBuyerOffererOrSellerTaker = isBuyerOffererOrSellerTaker;
this.isBuyerOffererAndSellerTaker = isBuyerOffererAndSellerTaker;
this.offererAccountId = offererAccountId;
this.takerAccountId = takerAccountId;
this.offererPaymentAccountContractData = offererPaymentAccountContractData;
@ -103,44 +101,44 @@ public class Contract implements Serializable {
}
public String getBuyerAccountId() {
return isBuyerOffererOrSellerTaker ? offererAccountId : takerAccountId;
return isBuyerOffererAndSellerTaker ? offererAccountId : takerAccountId;
}
public String getSellerAccountId() {
return isBuyerOffererOrSellerTaker ? takerAccountId : offererAccountId;
return isBuyerOffererAndSellerTaker ? takerAccountId : offererAccountId;
}
public String getBuyerPayoutAddressString() {
return isBuyerOffererOrSellerTaker ? offererPayoutAddressString : takerPayoutAddressString;
return isBuyerOffererAndSellerTaker ? offererPayoutAddressString : takerPayoutAddressString;
}
public String getSellerPayoutAddressString() {
return isBuyerOffererOrSellerTaker ? takerPayoutAddressString : offererPayoutAddressString;
return isBuyerOffererAndSellerTaker ? takerPayoutAddressString : offererPayoutAddressString;
}
public PubKeyRing getBuyerPubKeyRing() {
return isBuyerOffererOrSellerTaker ? offererPubKeyRing : takerPubKeyRing;
return isBuyerOffererAndSellerTaker ? offererPubKeyRing : takerPubKeyRing;
}
public PubKeyRing getSellerPubKeyRing() {
return isBuyerOffererOrSellerTaker ? takerPubKeyRing : offererPubKeyRing;
return isBuyerOffererAndSellerTaker ? takerPubKeyRing : offererPubKeyRing;
}
public byte[] getBuyerBtcPubKey() {
return isBuyerOffererOrSellerTaker ? offererBtcPubKey : takerBtcPubKey;
return isBuyerOffererAndSellerTaker ? offererBtcPubKey : takerBtcPubKey;
}
public byte[] getSellerBtcPubKey() {
return isBuyerOffererOrSellerTaker ? takerBtcPubKey : offererBtcPubKey;
return isBuyerOffererAndSellerTaker ? takerBtcPubKey : offererBtcPubKey;
}
public PaymentAccountContractData getBuyerPaymentAccountContractData() {
return isBuyerOffererOrSellerTaker ? offererPaymentAccountContractData : takerPaymentAccountContractData;
return isBuyerOffererAndSellerTaker ? offererPaymentAccountContractData : takerPaymentAccountContractData;
}
public PaymentAccountContractData getSellerPaymentAccountContractData() {
return isBuyerOffererOrSellerTaker ? takerPaymentAccountContractData : offererPaymentAccountContractData;
return isBuyerOffererAndSellerTaker ? takerPaymentAccountContractData : offererPaymentAccountContractData;
}
public String getPaymentMethodName() {
@ -166,17 +164,19 @@ public class Contract implements Serializable {
@Override
public String toString() {
return "Contract{" +
"tac='" + tac + '\'' +
", offer=" + offer +
"offer=" + offer +
", tradeAmount=" + tradeAmount +
", isBuyerOffererOrSellerTaker=" + isBuyerOffererOrSellerTaker +
", takeOfferFeeTxID='" + takeOfferFeeTxID + '\'' +
", offererAccountID='" + offererAccountId + '\'' +
", takerAccountID='" + takerAccountId + '\'' +
", offererPaymentAccount=" + offererPaymentAccountContractData +
", takerPaymentAccount=" + takerPaymentAccountContractData +
", arbitratorAddress=" + arbitratorAddress +
", isBuyerOffererAndSellerTaker=" + isBuyerOffererAndSellerTaker +
", offererAccountId='" + offererAccountId + '\'' +
", takerAccountId='" + takerAccountId + '\'' +
", offererPaymentAccountContractData=" + offererPaymentAccountContractData +
", takerPaymentAccountContractData=" + takerPaymentAccountContractData +
", offererPubKeyRing=" + offererPubKeyRing +
", takerPubKeyRing=" + takerPubKeyRing +
", buyerAddress=" + buyerAddress +
", sellerAddress=" + sellerAddress +
", offererPayoutAddressString='" + offererPayoutAddressString + '\'' +
", takerPayoutAddressString='" + takerPayoutAddressString + '\'' +
", offererBtcPubKey=" + Arrays.toString(offererBtcPubKey) +
@ -192,7 +192,7 @@ public class Contract implements Serializable {
Contract contract = (Contract) o;
if (tradeAmount != contract.tradeAmount) return false;
if (isBuyerOffererOrSellerTaker != contract.isBuyerOffererOrSellerTaker) return false;
if (isBuyerOffererAndSellerTaker != contract.isBuyerOffererAndSellerTaker) return false;
if (offer != null ? !offer.equals(contract.offer) : contract.offer != null) return false;
if (takeOfferFeeTxID != null ? !takeOfferFeeTxID.equals(contract.takeOfferFeeTxID) : contract.takeOfferFeeTxID != null)
return false;
@ -219,8 +219,7 @@ public class Contract implements Serializable {
if (takerPayoutAddressString != null ? !takerPayoutAddressString.equals(contract.takerPayoutAddressString) : contract.takerPayoutAddressString != null)
return false;
if (!Arrays.equals(offererBtcPubKey, contract.offererBtcPubKey)) return false;
if (!Arrays.equals(takerBtcPubKey, contract.takerBtcPubKey)) return false;
return !(tac != null ? !tac.equals(contract.tac) : contract.tac != null);
return Arrays.equals(takerBtcPubKey, contract.takerBtcPubKey);
}
@ -230,7 +229,7 @@ public class Contract implements Serializable {
result = 31 * result + (int) (tradeAmount ^ (tradeAmount >>> 32));
result = 31 * result + (takeOfferFeeTxID != null ? takeOfferFeeTxID.hashCode() : 0);
result = 31 * result + (arbitratorAddress != null ? arbitratorAddress.hashCode() : 0);
result = 31 * result + (isBuyerOffererOrSellerTaker ? 1 : 0);
result = 31 * result + (isBuyerOffererAndSellerTaker ? 1 : 0);
result = 31 * result + (offererAccountId != null ? offererAccountId.hashCode() : 0);
result = 31 * result + (takerAccountId != null ? takerAccountId.hashCode() : 0);
result = 31 * result + (offererPaymentAccountContractData != null ? offererPaymentAccountContractData.hashCode() : 0);
@ -243,7 +242,7 @@ public class Contract implements Serializable {
result = 31 * result + (takerPayoutAddressString != null ? takerPayoutAddressString.hashCode() : 0);
result = 31 * result + (offererBtcPubKey != null ? Arrays.hashCode(offererBtcPubKey) : 0);
result = 31 * result + (takerBtcPubKey != null ? Arrays.hashCode(takerBtcPubKey) : 0);
result = 31 * result + (tac != null ? tac.hashCode() : 0);
return result;
}
}

View File

@ -53,6 +53,10 @@ public final class Offer implements PubKeyProtectedExpirablePayload {
transient private static final Logger log = LoggerFactory.getLogger(Offer.class);
public static final long TTL = 10 * 60 * 1000; // 10 min.
public final static String TAC_OFFERER = "When placing that offer I accept that anyone who fulfills my conditions can " +
"take that offer.";
public static final String TAC_TAKER = "With taking the offer I commit to the trade conditions as defined.";
public enum Direction {BUY, SELL}
@ -397,14 +401,21 @@ public final class Offer implements PubKeyProtectedExpirablePayload {
", fiatPrice=" + fiatPrice +
", amount=" + amount +
", minAmount=" + minAmount +
", address=" + offererAddress +
", pubKeyRing.hashCode()=" + pubKeyRing.hashCode() +
", offererAddress=" + offererAddress +
", pubKeyRing=" + pubKeyRing +
", paymentMethodName='" + paymentMethodName + '\'' +
", paymentMethodCountryCode='" + paymentMethodCountryCode + '\'' +
", offererPaymentAccountId='" + offererPaymentAccountId + '\'' +
", acceptedCountryCodes=" + acceptedCountryCodes +
", arbitratorAddresses=" + arbitratorAddresses +
", offerFeePaymentTxID='" + offerFeePaymentTxID + '\'' +
", state=" + state +
", stateProperty=" + stateProperty +
", availabilityProtocol=" + availabilityProtocol +
", errorMessageProperty=" + errorMessageProperty +
", TAC_OFFERER=" + TAC_OFFERER +
", TAC_TAKER=" + TAC_TAKER +
'}';
}
}

View File

@ -41,11 +41,12 @@ public class SignAndPublishDepositTxAsBuyer extends TradeTask {
try {
runInterceptHook();
log.debug("getContractAsJson");
log.debug("----------");
log.debug(trade.getContractAsJson());
log.debug("----------");
log.info("\n\n------------------------------------------------------------\n"
+ "Contract as json\n"
+ trade.getContractAsJson()
+ "\n------------------------------------------------------------\n");
byte[] contractHash = Hash.getHash(trade.getContractAsJson());
trade.setContractHash(contractHash);
processModel.getTradeWalletService().takerSignsAndPublishesDepositTx(

View File

@ -48,11 +48,11 @@ public class CreateAndSignContract extends TradeTask {
TradingPeer taker = processModel.tradingPeer;
PaymentAccountContractData offererPaymentAccountContractData = processModel.getPaymentAccountContractData(trade);
PaymentAccountContractData takerPaymentAccountContractData = taker.getPaymentAccountContractData();
boolean isBuyerOffererOrSellerTaker = trade instanceof BuyerAsOffererTrade;
boolean isBuyerOffererAndSellerTaker = trade instanceof BuyerAsOffererTrade;
Address buyerAddress = isBuyerOffererOrSellerTaker ? processModel.getMyAddress() : processModel.getTempTradingPeerAddress();
Address sellerAddress = isBuyerOffererOrSellerTaker ? processModel.getTempTradingPeerAddress() : processModel.getMyAddress();
log.debug("isBuyerOffererOrSellerTaker " + isBuyerOffererOrSellerTaker);
Address buyerAddress = isBuyerOffererAndSellerTaker ? processModel.getMyAddress() : processModel.getTempTradingPeerAddress();
Address sellerAddress = isBuyerOffererAndSellerTaker ? processModel.getTempTradingPeerAddress() : processModel.getMyAddress();
log.debug("isBuyerOffererAndSellerTaker " + isBuyerOffererAndSellerTaker);
log.debug("buyerAddress " + buyerAddress);
log.debug("sellerAddress " + sellerAddress);
Contract contract = new Contract(
@ -62,7 +62,7 @@ public class CreateAndSignContract extends TradeTask {
buyerAddress,
sellerAddress,
trade.getArbitratorAddress(),
isBuyerOffererOrSellerTaker,
isBuyerOffererAndSellerTaker,
processModel.getAccountId(),
taker.getAccountId(),
offererPaymentAccountContractData,

View File

@ -44,6 +44,11 @@ public class CreateAndSignDepositTxAsSeller extends TradeTask {
Coin sellerInputAmount = FeePolicy.SECURITY_DEPOSIT.add(FeePolicy.TX_FEE).add(trade.getTradeAmount());
Coin msOutputAmount = sellerInputAmount.add(FeePolicy.SECURITY_DEPOSIT);
log.info("\n\n------------------------------------------------------------\n"
+ "Contract as json\n"
+ trade.getContractAsJson()
+ "\n------------------------------------------------------------\n");
byte[] contractHash = Hash.getHash(trade.getContractAsJson());
trade.setContractHash(contractHash);
PreparedDepositTxAndOffererInputs result = processModel.getTradeWalletService().offererCreatesAndSignsDepositTx(

View File

@ -40,10 +40,10 @@ public class SignAndPublishDepositTxAsSeller extends TradeTask {
protected void run() {
try {
runInterceptHook();
log.debug("getContractAsJson");
log.debug("----------");
log.debug(trade.getContractAsJson());
log.debug("----------");
log.info("\n\n------------------------------------------------------------\n"
+ "Contract as json\n"
+ trade.getContractAsJson()
+ "\n------------------------------------------------------------\n");
byte[] contractHash = Hash.getHash(trade.getContractAsJson());
trade.setContractHash(contractHash);

View File

@ -41,7 +41,7 @@ public class BroadcastTakeOfferFeeTx extends TradeTask {
new FutureCallback<Transaction>() {
@Override
public void onSuccess(Transaction transaction) {
log.debug("Take offer fee published successfully. Transaction ID = " + transaction.getHashAsString());
log.debug("Trading fee published successfully. Transaction ID = " + transaction.getHashAsString());
trade.setState(Trade.State.TAKER_FEE_PAID);
complete();
@ -49,7 +49,7 @@ public class BroadcastTakeOfferFeeTx extends TradeTask {
@Override
public void onFailure(@NotNull Throwable t) {
appendToErrorMessage("Take offer fee payment failed. Maybe your network connection was lost. Please try again.");
appendToErrorMessage("Trading fee payment failed. Maybe your network connection was lost. Please try again.");
failed(t);
}
});

View File

@ -47,10 +47,10 @@ public class VerifyAndSignContract extends TradeTask {
PaymentAccountContractData offererPaymentAccountContractData = offerer.getPaymentAccountContractData();
PaymentAccountContractData takerPaymentAccountContractData = processModel.getPaymentAccountContractData(trade);
boolean isBuyerOffererOrSellerTaker = trade instanceof SellerAsTakerTrade;
Address buyerAddress = isBuyerOffererOrSellerTaker ? processModel.getTempTradingPeerAddress() : processModel.getMyAddress();
Address sellerAddress = isBuyerOffererOrSellerTaker ? processModel.getMyAddress() : processModel.getTempTradingPeerAddress();
log.debug("isBuyerOffererOrSellerTaker " + isBuyerOffererOrSellerTaker);
boolean isBuyerOffererAndSellerTaker = trade instanceof SellerAsTakerTrade;
Address buyerAddress = isBuyerOffererAndSellerTaker ? processModel.getTempTradingPeerAddress() : processModel.getMyAddress();
Address sellerAddress = isBuyerOffererAndSellerTaker ? processModel.getMyAddress() : processModel.getTempTradingPeerAddress();
log.debug("isBuyerOffererAndSellerTaker " + isBuyerOffererAndSellerTaker);
log.debug("buyerAddress " + buyerAddress);
log.debug("sellerAddress " + sellerAddress);
@ -61,7 +61,7 @@ public class VerifyAndSignContract extends TradeTask {
buyerAddress,
sellerAddress,
trade.getArbitratorAddress(),
isBuyerOffererOrSellerTaker,
isBuyerOffererAndSellerTaker,
offerer.getAccountId(),
processModel.getAccountId(),
offererPaymentAccountContractData,

View File

@ -67,7 +67,6 @@ public class User implements Serializable {
private Alert displayedAlert;
@Nullable
private List<Arbitrator> acceptedArbitrators = new ArrayList<>();
@Nullable
private Arbitrator registeredArbitrator;
@ -244,7 +243,6 @@ public class User implements Serializable {
return registeredArbitrator;
}
@Nullable
public List<Arbitrator> getAcceptedArbitrators() {
return acceptedArbitrators;
}

View File

@ -167,7 +167,7 @@ public class BitsquareApp extends Application {
showFPSWindow();
else if (new KeyCodeCombination(KeyCode.E, KeyCombination.SHORTCUT_DOWN).match(keyEvent))
showEmptyWalletPopup();
else if (new KeyCodeCombination(KeyCode.A, KeyCombination.SHORTCUT_DOWN).match(keyEvent))
else if (new KeyCodeCombination(KeyCode.M, KeyCombination.SHORTCUT_DOWN).match(keyEvent))
showSendAlertMessagePopup();
});
@ -326,6 +326,7 @@ public class BitsquareApp extends Application {
private void restart() {
//TODO
stop();
//gracefulShutDown(UpdateFX::restartApp);
}
}

View File

@ -32,6 +32,7 @@ import org.bitcoinj.core.Coin;
public class BalanceTextField extends AnchorPane {
private static WalletService walletService;
private BalanceListener balanceListener;
public static void setWalletService(WalletService walletService) {
BalanceTextField.walletService = walletService;
@ -61,15 +62,20 @@ public class BalanceTextField extends AnchorPane {
public void setup(Address address, BSFormatter formatter) {
this.formatter = formatter;
walletService.addBalanceListener(new BalanceListener(address) {
balanceListener = new BalanceListener(address) {
@Override
public void onBalanceChanged(Coin balance) {
updateBalance(balance);
}
});
};
walletService.addBalanceListener(balanceListener);
updateBalance(walletService.getBalanceForAddress(address));
}
public void disarm() {
walletService.removeBalanceListener(balanceListener);
}
///////////////////////////////////////////////////////////////////////////////////////////
// Private methods

View File

@ -111,12 +111,16 @@ public abstract class PaymentMethodForm {
protected void addAllowedPeriod() {
long hours = paymentAccount.getPaymentMethod().getMaxTradePeriod() / 6;
String displayText = hours + " hours";
if (hours == 24)
if (hours == 1)
displayText = "1 hour";
else if (hours == 24)
displayText = "1 day";
if (hours > 24)
else if (hours > 24)
displayText = hours / 24 + " days";
addLabelTextField(gridPane, ++gridRow, "Max. allowed trade period:", displayText);
displayText += " (Max. permitted period until the trade needs to be completed)";
addLabelTextField(gridPane, ++gridRow, "Max. permitted trade period:", displayText);
}
abstract protected void autoFillNameTextField();

View File

@ -202,10 +202,16 @@ public class SeedWordsView extends ActivatableView<GridPane, Void> {
log.debug("Wallet restored with seed words");
new Popup()
.information("Wallet restored successfully with the new seed words.\n\n" +
"You need to shut down and restart the application.")
.closeButtonText("Shut down")
.onClose(() -> BitsquareApp.shutDownHandler.run()).show();
//TODO
/* new Popup()
.information("Wallet restored successfully with the new seed words.\n\n" +
"You need to restart now the application.")
.closeButtonText("Restart")
.onClose(() -> BitsquareApp.restartDownHandler.run()).show();
.onClose(() -> BitsquareApp.restartDownHandler.run()).show();*/
}),
throwable -> UserThread.execute(() -> {
log.error(throwable.getMessage());

View File

@ -37,7 +37,6 @@ import io.bitsquare.gui.main.account.settings.AccountSettingsView;
import io.bitsquare.gui.main.offer.OfferView;
import io.bitsquare.gui.main.portfolio.PortfolioView;
import io.bitsquare.gui.main.portfolio.openoffer.OpenOffersView;
import io.bitsquare.gui.main.portfolio.pendingtrades.PendingTradesView;
import io.bitsquare.gui.popups.OfferDetailsPopup;
import io.bitsquare.gui.popups.Popup;
import io.bitsquare.gui.util.BSFormatter;
@ -226,9 +225,13 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
private void onShowFundsScreen() {
if (!BitsquareApp.DEV_MODE) {
if (model.getDisplaySecurityDepositInfo()) {
new Popup().information("To ensure that both traders behave fair they need to pay a security deposit.\n\n" +
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.").show();
"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 " +
"the Bitcoin mining fee.\n" +
"You can see the details when you move the mouse over the question mark.").show();
model.onSecurityDepositInfoDisplayed();
}
@ -435,12 +438,16 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
UserThread.runAfter(() -> {
new Popup().headLine(BSResources.get("createOffer.success.headline"))
.message(BSResources.get("createOffer.success.info"))
.onClose(() -> {
navigation.navigateTo(MainView.class, PortfolioView.class, PendingTradesView.class);
.actionButtonText("Go to \"Open offers\"")
.onAction(() -> {
close();
UserThread.runAfter(() ->
navigation.navigateTo(MainView.class, PortfolioView.class, OpenOffersView.class),
100, TimeUnit.MILLISECONDS);
})
.onClose(() -> close())
.show();
}, 300, TimeUnit.MILLISECONDS);
}, 100, TimeUnit.MILLISECONDS);
}
};
}

View File

@ -289,7 +289,7 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
if (newValue != null) {
if (offer.getState() == Offer.State.OFFER_FEE_PAID)
this.errorMessage.set(newValue +
"\n\nThe create offer fee is already paid. In the worst case you have lost that fee. " +
"\n\nThe offer fee is already paid. In the worst case you have lost that fee. " +
"We are sorry about that but keep in mind it is a very small amount.\n" +
"Please try to restart you application and check your network connection to see if you can resolve the issue.");
else

View File

@ -239,23 +239,31 @@ public class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
private void onRemoveOpenOffer(Offer offer) {
if (model.isAuthenticated()) {
model.onRemoveOpenOffer(offer,
() -> {
log.debug("Remove offer was successful");
new Popup().information("You can withdraw the funds you paid in from the funds screens.").show();
navigation.navigateTo(MainView.class, FundsView.class, WithdrawalView.class);
},
(message) -> {
log.error(message);
new Popup().warning("Remove offer failed:\n" + message).show();
});
new Popup().warning("Are you sure you want to remove that offer?\n" +
"The offer fee you have paid will be lost if you remove that offer.")
.actionButtonText("Remove offer")
.onAction(() -> doRemoveOffer(offer))
.closeButtonText("Don't remove the offer")
.show();
} else {
new Popup().warning("You need to wait until your client is authenticated in the network.\n" +
"That might take up to about 2 minutes at startup.").show();
}
}
private void doRemoveOffer(Offer offer) {
model.onRemoveOpenOffer(offer,
() -> {
log.debug("Remove offer was successful");
new Popup().information("You can withdraw the funds you paid in from the funds screens.").show();
navigation.navigateTo(MainView.class, FundsView.class, WithdrawalView.class);
},
(message) -> {
log.error(message);
new Popup().warning("Remove offer failed:\n" + message).show();
});
}
private void showWarning(String masthead, String message, Class target) {
new Popup().information(masthead + "\n\n" + message)
.onAction(() -> {

View File

@ -127,6 +127,9 @@ class TakeOfferDataModel extends ActivatableDataModel {
void initWithData(Offer offer) {
this.offer = offer;
addressEntry = walletService.getAddressEntryByOfferId(offer.getId());
checkNotNull(addressEntry, "addressEntry must not be null");
ObservableList<PaymentAccount> possiblePaymentAccounts = getPossiblePaymentAccounts();
checkArgument(!possiblePaymentAccounts.isEmpty(), "possiblePaymentAccounts.isEmpty()");
paymentAccount = possiblePaymentAccounts.get(0);
@ -136,9 +139,6 @@ class TakeOfferDataModel extends ActivatableDataModel {
calculateVolume();
calculateTotalToPay();
addressEntry = walletService.getAddressEntryByOfferId(offer.getId());
checkNotNull(addressEntry, "addressEntry must not be null");
balanceListener = new BalanceListener(addressEntry.getAddress()) {
@Override
public void onBalanceChanged(@NotNull Coin balance) {
@ -255,6 +255,8 @@ class TakeOfferDataModel extends ActivatableDataModel {
amountAsCoin.get() != null &&
!amountAsCoin.get().isZero()) {
volumeAsFiat.set(new ExchangeRate(offer.getPrice()).coinToFiat(amountAsCoin.get()));
updateBalance(walletService.getBalanceForAddress(addressEntry.getAddress()));
}
}

View File

@ -225,12 +225,16 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
if (newValue && model.getTrade() != null && model.getTrade().errorMessageProperty().get() == null) {
UserThread.runAfter(() -> {
new Popup().information(BSResources.get("takeOffer.success.info"))
.onClose(() -> {
navigation.navigateTo(MainView.class, PortfolioView.class, PendingTradesView.class);
.actionButtonText("Go to \"Open trades\"")
.onAction(() -> {
close();
UserThread.runAfter(() ->
navigation.navigateTo(MainView.class, PortfolioView.class, PendingTradesView.class),
100, TimeUnit.MILLISECONDS);
})
.onClose(() -> close())
.show();
}, 300, TimeUnit.MILLISECONDS);
}, 100, TimeUnit.MILLISECONDS);
}
});
@ -322,7 +326,7 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
private void onTakeOffer() {
Offer offer = model.getOffer();
if (model.getShowTakeOfferConfirmation()) {
offerDetailsPopup.onTakeOffer(() -> model.onTakeOffer()).show(offer);
offerDetailsPopup.onTakeOffer(() -> model.onTakeOffer()).show(offer, model.dataModel.amountAsCoin.get());
} else {
if (model.hasAcceptedArbitrators()) {
model.onTakeOffer();
@ -351,9 +355,13 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
Dialog.Actions.CLOSE.handle(actionEvent);
}
});
new Popup().information("To ensure that both traders behave fair they need to pay a security deposit.\n\n" +
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.").show();
"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 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();
}

View File

@ -288,7 +288,7 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
"Please try to restart you application and check your network connection to see if you can resolve the issue.";
break;
case TAKER_FEE_PAID:
appendMsg = "\n\nThe take offer fee is already paid. In the worst case you have lost that fee. " +
appendMsg = "\n\nThe trading fee is already paid. In the worst case you have lost that fee. " +
"We are sorry about that but keep in mind it is a very small amount.\n" +
"Please try to restart you application and check your network connection to see if you can resolve the issue.";
break;

View File

@ -72,25 +72,34 @@ public class OpenOffersView extends ActivatableViewAndModel<VBox, OpenOffersView
table.setItems(model.getList());
}
private void onCancelOpenOffer(OpenOffer openOffer) {
private void onRemoveOpenOffer(OpenOffer openOffer) {
if (model.isAuthenticated()) {
model.onCancelOpenOffer(openOffer,
() -> {
log.debug("Remove offer was successful");
new Popup().information("You can withdraw the funds you paid in from the funds screens.")
.onClose(() -> navigation.navigateTo(MainView.class, FundsView.class, WithdrawalView.class))
.show();
},
(message) -> {
log.error(message);
new Popup().warning("Remove offer failed:\n" + message).show();
});
new Popup().warning("Are you sure you want to remove that offer?\n" +
"The offer fee you have paid will be lost if you remove that offer.")
.actionButtonText("Remove offer")
.onAction(() -> doRemoveOpenOffer(openOffer))
.closeButtonText("Don't remove the offer")
.show();
} else {
new Popup().warning("You need to wait until your client is authenticated in the network.\n" +
"That might take up to about 2 minutes at startup.").show();
}
}
private void doRemoveOpenOffer(OpenOffer openOffer) {
model.onCancelOpenOffer(openOffer,
() -> {
log.debug("Remove offer was successful");
new Popup().information("You can withdraw the funds you paid in from the funds screens.")
.onClose(() -> navigation.navigateTo(MainView.class, FundsView.class, WithdrawalView.class))
.show();
},
(message) -> {
log.error(message);
new Popup().warning("Remove offer failed:\n" + message).show();
});
}
/* private void openOfferDetails(OpenOfferListItem item) {
Offer offer = item.getOffer();
int rowIndex = 0;
@ -284,7 +293,7 @@ public class OpenOffersView extends ActivatableViewAndModel<VBox, OpenOffersView
super.updateItem(item, empty);
if (item != null) {
button.setOnAction(event -> onCancelOpenOffer(item.getOpenOffer()));
button.setOnAction(event -> onRemoveOpenOffer(item.getOpenOffer()));
setGraphic(button);
} else {
setGraphic(null);

View File

@ -136,8 +136,7 @@ public class PendingTradesDataModel extends ActivatableDataModel {
if (item == null) {
trade = null;
tradeProperty.set(null);
}
else {
} else {
trade = item.getTrade();
tradeProperty.set(trade);
@ -169,17 +168,17 @@ public class PendingTradesDataModel extends ActivatableDataModel {
}
private void doWithdrawRequest(String toAddress, KeyParameter aesKey) {
tradeManager.onWithdrawRequest(
toAddress,
aesKey,
trade,
() -> {
UserThread.execute(() -> navigation.navigateTo(MainView.class, PortfolioView.class, ClosedTradesView.class));
},
(errorMessage, throwable) -> {
log.error(errorMessage);
new Popup().error("An error occurred:\n" + throwable.getMessage()).show();
});
if (toAddress != null && toAddress.length() > 0) {
tradeManager.onWithdrawRequest(
toAddress,
aesKey,
trade,
() -> UserThread.execute(() -> navigation.navigateTo(MainView.class, PortfolioView.class, ClosedTradesView.class)),
(errorMessage, throwable) -> {
log.error(errorMessage);
new Popup().error("An error occurred:\n" + throwable.getMessage()).show();
});
}
}
public void onOpenDispute() {

View File

@ -60,9 +60,11 @@ public class CompletedView extends TradeStepDetailsView {
@Override
public void doActivate() {
super.doActivate();
withdrawAddressTextField.focusedProperty().addListener(focusedPropertyListener);
withdrawAddressTextField.setValidator(model.getBtcAddressValidator());
withdrawButton.disableProperty().bind(model.getWithdrawalButtonDisable());
// TODO valid. handler need improvement
//withdrawAddressTextField.focusedProperty().addListener(focusedPropertyListener);
//withdrawAddressTextField.setValidator(model.getBtcAddressValidator());
// withdrawButton.disableProperty().bind(model.getWithdrawalButtonDisable());
// 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
@ -78,8 +80,8 @@ public class CompletedView extends TradeStepDetailsView {
@Override
public void doDeactivate() {
super.doDeactivate();
withdrawAddressTextField.focusedProperty().removeListener(focusedPropertyListener);
withdrawButton.disableProperty().unbind();
//withdrawAddressTextField.focusedProperty().removeListener(focusedPropertyListener);
// withdrawButton.disableProperty().unbind();
}

View File

@ -111,19 +111,17 @@ public class ConfirmPaymentReceivedView extends TradeStepDetailsView {
Preferences preferences = model.dataModel.getPreferences();
String key = PopupId.PAYMENT_RECEIVED;
if (preferences.showAgain(key) && !BitsquareApp.DEV_MODE) {
new Popup().information("Please note that as soon you have confirmed that you have received the " +
"payment the locked Bitcoin will be released.\n" +
"There is no way to reverse a Bitcoin payment. Confirm only if you are sure.")
.onClose(() -> preferences.dontShowAgain(key))
new Popup().headLine("Confirmation")
.message("Do you have received the payment from your trading partner?\n\n" +
"Please note that as soon you have confirmed the locked Bitcoin will be released.\n" +
"There is no way to reverse a Bitcoin payment.")
.dontShowAgainId(key, preferences)
.actionButtonText("Yes I have received the payment")
.closeButtonText("No")
.onAction(() -> confirmPaymentReceived())
.show();
} else {
confirmFiatReceivedButton.setDisable(true);
statusProgressIndicator.setVisible(true);
statusProgressIndicator.setProgress(-1);
statusLabel.setText("Sending message to trading partner...");
model.fiatPaymentReceived();
confirmPaymentReceived();
}
} else {
new Popup().warning("You need to wait until your client is authenticated in the network.\n" +
@ -131,6 +129,16 @@ public class ConfirmPaymentReceivedView extends TradeStepDetailsView {
}
}
private void confirmPaymentReceived() {
confirmFiatReceivedButton.setDisable(true);
statusProgressIndicator.setVisible(true);
statusProgressIndicator.setProgress(-1);
statusLabel.setText("Sending message to trading partner...");
model.fiatPaymentReceived();
}
///////////////////////////////////////////////////////////////////////////////////////////
// Setters

View File

@ -143,18 +143,15 @@ public class StartPaymentView extends TradeStepDetailsView {
if (model.isAuthenticated()) {
String key = PopupId.PAYMENT_SENT;
if (preferences.showAgain(key) && !BitsquareApp.DEV_MODE) {
new Popup().information("You are confirming that you have transferred the payment to your trading partner.\n" +
"Please click the \"Payment started\" button only if you have completed the transfer.")
.onClose(() -> preferences.dontShowAgain(key))
new Popup().headLine("Confirmation")
.message("Do you have transferred the payment to your trading partner?")
.dontShowAgainId(key, preferences)
.actionButtonText("Yes I have started the payment")
.closeButtonText("No")
.onAction(() -> confirmPaymentStarted())
.show();
} else {
paymentStartedButton.setDisable(true);
statusProgressIndicator.setVisible(true);
statusProgressIndicator.setProgress(-1);
statusLabel.setText("Sending message to trading partner...");
model.fiatPaymentStarted();
confirmPaymentStarted();
}
} else {
new Popup().warning("You need to wait until your client is authenticated in the network.\n" +
@ -162,6 +159,16 @@ public class StartPaymentView extends TradeStepDetailsView {
}
}
private void confirmPaymentStarted() {
paymentStartedButton.setDisable(true);
statusProgressIndicator.setVisible(true);
statusProgressIndicator.setProgress(-1);
statusLabel.setText("Sending message to trading partner...");
model.fiatPaymentStarted();
}
///////////////////////////////////////////////////////////////////////////////////////////
// Build view

View File

@ -52,7 +52,7 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Preferenc
btcDenominationComboBox = addLabelComboBox(root, gridRow, "Bitcoin denomination:", Layout.FIRST_ROW_DISTANCE).second;
blockExplorerComboBox = addLabelComboBox(root, ++gridRow, "Bitcoin block explorer:").second;
useAnimationsCheckBox = addLabelCheckBox(root, ++gridRow, "Use animations:", "").second;
useEffectsCheckBox = addLabelCheckBox(root, ++gridRow, "Use efects:", "").second;
useEffectsCheckBox = addLabelCheckBox(root, ++gridRow, "Use effects:", "").second;
showPlaceOfferConfirmationCheckBox = addLabelCheckBox(root, ++gridRow, "Show confirmation at place offer:", "").second;
showTakeOfferConfirmationCheckBox = addLabelCheckBox(root, ++gridRow, "Show confirmation at take offer:", "").second;
autoSelectArbitratorsCheckBox = addLabelCheckBox(root, ++gridRow, "Auto select arbitrators by language:", "").second;

View File

@ -153,10 +153,8 @@ public class NetworkSettingsView extends ActivatableViewAndModel<GridPane, Activ
private void onSelectNetwork() {
if (netWorkComboBox.getSelectionModel().getSelectedItem() != preferences.getBitcoinNetwork()) {
if (netWorkComboBox.getSelectionModel().getSelectedItem() == BitcoinNetwork.MAINNET) {
new Popup().warning("The application is under heavy development. " +
"Using the mainnet network with Bitcoin is not recommended at that stage.\n\n" +
"Are you sure you want to switch to mainnet?")
.onAction(() -> selectNetwork())
new Popup().warning("The application needs more tested before it can be used in mainnet.\n" +
"Please follow our mailing list to get informed when Bitsquare will be ready for mainnet.")
.onClose(() -> UserThread.execute(() -> netWorkComboBox.getSelectionModel().select(preferences.getBitcoinNetwork())))
.show();
} else {
@ -166,10 +164,14 @@ public class NetworkSettingsView extends ActivatableViewAndModel<GridPane, Activ
}
private void selectNetwork() {
preferences.setBitcoinNetwork(netWorkComboBox.getSelectionModel().getSelectedItem());
new Popup().warning("You need to restart the application to apply the change of the Bitcoin network..\n\n" +
"Do you want to restart now?")
.onAction(() -> BitsquareApp.restartDownHandler.run())
//TODO restart
new Popup().warning("You need to shut down and restart the application to apply the change of the Bitcoin network.\n\n" +
"Do you want to shut down now?")
.onAction(() -> {
preferences.setBitcoinNetwork(netWorkComboBox.getSelectionModel().getSelectedItem());
UserThread.runAfter(() -> BitsquareApp.shutDownHandler.run(), 1);
})
.onClose(() -> netWorkComboBox.getSelectionModel().select(preferences.getBitcoinNetwork()))
.show();
}
}

View File

@ -109,8 +109,7 @@ public class ContractPopup extends Popup {
Layout.FIRST_ROW_DISTANCE).second.setMouseTransparent(false);
addLabelTextField(gridPane, ++rowIndex, "Offer date:", formatter.formatDateTime(offer.getDate()));
addLabelTextField(gridPane, ++rowIndex, "Trade date:", formatter.formatDateTime(dispute.getTradeDate()));
String direction = offer.getDirection() == Offer.Direction.BUY ? "Offerer as buyer / Taker as seller" : "Offerer as seller / Taker as buyer";
addLabelTextField(gridPane, ++rowIndex, "Trade type:", direction);
addLabelTextField(gridPane, ++rowIndex, "Trade type:", formatter.getDirectionDescription(offer.getDirection()));
addLabelTextField(gridPane, ++rowIndex, "Price:", formatter.formatFiat(offer.getPrice()) + " " + offer.getCurrencyCode());
addLabelTextField(gridPane, ++rowIndex, "Trade amount:", formatter.formatCoinWithCode(contract.getTradeAmount()));
addLabelTextFieldWithCopyIcon(gridPane, ++rowIndex, "Buyer bitcoin address:",
@ -144,8 +143,8 @@ public class ContractPopup extends Popup {
}
//addLabelTextField(gridPane, ++rowIndex, "Buyer Bitsquare account ID:", contract.getBuyerAccountId()).second.setMouseTransparent(false);
//addLabelTextField(gridPane, ++rowIndex, "Seller Bitsquare account ID:", contract.getSellerAccountId()).second.setMouseTransparent(false);
addLabelTxIdTextField(gridPane, ++rowIndex, "Create offer fee transaction ID:", offer.getOfferFeePaymentTxID());
addLabelTxIdTextField(gridPane, ++rowIndex, "Take offer fee transaction ID:", contract.takeOfferFeeTxID);
addLabelTxIdTextField(gridPane, ++rowIndex, "Offer fee transaction ID:", offer.getOfferFeePaymentTxID());
addLabelTxIdTextField(gridPane, ++rowIndex, "Trading fee transaction ID:", contract.takeOfferFeeTxID);
if (dispute.getDepositTxSerialized() != null)
addLabelTxIdTextField(gridPane, ++rowIndex, "Deposit transaction ID:", dispute.getDepositTxId());
if (dispute.getPayoutTxSerialized() != null)

View File

@ -54,8 +54,8 @@ public class FirstTimeWebViewPopup extends WebViewPopup {
return this;
}
public FirstTimeWebViewPopup id(String id) {
this.id = id;
public FirstTimeWebViewPopup id(String dontShowAgainId) {
this.id = dontShowAgainId;
return this;
}

View File

@ -35,6 +35,8 @@ import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.TextField;
import javafx.scene.control.Tooltip;
import org.bitcoinj.core.Coin;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -52,6 +54,7 @@ public class OfferDetailsPopup extends Popup {
private User user;
private final Navigation navigation;
private Offer offer;
private Coin tradeAmount;
private Optional<Consumer<Offer>> placeOfferHandlerOptional = Optional.empty();
private Optional<Runnable> takeOfferHandlerOptional = Optional.empty();
@ -68,6 +71,18 @@ public class OfferDetailsPopup extends Popup {
this.navigation = navigation;
}
public OfferDetailsPopup show(Offer offer, Coin tradeAmount) {
this.offer = offer;
this.tradeAmount = tradeAmount;
rowIndex = -1;
width = 850;
createGridPane();
addContent();
createPopup();
return this;
}
public OfferDetailsPopup show(Offer offer) {
this.offer = offer;
@ -110,25 +125,36 @@ public class OfferDetailsPopup extends Popup {
}
private void addContent() {
int rows = 11;
int rows = 5;
if (!takeOfferHandlerOptional.isPresent())
rows++;
addTitledGroupBg(gridPane, ++rowIndex, rows, "Offer");
addLabelTextField(gridPane, rowIndex, "Offer type:", formatter.getDirectionDescription(offer.getDirection()), Layout.FIRST_ROW_DISTANCE);
addLabelTextField(gridPane, ++rowIndex, "Currency:", offer.getCurrencyCode());
addLabelTextField(gridPane, ++rowIndex, "Price:", formatter.formatFiat(offer.getPrice()) + " " + offer.getCurrencyCode() + "/" + "BTC");
if (takeOfferHandlerOptional.isPresent()) {
addLabelTextField(gridPane, ++rowIndex, "Trade amount:", formatter.formatCoinWithCode(tradeAmount));
} else {
addLabelTextField(gridPane, ++rowIndex, "Amount:", formatter.formatCoinWithCode(offer.getAmount()));
addLabelTextField(gridPane, ++rowIndex, "Min. amount:", formatter.formatCoinWithCode(offer.getMinAmount()));
}
addLabelTextField(gridPane, ++rowIndex, "Payment method:", BSResources.get(offer.getPaymentMethod().getId()));
rows = 3;
if (offer.getPaymentMethodCountryCode() != null)
rows++;
if (offer.getOfferFeePaymentTxID() != null)
rows++;
if (offer.getAcceptedCountryCodes() != null)
rows++;
if (placeOfferHandlerOptional.isPresent())
rows -= 2;
/* if (placeOfferHandlerOptional.isPresent())
rows -= 2;*/
addTitledGroupBg(gridPane, ++rowIndex, rows, "Offer details");
addLabelTextField(gridPane, rowIndex, "Offer ID:", offer.getId(), Layout.FIRST_ROW_DISTANCE);
addTitledGroupBg(gridPane, ++rowIndex, rows, "Details", Layout.GROUP_DISTANCE);
addLabelTextField(gridPane, rowIndex, "Offer ID:", offer.getId(), Layout.FIRST_ROW_AND_GROUP_DISTANCE);
addLabelTextField(gridPane, ++rowIndex, "Creation date:", formatter.formatDateTime(offer.getDate()));
addLabelTextField(gridPane, ++rowIndex, "Offer direction:", Offer.Direction.BUY.name());
addLabelTextField(gridPane, ++rowIndex, "Currency:", offer.getCurrencyCode());
addLabelTextField(gridPane, ++rowIndex, "Price:", formatter.formatFiat(offer.getPrice()) + " " + offer.getCurrencyCode() + "/" + "BTC");
addLabelTextField(gridPane, ++rowIndex, "Amount:", formatter.formatCoinWithCode(offer.getAmount()));
addLabelTextField(gridPane, ++rowIndex, "Min. amount:", formatter.formatCoinWithCode(offer.getMinAmount()));
addLabelTextField(gridPane, ++rowIndex, "Payment method:", BSResources.get(offer.getPaymentMethod().getId()));
if (offer.getPaymentMethodCountryCode() != null)
addLabelTextField(gridPane, ++rowIndex, "Offerers country of bank:", offer.getPaymentMethodCountryCode());
if (offer.getAcceptedCountryCodes() != null) {
@ -141,61 +167,27 @@ public class OfferDetailsPopup extends Popup {
tooltip = new Tooltip(CountryUtil.getNamesByCodesString(offer.getAcceptedCountryCodes()));
}
TextField acceptedCountries = addLabelTextField(gridPane, ++rowIndex, "Accepted taker countries:", countries).second;
if (tooltip != null) acceptedCountries.setTooltip(new Tooltip());
if (tooltip != null) {
acceptedCountries.setMouseTransparent(false);
acceptedCountries.setTooltip(tooltip);
}
}
addLabelTextField(gridPane, ++rowIndex, "Accepted arbitrators:", formatter.arbitratorAddressesToString(offer.getArbitratorAddresses()));
if (offer.getOfferFeePaymentTxID() != null)
addLabelTxIdTextField(gridPane, ++rowIndex, "Create offer fee transaction ID:", offer.getOfferFeePaymentTxID());
addLabelTxIdTextField(gridPane, ++rowIndex, "Offer fee transaction ID:", offer.getOfferFeePaymentTxID());
if (placeOfferHandlerOptional.isPresent()) {
Tuple2<Button, Button> tuple = add2ButtonsAfterGroup(gridPane, ++rowIndex, "Confirm place offer", "Cancel");
Button placeButton = tuple.first;
placeButton.setOnAction(e -> {
if (user.getAcceptedArbitrators().size() > 0) {
placeOfferHandlerOptional.get().accept(offer);
} else {
new Popup().warning("You have no arbitrator selected.\n" +
"Please select at least one arbitrator.").show();
addTitledGroupBg(gridPane, ++rowIndex, 1, "Commitment", Layout.GROUP_DISTANCE);
addLabelTextField(gridPane, rowIndex, "Please note:", Offer.TAC_OFFERER, Layout.FIRST_ROW_AND_GROUP_DISTANCE);
navigation.navigateTo(MainView.class, AccountView.class, AccountSettingsView.class, ArbitratorSelectionView.class);
}
hide();
});
Button cancelButton = tuple.second;
cancelButton.setOnAction(e -> {
closeHandlerOptional.ifPresent(closeHandler -> closeHandler.run());
hide();
});
CheckBox checkBox = addCheckBox(gridPane, ++rowIndex, "Don't show again", 5);
checkBox.setSelected(!preferences.getShowPlaceOfferConfirmation());
checkBox.setOnAction(e -> preferences.setShowPlaceOfferConfirmation(!checkBox.isSelected()));
Button cancelButton = addConfirmButton(true);
addCancelButton(cancelButton, true);
} else if (takeOfferHandlerOptional.isPresent()) {
Tuple2<Button, Button> tuple = add2ButtonsAfterGroup(gridPane, ++rowIndex, "Confirm take offer", "Cancel");
Button placeButton = tuple.first;
placeButton.setOnAction(e -> {
if (user.getAcceptedArbitrators().size() > 0) {
takeOfferHandlerOptional.get().run();
} else {
new Popup().warning("You have no arbitrator selected.\n" +
"Please select at least one arbitrator.").show();
addTitledGroupBg(gridPane, ++rowIndex, 1, "Contract", Layout.GROUP_DISTANCE);
addLabelTextField(gridPane, rowIndex, "Terms and conditions:", Offer.TAC_TAKER, Layout.FIRST_ROW_AND_GROUP_DISTANCE);
navigation.navigateTo(MainView.class, AccountView.class, AccountSettingsView.class, ArbitratorSelectionView.class);
}
hide();
});
Button cancelButton = tuple.second;
cancelButton.setOnAction(e -> {
closeHandlerOptional.ifPresent(closeHandler -> closeHandler.run());
hide();
});
CheckBox checkBox = addCheckBox(gridPane, ++rowIndex, "Don't show again", 5);
checkBox.setPadding(new Insets(20, 0, 25, 0));
checkBox.setSelected(!preferences.getShowTakeOfferConfirmation());
checkBox.setOnAction(e -> preferences.setShowTakeOfferConfirmation(!checkBox.isSelected()));
Button cancelButton = addConfirmButton(false);
addCancelButton(cancelButton, false);
} else {
Button cancelButton = addButtonAfterGroup(gridPane, ++rowIndex, "Close");
cancelButton.setOnAction(e -> {
@ -204,4 +196,44 @@ public class OfferDetailsPopup extends Popup {
});
}
}
@NotNull
private Button addConfirmButton(boolean isPlaceOffer) {
Tuple2<Button, Button> tuple = add2ButtonsAfterGroup(gridPane,
++rowIndex,
isPlaceOffer ? "Confirm place offer" : "Confirm take offer",
"Cancel");
Button placeButton = tuple.first;
placeButton.setOnAction(e -> {
if (user.getAcceptedArbitrators().size() > 0) {
if (isPlaceOffer)
placeOfferHandlerOptional.get().accept(offer);
else
takeOfferHandlerOptional.get().run();
} else {
new Popup().warning("You have no arbitrator selected.\n" +
"Please select at least one arbitrator.").show();
navigation.navigateTo(MainView.class, AccountView.class, AccountSettingsView.class, ArbitratorSelectionView.class);
}
hide();
});
return tuple.second;
}
private void addCancelButton(Button cancelButton, boolean isPlaceOffer) {
cancelButton.setOnAction(e -> {
closeHandlerOptional.ifPresent(closeHandler -> closeHandler.run());
hide();
});
CheckBox checkBox = addCheckBox(gridPane, ++rowIndex, "Don't show again", 5);
if (isPlaceOffer) {
checkBox.setSelected(!preferences.getShowPlaceOfferConfirmation());
checkBox.setOnAction(e -> preferences.setShowPlaceOfferConfirmation(!checkBox.isSelected()));
} else {
checkBox.setSelected(!preferences.getShowTakeOfferConfirmation());
checkBox.setOnAction(e -> preferences.setShowTakeOfferConfirmation(!checkBox.isSelected()));
}
}
}

View File

@ -21,15 +21,13 @@ import io.bitsquare.common.util.Utilities;
import io.bitsquare.gui.main.MainView;
import io.bitsquare.gui.util.Transitions;
import io.bitsquare.locale.BSResources;
import io.bitsquare.user.Preferences;
import javafx.geometry.HPos;
import javafx.geometry.Insets;
import javafx.geometry.Orientation;
import javafx.geometry.Point2D;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.control.Separator;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.stage.Modality;
@ -43,6 +41,8 @@ import org.slf4j.LoggerFactory;
import java.time.Duration;
import java.util.Optional;
import static io.bitsquare.gui.util.FormBuilder.addCheckBox;
public class Popup {
protected final Logger log = LoggerFactory.getLogger(this.getClass());
@ -66,6 +66,8 @@ public class Popup {
private boolean showProgressIndicator;
private Button actionButton;
protected Label headLineLabel;
private String dontShowAgainId;
private Preferences preferences;
///////////////////////////////////////////////////////////////////////////////////////////
@ -87,6 +89,7 @@ public class Popup {
addReportErrorButtons();
addCloseButton();
addDontShowAgainCheckBox();
createPopup();
return this;
}
@ -164,6 +167,12 @@ public class Popup {
return this;
}
public Popup dontShowAgainId(String dontShowAgainId, Preferences preferences) {
this.dontShowAgainId = dontShowAgainId;
this.preferences = preferences;
return this;
}
///////////////////////////////////////////////////////////////////////////////////////////
// Protected
///////////////////////////////////////////////////////////////////////////////////////////
@ -297,6 +306,17 @@ public class Popup {
gridPane.getChildren().add(progressIndicator);
}
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())
preferences.dontShowAgain(dontShowAgainId);
});
}
}
protected void addCloseButton() {
closeButton = new Button(closeButtonText == null ? "Close" : closeButtonText);
closeButton.setOnAction(event -> {
@ -333,12 +353,11 @@ public class Popup {
GridPane.setColumnIndex(closeButton, 1);
gridPane.getChildren().add(closeButton);
}
}
protected void setTruncatedMessage() {
if (message != null && message.length() > 500)
truncatedMessage = message.substring(0, 500) + "...";
if (message != null && message.length() > 600)
truncatedMessage = message.substring(0, 600) + "...";
else
truncatedMessage = message;
}

View File

@ -90,7 +90,15 @@ public class TradeDetailsPopup extends Popup {
Offer offer = trade.getOffer();
Contract contract = trade.getContract();
int rows = 7;
int rows = 5;
addTitledGroupBg(gridPane, ++rowIndex, rows, "Trade");
addLabelTextField(gridPane, rowIndex, "Trade type:", formatter.getDirectionDescription(offer.getDirection()), Layout.FIRST_ROW_DISTANCE);
addLabelTextField(gridPane, ++rowIndex, "Currency:", offer.getCurrencyCode());
addLabelTextField(gridPane, ++rowIndex, "Price:", formatter.formatFiat(offer.getPrice()) + " " + offer.getCurrencyCode());
addLabelTextField(gridPane, ++rowIndex, "Trade amount:", formatter.formatCoinWithCode(trade.getTradeAmount()));
addLabelTextField(gridPane, ++rowIndex, "Payment method:", BSResources.get(offer.getPaymentMethod().getId()));
rows = 4;
PaymentAccountContractData buyerPaymentAccountContractData = null;
PaymentAccountContractData sellerPaymentAccountContractData = null;
@ -98,6 +106,8 @@ public class TradeDetailsPopup extends Popup {
rows++;
if (contract != null) {
rows++;
buyerPaymentAccountContractData = contract.getBuyerPaymentAccountContractData();
sellerPaymentAccountContractData = contract.getSellerPaymentAccountContractData();
if (buyerPaymentAccountContractData != null)
@ -120,13 +130,9 @@ public class TradeDetailsPopup extends Popup {
if (trade.errorMessageProperty().get() != null)
rows += 2;
addTitledGroupBg(gridPane, ++rowIndex, rows, "Trade details");
addLabelTextField(gridPane, rowIndex, "Trade ID:", trade.getId(), Layout.FIRST_ROW_DISTANCE);
addTitledGroupBg(gridPane, ++rowIndex, rows, "Details", Layout.GROUP_DISTANCE);
addLabelTextField(gridPane, rowIndex, "Trade ID:", trade.getId(), Layout.FIRST_ROW_AND_GROUP_DISTANCE);
addLabelTextField(gridPane, ++rowIndex, "Trade date:", formatter.formatDateTime(trade.getDate()));
String direction = offer.getDirection() == Offer.Direction.BUY ? "Offerer as buyer / Taker as seller" : "Offerer as seller / Taker as buyer";
addLabelTextField(gridPane, ++rowIndex, "Offer direction:", direction);
addLabelTextField(gridPane, ++rowIndex, "Price:", formatter.formatFiat(offer.getPrice()) + " " + offer.getCurrencyCode());
addLabelTextField(gridPane, ++rowIndex, "Trade amount:", formatter.formatCoinWithCode(trade.getTradeAmount()));
addLabelTextField(gridPane, ++rowIndex, "Selected arbitrator:", trade.getArbitratorAddress().getFullAddress());
if (contract != null) {
@ -144,15 +150,22 @@ public class TradeDetailsPopup extends Popup {
addLabelTextField(gridPane, ++rowIndex, "Payment method:", BSResources.get(contract.getPaymentMethodName()));
}
addLabelTxIdTextField(gridPane, ++rowIndex, "Create offer fee transaction ID:", offer.getOfferFeePaymentTxID());
addLabelTxIdTextField(gridPane, ++rowIndex, "Offer fee transaction ID:", offer.getOfferFeePaymentTxID());
if (contract != null && contract.takeOfferFeeTxID != null)
addLabelTxIdTextField(gridPane, ++rowIndex, "Take offer fee transaction ID:", contract.takeOfferFeeTxID);
addLabelTxIdTextField(gridPane, ++rowIndex, "Trading fee transaction ID:", contract.takeOfferFeeTxID);
if (trade.getDepositTx() != null)
addLabelTxIdTextField(gridPane, ++rowIndex, "Deposit transaction ID:", trade.getDepositTx().getHashAsString());
if (trade.getPayoutTx() != null)
addLabelTxIdTextField(gridPane, ++rowIndex, "Payout transaction ID:", trade.getPayoutTx().getHashAsString());
if (contract != null) {
TextArea textArea = addLabelTextArea(gridPane, ++rowIndex, "Contract in JSON format:", trade.getContractAsJson()).second;
textArea.setText(trade.getContractAsJson());
textArea.setPrefHeight(50);
textArea.setEditable(false);
}
if (trade.errorMessageProperty().get() != null) {
TextArea textArea = addLabelTextArea(gridPane, ++rowIndex, "Error message:", "").second;
textArea.setText(trade.errorMessageProperty().get());
@ -169,26 +182,6 @@ public class TradeDetailsPopup extends Popup {
TextField state = addLabelTextField(gridPane, ++rowIndex, "Trade state:").second;
state.setText(trade.getState().getPhase().name());
//TODO better msg display
/* switch (trade.getTradeState().getPhase()) {
case PREPARATION:
state.setText("Take offer fee is already paid.");
break;
case TAKER_FEE_PAID:
state.setText("Take offer fee is already paid.");
break;
case DEPOSIT_PAID:
case FIAT_SENT:
case FIAT_RECEIVED:
state.setText("Deposit is already paid.");
break;
case PAYOUT_PAID:
break;
case WITHDRAWN:
break;
case DISPUTE:
break;
}*/
}
Button cancelButton = addButtonAfterGroup(gridPane, ++rowIndex, "Close");

View File

@ -125,8 +125,7 @@ public class BSFormatter {
log.warn("Exception at formatBtc: " + t.toString());
return "";
}
}
else {
} else {
return "";
}
}
@ -141,8 +140,7 @@ public class BSFormatter {
log.warn("Exception at formatBtcWithCode: " + t.toString());
return "";
}
}
else {
} else {
return "";
}
}
@ -155,8 +153,7 @@ public class BSFormatter {
log.warn("Exception at parseToBtc: " + t.toString());
return Coin.ZERO;
}
}
else {
} else {
return Coin.ZERO;
}
}
@ -208,8 +205,7 @@ public class BSFormatter {
log.warn("Exception at formatFiat: " + t.toString());
return "";
}
}
else {
} else {
return "";
}
}
@ -223,8 +219,7 @@ public class BSFormatter {
log.warn("Exception at formatFiatWithCode: " + t.toString());
return "";
}
}
else {
} else {
return "";
}
}
@ -238,8 +233,7 @@ public class BSFormatter {
return Fiat.valueOf(currencyCode, 0);
}
}
else {
} else {
return Fiat.valueOf(currencyCode, 0);
}
}
@ -374,4 +368,8 @@ public class BSFormatter {
return "";
}
}
public String getDirectionDescription(Offer.Direction direction) {
return direction == Offer.Direction.BUY ? "Offerer as Bitcoin buyer / Taker as Bitcoin seller" : "Offerer as Bitcoin seller / Taker as Bitcoin buyer";
}
}

View File

@ -17,7 +17,7 @@ arising from, out of or in connection with the software or the use or other deal
2. The user is responsible to use the software in compliance with local laws.
<br/><br/>
3. The user confirms that he has read and agreed to the rules defined in our
<a href="https://github.com/bitsquare/bitsquare/wiki/Dispute-process" target="_blank">wiki</a> regrading the dispute
<a href="https://github.com/bitsquare/bitsquare/wiki/Dispute-process" target="_blank">Wiki</a> regrading the dispute
process.<br/>
</body>
</html>

View File

@ -23,13 +23,12 @@
</head>
<body>
<h1>Information</h1>
Bitsquare does not use a global wallet.<br/>
For every trade a dedicated wallet will be created. Funding of the wallet will be done just in time when it is needed.
For instance when you create an offer or
when you take an offer. Withdrawing from your funds can be done after a trade has been completed.<br/>
That separation of addresses helps to protect users privacy and not leaking information of previous trades to new
Bitsquare does not use a single application wallet, but dedicated wallets for every trade.<br/>
Funding of the wallet will be done when needed, for instance when you create or take an offer.
Withdrawing funds can be done after a trade is completed.<br/>
Dedicated wallets help protect user privacy and prevent leaking information of previous trades to other
traders.<br/>
Please read more background information to that topic at the Bitsquare <a href="https://bitsquare.io/faq/#tradeWallet"
For more background information please see the Bitsquare <a href="https://bitsquare.io/faq/#tradeWallet"
target="_blank">FAQ</a>.
</body>
</html>

View File

@ -646,16 +646,32 @@ public class PeerGroup implements MessageListener, ConnectionListener {
// Reported peers
///////////////////////////////////////////////////////////////////////////////////////////
void addToReportedPeers(HashSet<ReportedPeer> reportedPeers, Connection connection) {
void addToReportedPeers(HashSet<ReportedPeer> newReportedPeers, Connection connection) {
Log.traceCall();
// we disconnect misbehaving nodes trying to send too many peers
// reported peers include the peers connected peers which is normally max. 8 but we give some headroom
// for safety
if (reportedPeers.size() > 1100) {
if (newReportedPeers.size() > 1100) {
connection.shutDown();
} else {
reportedPeers.remove(new ReportedPeer(getMyAddress(), new Date()));
this.reportedPeers.addAll(reportedPeers);
newReportedPeers.remove(new ReportedPeer(getMyAddress(), new Date()));
//TODO if we have already peer, we mix date from old and new item
//
/* Map<Address, ReportedPeer> reportedPeersMap = new HashMap<>();
reportedPeers.stream().forEach(e -> reportedPeersMap.put(e.address, e));
HashSet<ReportedPeer> newAdjustedReportedPeers = new HashSet<>();
newReportedPeers.stream()
.forEach(e -> {
if()
long adjustedTime = (e.lastActivityDate.getTime() +
reportedPeersMap.get(e.address).lastActivityDate.getTime()) / 2;
newAdjustedReportedPeers.add(new ReportedPeer(e.address,
new Date(adjustedTime)));
});*/
this.reportedPeers.addAll(newReportedPeers);
purgeReportedPeersIfExceeds();
}
@ -670,6 +686,8 @@ public class PeerGroup implements MessageListener, ConnectionListener {
"We remove random peers from the reported peers list.", MAX_REPORTED_PEERS, size);
int diff = size - MAX_REPORTED_PEERS;
List<ReportedPeer> list = new LinkedList<>(getReportedNotConnectedPeerAddresses());
//TODO sort and remove oldest
for (int i = 0; i < diff; i++) {
ReportedPeer toRemove = getAndRemoveRandomReportedPeer(list);
reportedPeers.remove(toRemove);