diff --git a/core/src/main/java/io/bitsquare/btc/pricefeed/providers/PoloniexPriceProvider.java b/core/src/main/java/io/bitsquare/btc/pricefeed/providers/PoloniexPriceProvider.java index bd8950a878..39a5efaf73 100644 --- a/core/src/main/java/io/bitsquare/btc/pricefeed/providers/PoloniexPriceProvider.java +++ b/core/src/main/java/io/bitsquare/btc/pricefeed/providers/PoloniexPriceProvider.java @@ -33,7 +33,7 @@ public class PoloniexPriceProvider implements PriceProvider { String response = httpClient.requestWithGET("?command=returnTicker"); LinkedTreeMap treeMap = new Gson().fromJson(response, LinkedTreeMap.class); Map temp = new HashMap<>(); - Set supported = CurrencyUtil.getSortedCryptoCurrencies().stream() + Set supported = CurrencyUtil.getAllSortedCryptoCurrencies().stream() .map(TradeCurrency::getCode) .collect(Collectors.toSet()); treeMap.entrySet().stream().forEach(e -> { diff --git a/core/src/main/java/io/bitsquare/locale/CountryUtil.java b/core/src/main/java/io/bitsquare/locale/CountryUtil.java index c4779039a4..f02b343ef7 100644 --- a/core/src/main/java/io/bitsquare/locale/CountryUtil.java +++ b/core/src/main/java/io/bitsquare/locale/CountryUtil.java @@ -126,7 +126,7 @@ public class CountryUtil { selectedRegion != null && country != null && selectedRegion.equals(country.region))); } - private static List getAllCountries() { + public static List getAllCountries() { final List allCountries = new ArrayList<>(); for (final Locale locale : getAllCountryLocales()) { String regionCode = getRegionCode(locale.getCountry()); diff --git a/core/src/main/java/io/bitsquare/locale/CurrencyUtil.java b/core/src/main/java/io/bitsquare/locale/CurrencyUtil.java index 3ed1f57f18..d43fed3227 100644 --- a/core/src/main/java/io/bitsquare/locale/CurrencyUtil.java +++ b/core/src/main/java/io/bitsquare/locale/CurrencyUtil.java @@ -27,14 +27,96 @@ import java.util.stream.Collectors; public class CurrencyUtil { private static final Logger log = LoggerFactory.getLogger(CurrencyUtil.class); - private static final List allSortedCurrencies = createAllSortedCurrenciesList(); + private static final List allSortedFiatCurrencies = createAllSortedFiatCurrenciesList(); - public static List getAllSortedCurrencies() { - return allSortedCurrencies; + private static List createAllSortedFiatCurrenciesList() { + Set set = CountryUtil.getAllCountries().stream() + .map(country -> getCurrencyByCountryCode(country.code)) + .collect(Collectors.toSet()); + List list = new ArrayList<>(set); + list.sort(TradeCurrency::compareTo); + return list; } - // We add all currencies supported by payment methods - private static List createAllSortedCurrenciesList() { + public static List getAllSortedFiatCurrencies() { + return allSortedFiatCurrencies; + } + + public static List getAllMainFiatCurrencies() { + List list = new ArrayList<>(); + // Top traded currencies + list.add(new FiatCurrency("USD")); + list.add(new FiatCurrency("EUR")); + list.add(new FiatCurrency("GBP")); + list.add(new FiatCurrency("CAD")); + list.add(new FiatCurrency("AUD")); + list.add(new FiatCurrency("RUB")); + list.add(new FiatCurrency("INR")); + + TradeCurrency defaultTradeCurrency = getDefaultTradeCurrency(); + FiatCurrency defaultFiatCurrency = defaultTradeCurrency instanceof FiatCurrency ? (FiatCurrency) defaultTradeCurrency : null; + if (defaultFiatCurrency != null && list.contains(defaultFiatCurrency)) { + list.remove(defaultTradeCurrency); + list.add(0, defaultFiatCurrency); + } + return list; + } + + + private static final List allSortedCryptoCurrencies = createAllSortedCryptoCurrenciesList(); + + public static List getAllSortedCryptoCurrencies() { + return allSortedCryptoCurrencies; + } + + public static List getMainCryptoCurrencies() { + final List result = new ArrayList<>(); + result.add(new CryptoCurrency("ETH", "Ethereum")); + result.add(new CryptoCurrency("LTC", "Litecoin")); + result.add(new CryptoCurrency("NMC", "Namecoin")); + result.add(new CryptoCurrency("DASH", "Dash")); + result.add(new CryptoCurrency("NBT", "NuBits")); + result.add(new CryptoCurrency("DOGE", "Dogecoin")); + result.add(new CryptoCurrency("NXT", "Nxt")); + result.add(new CryptoCurrency("BTS", "BitShares")); + return result; + } + + public static List createAllSortedCryptoCurrenciesList() { + final List result = new ArrayList<>(); + result.add(new CryptoCurrency("ETH", "Ethereum")); + result.add(new CryptoCurrency("LTC", "Litecoin")); + result.add(new CryptoCurrency("NMC", "Namecoin")); + result.add(new CryptoCurrency("DASH", "Dash")); + result.add(new CryptoCurrency("NBT", "NuBits")); + result.add(new CryptoCurrency("NSR", "NuShares")); + result.add(new CryptoCurrency("PPC", "Peercoin")); + result.add(new CryptoCurrency("XPM", "Primecoin")); + result.add(new CryptoCurrency("SC", "Siacoin")); + result.add(new CryptoCurrency("SJCX", "StorjcoinX")); + result.add(new CryptoCurrency("GEMZ", "Gemz")); + result.add(new CryptoCurrency("DOGE", "Dogecoin")); + result.add(new CryptoCurrency("BLK", "Blackcoin")); + result.add(new CryptoCurrency("FCT", "Factom")); + result.add(new CryptoCurrency("NXT", "Nxt")); + result.add(new CryptoCurrency("BTS", "BitShares")); + result.add(new CryptoCurrency("XCP", "Counterparty")); + result.add(new CryptoCurrency("XRP", "Ripple")); + + // Unfortunately we cannot support CryptoNote coins yet as there is no way to proof the transaction. Payment ID helps only locate the tx but the + // arbitrator cannot see if the receiving key matches the receivers address. They might add support for exposing the tx key, but that is not + // implemented yet. To use the view key (also not available in GUI wallets) would reveal the complete wallet history for incoming payments, which is + // not acceptable from privacy point of view. + // result.add(new CryptoCurrency("XMR", "Monero")); + // result.add(new CryptoCurrency("BCN", "Bytecoin")); + return result; + } + + + + /* // We add all currencies supported by payment methods + // TODO not used anymore + private static List createAllSortedCurrenciesListDerivedFromPaymentMethods() { Set set = new HashSet<>(); // Sepa: EUR at first place @@ -52,11 +134,11 @@ public class CurrencyUtil { // Swish: it is already added by SEPA // for printing out all codes - /* String res; + *//* String res; result.stream().forEach(e -> { res += "list.add(new FiatCurrency(\""+e.code+"\"));\n"; }); - log.debug(res);*/ + log.debug(res);*//* List list = getAllManuallySortedFiatCurrencies(); @@ -84,9 +166,10 @@ public class CurrencyUtil { getSortedCryptoCurrencies().stream().forEach(list::add); return list; - } + }*/ - private static List getAllManuallySortedFiatCurrencies() { + + /* private static List getAllManuallySortedFiatCurrencies() { List list = new ArrayList<>(); list.add(new FiatCurrency("EUR")); list.add(new FiatCurrency("USD")); @@ -116,7 +199,7 @@ public class CurrencyUtil { list.add(new FiatCurrency("RON")); return list; - } + }*/ /** * @return Sorted list of SEPA currencies with EUR as first item @@ -160,41 +243,11 @@ public class CurrencyUtil { @SuppressWarnings("WeakerAccess") public static boolean isCryptoCurrency(String currencyCode) { - return getSortedCryptoCurrencies().stream().filter(e -> e.getCode().equals(currencyCode)).findAny().isPresent(); + return getAllSortedCryptoCurrencies().stream().filter(e -> e.getCode().equals(currencyCode)).findAny().isPresent(); } - public static Optional getCryptoCurrency(String currencyCode) { - return getSortedCryptoCurrencies().stream().filter(e -> e.getCode().equals(currencyCode)).findAny(); - } - - public static List getSortedCryptoCurrencies() { - final List result = new ArrayList<>(); - result.add(new CryptoCurrency("ETH", "Ethereum")); - result.add(new CryptoCurrency("LTC", "Litecoin")); - result.add(new CryptoCurrency("NMC", "Namecoin")); - result.add(new CryptoCurrency("DASH", "Dash")); - result.add(new CryptoCurrency("NBT", "NuBits")); - result.add(new CryptoCurrency("NSR", "NuShares")); - result.add(new CryptoCurrency("PPC", "Peercoin")); - result.add(new CryptoCurrency("XPM", "Primecoin")); - result.add(new CryptoCurrency("SC", "Siacoin")); - result.add(new CryptoCurrency("SJCX", "StorjcoinX")); - result.add(new CryptoCurrency("GEMZ", "Gemz")); - result.add(new CryptoCurrency("DOGE", "Dogecoin")); - result.add(new CryptoCurrency("BLK", "Blackcoin")); - result.add(new CryptoCurrency("FCT", "Factom")); - result.add(new CryptoCurrency("NXT", "Nxt")); - result.add(new CryptoCurrency("BTS", "BitShares")); - result.add(new CryptoCurrency("XCP", "Counterparty")); - result.add(new CryptoCurrency("XRP", "Ripple")); - - // Unfortunately we cannot support CryptoNote coins yet as there is no way to proof the transaction. Payment ID helps only locate the tx but the - // arbitrator cannot see if the receiving key matches the receivers address. They might add support for exposing the tx key, but that is not - // implemented yet. To use the view key (also not available in GUI wallets) would reveal the complete wallet history for incoming payments, which is - // not acceptable from privacy point of view. - // result.add(new CryptoCurrency("XMR", "Monero")); - // result.add(new CryptoCurrency("BCN", "Bytecoin")); - return result; + public static Optional getCryptoCurrency(String currencyCode) { + return getAllSortedCryptoCurrencies().stream().filter(e -> e.getCode().equals(currencyCode)).findAny(); } public static boolean isCryptoNoteCoin(String currencyCode) { @@ -206,6 +259,9 @@ public class CurrencyUtil { if (!countryCode.equals("LT")) return new FiatCurrency(Currency.getInstance(new Locale(LanguageUtil.getDefaultLanguage(), countryCode)).getCurrencyCode()); else { + if (!Currency.getInstance(new Locale(LanguageUtil.getDefaultLanguage(), countryCode)).getCurrencyCode().equals("EUR")) + log.error("wrong currency reported for LT"); + return new FiatCurrency("EUR"); } } @@ -216,7 +272,7 @@ public class CurrencyUtil { return Currency.getInstance(currencyCode).getDisplayName(Preferences.getDefaultLocale()); } catch (Throwable t) { // Seems that it is a cryptocurrency - return getSortedCryptoCurrencies().stream().filter(e -> e.getCode().equals(currencyCode)).findFirst().get().getName(); + return getAllSortedCryptoCurrencies().stream().filter(e -> e.getCode().equals(currencyCode)).findFirst().get().getName(); } } diff --git a/core/src/main/java/io/bitsquare/locale/TradeCurrency.java b/core/src/main/java/io/bitsquare/locale/TradeCurrency.java index c3119c6b8c..64aa413f4e 100644 --- a/core/src/main/java/io/bitsquare/locale/TradeCurrency.java +++ b/core/src/main/java/io/bitsquare/locale/TradeCurrency.java @@ -19,8 +19,9 @@ package io.bitsquare.locale; import io.bitsquare.app.Version; import io.bitsquare.common.persistance.Persistable; +import org.jetbrains.annotations.NotNull; -public abstract class TradeCurrency implements Persistable { +public abstract class TradeCurrency implements Persistable, Comparable { // That object is saved to disc. We need to take care of changes to not break deserialization. private static final long serialVersionUID = Version.LOCAL_DB_VERSION; @@ -65,6 +66,11 @@ public abstract class TradeCurrency implements Persistable { return code + " (" + name + ")"; } + @Override + public int compareTo(@NotNull TradeCurrency other) { + return this.getName().compareTo(other.getName()); + } + @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/core/src/main/java/io/bitsquare/payment/BankAccountContractData.java b/core/src/main/java/io/bitsquare/payment/BankAccountContractData.java index ad540b667f..d034cfde58 100644 --- a/core/src/main/java/io/bitsquare/payment/BankAccountContractData.java +++ b/core/src/main/java/io/bitsquare/payment/BankAccountContractData.java @@ -29,12 +29,12 @@ public abstract class BankAccountContractData extends PaymentAccountContractData private static final Logger log = LoggerFactory.getLogger(BankAccountContractData.class); - private String holderName; - private String bankName; - private String bankId; - private String branchId; - private String accountNr; - private String holderId; + protected String holderName; + protected String bankName; + protected String bankId; + protected String branchId; + protected String accountNr; + protected String holderId; public BankAccountContractData(String paymentMethod, String id, int maxTradePeriod) { super(paymentMethod, id, maxTradePeriod); diff --git a/core/src/main/java/io/bitsquare/payment/SameBankAccount.java b/core/src/main/java/io/bitsquare/payment/SameBankAccount.java index 3f687051d1..144fef6985 100644 --- a/core/src/main/java/io/bitsquare/payment/SameBankAccount.java +++ b/core/src/main/java/io/bitsquare/payment/SameBankAccount.java @@ -31,4 +31,8 @@ public final class SameBankAccount extends PaymentAccount { protected PaymentAccountContractData setContractData() { return new SameBankAccountContractData(paymentMethod.getId(), id, paymentMethod.getMaxTradePeriod()); } + + public String getAcceptedBank() { + return ((SameBankAccountContractData) contractData).getBankName(); + } } diff --git a/core/src/main/java/io/bitsquare/payment/SpecificBankAccount.java b/core/src/main/java/io/bitsquare/payment/SpecificBankAccount.java index 6ffc1b911e..23e8e60cf7 100644 --- a/core/src/main/java/io/bitsquare/payment/SpecificBankAccount.java +++ b/core/src/main/java/io/bitsquare/payment/SpecificBankAccount.java @@ -19,6 +19,8 @@ package io.bitsquare.payment; import io.bitsquare.app.Version; +import java.util.ArrayList; + public final class SpecificBankAccount extends PaymentAccount { // That object is saved to disc. We need to take care of changes to not break deserialization. private static final long serialVersionUID = Version.LOCAL_DB_VERSION; @@ -31,4 +33,8 @@ public final class SpecificBankAccount extends PaymentAccount { protected PaymentAccountContractData setContractData() { return new SpecificBanksAccountContractData(paymentMethod.getId(), id, paymentMethod.getMaxTradePeriod()); } + + public ArrayList getAcceptedBanks() { + return ((SpecificBanksAccountContractData) contractData).getAcceptedBanks(); + } } diff --git a/core/src/main/java/io/bitsquare/payment/SpecificBanksAccountContractData.java b/core/src/main/java/io/bitsquare/payment/SpecificBanksAccountContractData.java index 70fd789999..e8cb1ceba9 100644 --- a/core/src/main/java/io/bitsquare/payment/SpecificBanksAccountContractData.java +++ b/core/src/main/java/io/bitsquare/payment/SpecificBanksAccountContractData.java @@ -17,7 +17,10 @@ package io.bitsquare.payment; +import com.google.common.base.Joiner; import io.bitsquare.app.Version; +import io.bitsquare.locale.BankUtil; +import io.bitsquare.locale.CountryUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -56,4 +59,17 @@ public final class SpecificBanksAccountContractData extends BankAccountContractD public String getPaymentDetails() { return "Transfers with specific banks - " + getPaymentDetailsForTradePopup().replace("\n", ", "); } + + @Override + public String getPaymentDetailsForTradePopup() { + String holderIdString = BankUtil.requiresHolderId(countryCode) ? (getHolderIdLabel() + ": " + holderId + "\n") : ""; + return "Holder name: " + holderName + "\n" + + holderIdString + + "Bank name: " + bankName + "\n" + + "Bank Nr.: " + bankId + "\n" + + "Branch Nr.: " + branchId + "\n" + + "Account Nr.: " + accountNr + "\n" + + "Accepted banks: " + Joiner.on(", ").join(acceptedBanks) + "\n" + + "Country of bank: " + CountryUtil.getNameAndCode(getCountryCode()); + } } diff --git a/core/src/main/java/io/bitsquare/trade/offer/Offer.java b/core/src/main/java/io/bitsquare/trade/offer/Offer.java index 9aa5e1c74d..71becc170a 100644 --- a/core/src/main/java/io/bitsquare/trade/offer/Offer.java +++ b/core/src/main/java/io/bitsquare/trade/offer/Offer.java @@ -102,6 +102,8 @@ public final class Offer implements StoragePayload, RequiresOwnerIsOnlinePayload @Nullable private final ArrayList acceptedCountryCodes; + @Nullable + private final ArrayList acceptedBanks; private final ArrayList arbitratorNodeAddresses; // Mutable property. Has to be set before offer is save in P2P network as it changes the objects hash! @@ -136,7 +138,8 @@ public final class Offer implements StoragePayload, RequiresOwnerIsOnlinePayload @Nullable Country paymentMethodCountry, String offererPaymentAccountId, ArrayList arbitratorNodeAddresses, - @Nullable ArrayList acceptedCountryCodes) { + @Nullable ArrayList acceptedCountryCodes, + @Nullable ArrayList acceptedBanks) { this.id = id; this.offererNodeAddress = offererNodeAddress; this.pubKeyRing = pubKeyRing; @@ -150,6 +153,7 @@ public final class Offer implements StoragePayload, RequiresOwnerIsOnlinePayload this.offererPaymentAccountId = offererPaymentAccountId; this.arbitratorNodeAddresses = arbitratorNodeAddresses; this.acceptedCountryCodes = acceptedCountryCodes; + this.acceptedBanks = acceptedBanks; protocolVersion = Version.TRADE_PROTOCOL_VERSION; @@ -336,6 +340,11 @@ public final class Offer implements StoragePayload, RequiresOwnerIsOnlinePayload return acceptedCountryCodes; } + @Nullable + public List getAcceptedBanks() { + return acceptedBanks; + } + public String getOfferFeePaymentTxID() { return offerFeePaymentTxID; } @@ -389,6 +398,8 @@ public final class Offer implements StoragePayload, RequiresOwnerIsOnlinePayload return false; if (acceptedCountryCodes != null ? !acceptedCountryCodes.equals(offer.acceptedCountryCodes) : offer.acceptedCountryCodes != null) return false; + if (acceptedBanks != null ? !acceptedBanks.equals(offer.acceptedBanks) : offer.acceptedBanks != null) + return false; if (arbitratorNodeAddresses != null ? !arbitratorNodeAddresses.equals(offer.arbitratorNodeAddresses) : offer.arbitratorNodeAddresses != null) return false; return !(offerFeePaymentTxID != null ? !offerFeePaymentTxID.equals(offer.offerFeePaymentTxID) : offer.offerFeePaymentTxID != null); @@ -410,6 +421,7 @@ public final class Offer implements StoragePayload, RequiresOwnerIsOnlinePayload result = 31 * result + (paymentMethodCountryCode != null ? paymentMethodCountryCode.hashCode() : 0); result = 31 * result + (offererPaymentAccountId != null ? offererPaymentAccountId.hashCode() : 0); result = 31 * result + (acceptedCountryCodes != null ? acceptedCountryCodes.hashCode() : 0); + result = 31 * result + (acceptedBanks != null ? acceptedBanks.hashCode() : 0); result = 31 * result + (arbitratorNodeAddresses != null ? arbitratorNodeAddresses.hashCode() : 0); result = 31 * result + (offerFeePaymentTxID != null ? offerFeePaymentTxID.hashCode() : 0); return result; @@ -431,6 +443,7 @@ public final class Offer implements StoragePayload, RequiresOwnerIsOnlinePayload "\n\tpaymentMethodCountryCode='" + paymentMethodCountryCode + '\'' + "\n\toffererPaymentAccountId='" + offererPaymentAccountId + '\'' + "\n\tacceptedCountryCodes=" + acceptedCountryCodes + + "\n\tacceptedBanks=" + acceptedBanks + "\n\tarbitratorAddresses=" + arbitratorNodeAddresses + "\n\tofferFeePaymentTxID='" + offerFeePaymentTxID + '\'' + "\n\tstate=" + state + diff --git a/core/src/main/java/io/bitsquare/user/Preferences.java b/core/src/main/java/io/bitsquare/user/Preferences.java index 3e34d79f43..e00308f401 100644 --- a/core/src/main/java/io/bitsquare/user/Preferences.java +++ b/core/src/main/java/io/bitsquare/user/Preferences.java @@ -22,10 +22,7 @@ import io.bitsquare.app.Version; import io.bitsquare.btc.BitcoinNetwork; import io.bitsquare.btc.FeePolicy; import io.bitsquare.common.persistance.Persistable; -import io.bitsquare.locale.CountryUtil; -import io.bitsquare.locale.CurrencyUtil; -import io.bitsquare.locale.FiatCurrency; -import io.bitsquare.locale.TradeCurrency; +import io.bitsquare.locale.*; import io.bitsquare.storage.Storage; import javafx.beans.Observable; import javafx.beans.property.BooleanProperty; @@ -33,6 +30,7 @@ import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; import javafx.collections.FXCollections; +import javafx.collections.ListChangeListener; import javafx.collections.ObservableList; import org.bitcoinj.core.Coin; import org.bitcoinj.core.Transaction; @@ -94,7 +92,8 @@ public final class Preferences implements Persistable { private String btcDenomination = MonetaryFormat.CODE_BTC; private boolean useAnimations = true; private boolean useEffects = true; - private final ArrayList tradeCurrencies; + private final ArrayList fiatCurrencies; + private final ArrayList cryptoCurrencies; private BlockChainExplorer blockChainExplorerMainNet; private BlockChainExplorer blockChainExplorerTestNet; private boolean showNotifications = true; @@ -112,6 +111,8 @@ public final class Preferences implements Persistable { transient private final StringProperty btcDenominationProperty = new SimpleStringProperty(btcDenomination); transient private final BooleanProperty useAnimationsProperty = new SimpleBooleanProperty(useAnimations); transient private final BooleanProperty useEffectsProperty = new SimpleBooleanProperty(useEffects); + transient private final ObservableList fiatCurrenciesAsObservable = FXCollections.observableArrayList(); + transient private final ObservableList cryptoCurrenciesAsObservable = FXCollections.observableArrayList(); transient private final ObservableList tradeCurrenciesAsObservable = FXCollections.observableArrayList(); @@ -130,8 +131,12 @@ public final class Preferences implements Persistable { setBtcDenomination(persisted.btcDenomination); setUseAnimations(persisted.useAnimations); setUseEffects(persisted.useEffects); - setTradeCurrencies(persisted.tradeCurrencies); - tradeCurrencies = new ArrayList<>(tradeCurrenciesAsObservable); + + setFiatCurrencies(persisted.fiatCurrencies); + fiatCurrencies = new ArrayList<>(fiatCurrenciesAsObservable); + + setCryptoCurrencies(persisted.cryptoCurrencies); + cryptoCurrencies = new ArrayList<>(cryptoCurrenciesAsObservable); setBlockChainExplorerTestNet(persisted.getBlockChainExplorerTestNet()); setBlockChainExplorerMainNet(persisted.getBlockChainExplorerMainNet()); @@ -160,8 +165,12 @@ public final class Preferences implements Persistable { // leave default value } } else { - setTradeCurrencies(CurrencyUtil.getAllSortedCurrencies()); - tradeCurrencies = new ArrayList<>(tradeCurrenciesAsObservable); + setFiatCurrencies(CurrencyUtil.getAllMainFiatCurrencies()); + fiatCurrencies = new ArrayList<>(fiatCurrenciesAsObservable); + + setCryptoCurrencies(CurrencyUtil.getMainCryptoCurrencies()); + cryptoCurrencies = new ArrayList<>(cryptoCurrenciesAsObservable); + setBlockChainExplorerTestNet(blockChainExplorersTestNet.get(0)); setBlockChainExplorerMainNet(blockChainExplorersMainNet.get(0)); @@ -193,11 +202,21 @@ public final class Preferences implements Persistable { useEffects = useEffectsProperty.get(); storage.queueUpForSave(2000); }); - tradeCurrenciesAsObservable.addListener((Observable ov) -> { - tradeCurrencies.clear(); - tradeCurrencies.addAll(tradeCurrenciesAsObservable); + fiatCurrenciesAsObservable.addListener((Observable ov) -> { + fiatCurrencies.clear(); + fiatCurrencies.addAll(fiatCurrenciesAsObservable); storage.queueUpForSave(); }); + cryptoCurrenciesAsObservable.addListener((Observable ov) -> { + cryptoCurrencies.clear(); + cryptoCurrencies.addAll(cryptoCurrenciesAsObservable); + storage.queueUpForSave(); + }); + + fiatCurrenciesAsObservable.addListener((ListChangeListener) this::updateTradeCurrencies); + cryptoCurrenciesAsObservable.addListener((ListChangeListener) this::updateTradeCurrencies); + tradeCurrenciesAsObservable.addAll(fiatCurrencies); + tradeCurrenciesAsObservable.addAll(cryptoCurrencies); } public void dontShowAgain(String id) { @@ -205,6 +224,14 @@ public final class Preferences implements Persistable { storage.queueUpForSave(2000); } + private void updateTradeCurrencies(ListChangeListener.Change change) { + change.next(); + if (change.wasAdded() && change.getAddedSize() == 1) + tradeCurrenciesAsObservable.add(change.getAddedSubList().get(0)); + else if (change.wasRemoved() && change.getRemovedSize() == 1) + tradeCurrenciesAsObservable.remove(change.getRemoved().get(0)); + } + /////////////////////////////////////////////////////////////////////////////////////////// // Setter @@ -231,8 +258,46 @@ public final class Preferences implements Persistable { // We don't store the bitcoinNetwork locally as BitcoinNetwork is not serializable! } - private void setTradeCurrencies(List tradeCurrencies) { - tradeCurrenciesAsObservable.setAll(tradeCurrencies); + private void setFiatCurrencies(List currencies) { + fiatCurrenciesAsObservable.setAll(currencies); + } + + private void setCryptoCurrencies(List currencies) { + cryptoCurrenciesAsObservable.setAll(currencies); + } + + public void addFiatCurrency(FiatCurrency tradeCurrency) { + if (!fiatCurrenciesAsObservable.contains(tradeCurrency)) + fiatCurrenciesAsObservable.add(tradeCurrency); + } + + public void removeFiatCurrency(FiatCurrency tradeCurrency) { + if (tradeCurrenciesAsObservable.size() > 1) { + if (fiatCurrenciesAsObservable.contains(tradeCurrency)) + fiatCurrenciesAsObservable.remove(tradeCurrency); + + if (preferredTradeCurrency.equals(tradeCurrency)) + setPreferredTradeCurrency(tradeCurrenciesAsObservable.get(0)); + } else { + log.error("you cannot remove the last currency"); + } + } + + public void addCryptoCurrency(CryptoCurrency tradeCurrency) { + if (!cryptoCurrenciesAsObservable.contains(tradeCurrency)) + cryptoCurrenciesAsObservable.add(tradeCurrency); + } + + public void removeCryptoCurrency(CryptoCurrency tradeCurrency) { + if (tradeCurrenciesAsObservable.size() > 1) { + if (cryptoCurrenciesAsObservable.contains(tradeCurrency)) + cryptoCurrenciesAsObservable.remove(tradeCurrency); + + if (preferredTradeCurrency.equals(tradeCurrency)) + setPreferredTradeCurrency(tradeCurrenciesAsObservable.get(0)); + } else { + log.error("you cannot remove the last currency"); + } } private void setBlockChainExplorerTestNet(BlockChainExplorer blockChainExplorerTestNet) { @@ -274,9 +339,11 @@ public final class Preferences implements Persistable { } public void setPreferredTradeCurrency(TradeCurrency preferredTradeCurrency) { - this.preferredTradeCurrency = preferredTradeCurrency; - defaultTradeCurrency = preferredTradeCurrency; - storage.queueUpForSave(); + if (preferredTradeCurrency != null) { + this.preferredTradeCurrency = preferredTradeCurrency; + defaultTradeCurrency = preferredTradeCurrency; + storage.queueUpForSave(); + } } public void setTxFeePerKB(long txFeePerKB) throws Exception { @@ -329,6 +396,14 @@ public final class Preferences implements Persistable { return bitcoinNetwork; } + public ObservableList getFiatCurrenciesAsObservable() { + return fiatCurrenciesAsObservable; + } + + public ObservableList getCryptoCurrenciesAsObservable() { + return cryptoCurrenciesAsObservable; + } + public ObservableList getTradeCurrenciesAsObservable() { return tradeCurrenciesAsObservable; } diff --git a/gui/src/main/java/io/bitsquare/gui/components/paymentmethods/BankForm.java b/gui/src/main/java/io/bitsquare/gui/components/paymentmethods/BankForm.java index 64e508464e..fd4f3dadec 100644 --- a/gui/src/main/java/io/bitsquare/gui/components/paymentmethods/BankForm.java +++ b/gui/src/main/java/io/bitsquare/gui/components/paymentmethods/BankForm.java @@ -53,14 +53,15 @@ abstract class BankForm extends PaymentMethodForm { static int addFormForBuyer(GridPane gridPane, int gridRow, PaymentAccountContractData paymentAccountContractData) { BankAccountContractData bankAccountContractData = (BankAccountContractData) paymentAccountContractData; - addLabelTextFieldWithCopyIcon(gridPane, ++gridRow, "Account holder name:", bankAccountContractData.getHolderName()); - addLabelTextFieldWithCopyIcon(gridPane, ++gridRow, "Country of bank:", CountryUtil.getNameAndCode(bankAccountContractData.getCountryCode())); - addLabelTextFieldWithCopyIcon(gridPane, ++gridRow, "Bank name:", bankAccountContractData.getBankName()); - addLabelTextFieldWithCopyIcon(gridPane, ++gridRow, "Bank number:", bankAccountContractData.getBankId()); - addLabelTextFieldWithCopyIcon(gridPane, ++gridRow, "Branch number:", bankAccountContractData.getBranchId()); - addLabelTextFieldWithCopyIcon(gridPane, ++gridRow, "Account number:", bankAccountContractData.getAccountNr()); if (bankAccountContractData.getHolderId() != null) - addLabelTextFieldWithCopyIcon(gridPane, ++gridRow, bankAccountContractData.getHolderIdLabel(), bankAccountContractData.getHolderId()); + addLabelTextFieldWithCopyIcon(gridPane, ++gridRow, "Account holder name / " + bankAccountContractData.getHolderIdLabel(), + bankAccountContractData.getHolderName() + " / " + bankAccountContractData.getHolderId()); + else + addLabelTextFieldWithCopyIcon(gridPane, ++gridRow, "Account holder name:", bankAccountContractData.getHolderName()); + + addLabelTextFieldWithCopyIcon(gridPane, ++gridRow, "Country of bank:", CountryUtil.getNameAndCode(bankAccountContractData.getCountryCode())); + addLabelTextFieldWithCopyIcon(gridPane, ++gridRow, "Bank name / number:", bankAccountContractData.getBankName() + " / " + bankAccountContractData.getBankId()); + addLabelTextFieldWithCopyIcon(gridPane, ++gridRow, "Branch number / Account number:", bankAccountContractData.getBranchId() + " / " + bankAccountContractData.getAccountNr()); return gridRow; } diff --git a/gui/src/main/java/io/bitsquare/gui/components/paymentmethods/BlockChainForm.java b/gui/src/main/java/io/bitsquare/gui/components/paymentmethods/BlockChainForm.java index f5b52e9140..26a70e7a29 100644 --- a/gui/src/main/java/io/bitsquare/gui/components/paymentmethods/BlockChainForm.java +++ b/gui/src/main/java/io/bitsquare/gui/components/paymentmethods/BlockChainForm.java @@ -114,7 +114,7 @@ public class BlockChainForm extends PaymentMethodForm { protected void addTradeCurrencyComboBox() { currencyComboBox = addLabelComboBox(gridPane, ++gridRow, "Crypto currency:", Layout.FIRST_ROW_AND_GROUP_DISTANCE).second; currencyComboBox.setPromptText("Select cryptocurrency"); - currencyComboBox.setItems(FXCollections.observableArrayList(CurrencyUtil.getSortedCryptoCurrencies())); + currencyComboBox.setItems(FXCollections.observableArrayList(CurrencyUtil.getAllSortedCryptoCurrencies())); currencyComboBox.setVisibleRowCount(Math.min(currencyComboBox.getItems().size(), 20)); currencyComboBox.setConverter(new StringConverter() { @Override diff --git a/gui/src/main/java/io/bitsquare/gui/components/paymentmethods/NationalBankForm.java b/gui/src/main/java/io/bitsquare/gui/components/paymentmethods/NationalBankForm.java index aa9b168583..32a17ad64f 100644 --- a/gui/src/main/java/io/bitsquare/gui/components/paymentmethods/NationalBankForm.java +++ b/gui/src/main/java/io/bitsquare/gui/components/paymentmethods/NationalBankForm.java @@ -28,8 +28,7 @@ public class NationalBankForm extends BankForm { private static final Logger log = LoggerFactory.getLogger(NationalBankForm.class); public static int addFormForBuyer(GridPane gridPane, int gridRow, PaymentAccountContractData paymentAccountContractData) { - BankForm.addFormForBuyer(gridPane, gridRow, paymentAccountContractData); - return gridRow; + return BankForm.addFormForBuyer(gridPane, gridRow, paymentAccountContractData); } public NationalBankForm(PaymentAccount paymentAccount, InputValidator inputValidator, diff --git a/gui/src/main/java/io/bitsquare/gui/components/paymentmethods/PaymentMethodForm.java b/gui/src/main/java/io/bitsquare/gui/components/paymentmethods/PaymentMethodForm.java index 4f0ec7307d..035ea32f4b 100644 --- a/gui/src/main/java/io/bitsquare/gui/components/paymentmethods/PaymentMethodForm.java +++ b/gui/src/main/java/io/bitsquare/gui/components/paymentmethods/PaymentMethodForm.java @@ -61,7 +61,7 @@ public abstract class PaymentMethodForm { protected void addTradeCurrencyComboBox() { currencyComboBox = addLabelComboBox(gridPane, ++gridRow, "Currency:").second; currencyComboBox.setPromptText("Select currency"); - currencyComboBox.setItems(FXCollections.observableArrayList(CurrencyUtil.getAllSortedCurrencies())); + currencyComboBox.setItems(FXCollections.observableArrayList(CurrencyUtil.getAllMainFiatCurrencies())); currencyComboBox.setConverter(new StringConverter() { @Override public String toString(TradeCurrency tradeCurrency) { diff --git a/gui/src/main/java/io/bitsquare/gui/components/paymentmethods/SameBankForm.java b/gui/src/main/java/io/bitsquare/gui/components/paymentmethods/SameBankForm.java index 2baf0e6fa2..ac42c074e5 100644 --- a/gui/src/main/java/io/bitsquare/gui/components/paymentmethods/SameBankForm.java +++ b/gui/src/main/java/io/bitsquare/gui/components/paymentmethods/SameBankForm.java @@ -35,8 +35,7 @@ public class SameBankForm extends BankForm { private static final Logger log = LoggerFactory.getLogger(SameBankForm.class); public static int addFormForBuyer(GridPane gridPane, int gridRow, PaymentAccountContractData paymentAccountContractData) { - BankForm.addFormForBuyer(gridPane, gridRow, paymentAccountContractData); - return gridRow; + return BankForm.addFormForBuyer(gridPane, gridRow, paymentAccountContractData); } public SameBankForm(PaymentAccount paymentAccount, InputValidator inputValidator, @@ -72,10 +71,7 @@ public class SameBankForm extends BankForm { Tuple2 tuple = addLabelTextField(gridPane, ++gridRow, "Account holder name:"); TextField holderNameTextField = tuple.second; holderNameTextField.setMinWidth(300); - holderNameTextField.textProperty().addListener((ov, oldValue, newValue) -> { - bankAccountContractData.setHolderName(newValue); - updateFromInputs(); - }); + holderNameTextField.setText(bankAccountContractData.getHolderName()); } } diff --git a/gui/src/main/java/io/bitsquare/gui/components/paymentmethods/SpecificBankForm.java b/gui/src/main/java/io/bitsquare/gui/components/paymentmethods/SpecificBankForm.java index 02c9a312d6..ca58861688 100644 --- a/gui/src/main/java/io/bitsquare/gui/components/paymentmethods/SpecificBankForm.java +++ b/gui/src/main/java/io/bitsquare/gui/components/paymentmethods/SpecificBankForm.java @@ -43,8 +43,7 @@ public class SpecificBankForm extends BankForm { private Tooltip acceptedBanksTooltip; public static int addFormForBuyer(GridPane gridPane, int gridRow, PaymentAccountContractData paymentAccountContractData) { - BankForm.addFormForBuyer(gridPane, gridRow, paymentAccountContractData); - return gridRow; + return BankForm.addFormForBuyer(gridPane, gridRow, paymentAccountContractData); } public SpecificBankForm(PaymentAccount paymentAccount, InputValidator inputValidator, diff --git a/gui/src/main/java/io/bitsquare/gui/main/account/content/altcoinaccounts/AltCoinAccountsViewModel.java b/gui/src/main/java/io/bitsquare/gui/main/account/content/altcoinaccounts/AltCoinAccountsViewModel.java index ad831b83b1..8a8f20b6c6 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/account/content/altcoinaccounts/AltCoinAccountsViewModel.java +++ b/gui/src/main/java/io/bitsquare/gui/main/account/content/altcoinaccounts/AltCoinAccountsViewModel.java @@ -20,15 +20,24 @@ package io.bitsquare.gui.main.account.content.altcoinaccounts; import com.google.inject.Inject; import io.bitsquare.gui.common.model.ActivatableWithDataModel; import io.bitsquare.gui.common.model.ViewModel; +import io.bitsquare.locale.CryptoCurrency; +import io.bitsquare.locale.FiatCurrency; +import io.bitsquare.locale.TradeCurrency; import io.bitsquare.payment.PaymentAccount; +import io.bitsquare.user.Preferences; import javafx.collections.ObservableList; +import java.util.List; + class AltCoinAccountsViewModel extends ActivatableWithDataModel implements ViewModel { + private Preferences preferences; + @Inject - public AltCoinAccountsViewModel(AltCoinAccountsDataModel dataModel) { + public AltCoinAccountsViewModel(AltCoinAccountsDataModel dataModel, Preferences preferences) { super(dataModel); + this.preferences = preferences; } @Override @@ -46,6 +55,21 @@ class AltCoinAccountsViewModel extends ActivatableWithDataModel tradeCurrencies = paymentAccount.getTradeCurrencies(); + if (singleTradeCurrency != null) { + if (singleTradeCurrency instanceof FiatCurrency) + preferences.addFiatCurrency((FiatCurrency) singleTradeCurrency); + else + preferences.addCryptoCurrency((CryptoCurrency) singleTradeCurrency); + } else if (tradeCurrencies != null && !tradeCurrencies.isEmpty()) { + tradeCurrencies.stream().forEach(tradeCurrency -> { + if (tradeCurrency instanceof FiatCurrency) + preferences.addFiatCurrency((FiatCurrency) tradeCurrency); + else + preferences.addCryptoCurrency((CryptoCurrency) tradeCurrency); + }); + } } public void onDeleteAccount(PaymentAccount paymentAccount) { diff --git a/gui/src/main/java/io/bitsquare/gui/main/account/content/fiataccounts/FiatAccountsDataModel.java b/gui/src/main/java/io/bitsquare/gui/main/account/content/fiataccounts/FiatAccountsDataModel.java index e8e48dd0f4..24b79a4da6 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/account/content/fiataccounts/FiatAccountsDataModel.java +++ b/gui/src/main/java/io/bitsquare/gui/main/account/content/fiataccounts/FiatAccountsDataModel.java @@ -19,8 +19,12 @@ package io.bitsquare.gui.main.account.content.fiataccounts; import com.google.inject.Inject; import io.bitsquare.gui.common.model.ActivatableDataModel; +import io.bitsquare.locale.CryptoCurrency; +import io.bitsquare.locale.FiatCurrency; +import io.bitsquare.locale.TradeCurrency; import io.bitsquare.payment.PaymentAccount; import io.bitsquare.payment.PaymentMethod; +import io.bitsquare.user.Preferences; import io.bitsquare.user.User; import javafx.collections.FXCollections; import javafx.collections.ObservableList; @@ -32,12 +36,14 @@ import java.util.stream.Collectors; class FiatAccountsDataModel extends ActivatableDataModel { private final User user; + private Preferences preferences; final ObservableList paymentAccounts = FXCollections.observableArrayList(); private final SetChangeListener setChangeListener; @Inject - public FiatAccountsDataModel(User user) { + public FiatAccountsDataModel(User user, Preferences preferences) { this.user = user; + this.preferences = preferences; setChangeListener = change -> fillAndSortPaymentAccounts(); } @@ -67,6 +73,21 @@ class FiatAccountsDataModel extends ActivatableDataModel { public void onSaveNewAccount(PaymentAccount paymentAccount) { user.addPaymentAccount(paymentAccount); + TradeCurrency singleTradeCurrency = paymentAccount.getSingleTradeCurrency(); + List tradeCurrencies = paymentAccount.getTradeCurrencies(); + if (singleTradeCurrency != null) { + if (singleTradeCurrency instanceof FiatCurrency) + preferences.addFiatCurrency((FiatCurrency) singleTradeCurrency); + else + preferences.addCryptoCurrency((CryptoCurrency) singleTradeCurrency); + } else if (tradeCurrencies != null && !tradeCurrencies.isEmpty()) { + tradeCurrencies.stream().forEach(tradeCurrency -> { + if (tradeCurrency instanceof FiatCurrency) + preferences.addFiatCurrency((FiatCurrency) tradeCurrency); + else + preferences.addCryptoCurrency((CryptoCurrency) tradeCurrency); + }); + } } public void onDeleteAccount(PaymentAccount paymentAccount) { diff --git a/gui/src/main/java/io/bitsquare/gui/main/markets/charts/MarketsChartsView.java b/gui/src/main/java/io/bitsquare/gui/main/markets/charts/MarketsChartsView.java index 8ecc30aa0c..c157f7c006 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/markets/charts/MarketsChartsView.java +++ b/gui/src/main/java/io/bitsquare/gui/main/markets/charts/MarketsChartsView.java @@ -149,6 +149,7 @@ public class MarketsChartsView extends ActivatableViewAndModel acceptedBanks = new ArrayList<>(); + if (paymentAccount instanceof SpecificBankAccount) + acceptedBanks.addAll(((SpecificBankAccount) paymentAccount).getAcceptedBanks()); + else if (paymentAccount instanceof SameBankAccount) + acceptedBanks.add(((SameBankAccount) paymentAccount).getAcceptedBank()); + // That is optional and set to null if not supported (AltCoins, OKPay,...) Country country = paymentAccount.getCountry(); @@ -258,7 +266,8 @@ class CreateOfferDataModel extends ActivatableDataModel { country, paymentAccount.getId(), new ArrayList<>(user.getAcceptedArbitratorAddresses()), - acceptedCountryCodes); + acceptedCountryCodes, + acceptedBanks); } void onPlaceOffer(Offer offer, TransactionResultHandler resultHandler) { diff --git a/gui/src/main/java/io/bitsquare/gui/main/offer/offerbook/OfferBookViewModel.java b/gui/src/main/java/io/bitsquare/gui/main/offer/offerbook/OfferBookViewModel.java index ae8a7d6907..34c18592b7 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/offer/offerbook/OfferBookViewModel.java +++ b/gui/src/main/java/io/bitsquare/gui/main/offer/offerbook/OfferBookViewModel.java @@ -17,6 +17,7 @@ package io.bitsquare.gui.main.offer.offerbook; +import com.google.common.base.Joiner; import com.google.inject.Inject; import io.bitsquare.app.Version; import io.bitsquare.btc.pricefeed.PriceFeed; @@ -60,6 +61,7 @@ class OfferBookViewModel extends ActivatableViewModel { private final FilteredList filteredItems; private final SortedList sortedItems; + private final ListChangeListener tradeCurrencyListChangeListener; private TradeCurrency selectedTradeCurrency; private final ObservableList allTradeCurrencies = FXCollections.observableArrayList(); @@ -106,13 +108,7 @@ class OfferBookViewModel extends ActivatableViewModel { selectedTradeCurrency = CurrencyUtil.getDefaultTradeCurrency(); tradeCurrencyCode.set(selectedTradeCurrency.getCode()); - preferences.getTradeCurrenciesAsObservable().addListener(new ListChangeListener() { - @Override - public void onChanged(Change c) { - fillAllTradeCurrencies(); - } - }); - + tradeCurrencyListChangeListener = c -> fillAllTradeCurrencies(); } @Override @@ -120,6 +116,7 @@ class OfferBookViewModel extends ActivatableViewModel { fillAllTradeCurrencies(); btcCode.bind(preferences.btcDenominationProperty()); offerBookListItems.addListener(listChangeListener); + preferences.getTradeCurrenciesAsObservable().addListener(tradeCurrencyListChangeListener); offerBook.fillOfferBookListItems(); filterList(); setMarketPriceFeedCurrency(); @@ -130,6 +127,7 @@ class OfferBookViewModel extends ActivatableViewModel { protected void deactivate() { btcCode.unbind(); offerBookListItems.removeListener(listChangeListener); + preferences.getTradeCurrenciesAsObservable().removeListener(tradeCurrencyListChangeListener); } private void fillAllTradeCurrencies() { @@ -255,7 +253,7 @@ class OfferBookViewModel extends ActivatableViewModel { String result = ""; if (item != null) { Offer offer = item.getOffer(); - String method = BSResources.get(offer.getPaymentMethod().getId()); + String method = BSResources.get(offer.getPaymentMethod().getId() + "_SHORT"); String methodCountryCode = offer.getPaymentMethodCountryCode(); if (methodCountryCode != null) @@ -279,11 +277,17 @@ class OfferBookViewModel extends ActivatableViewModel { result = method; List acceptedCountryCodes = offer.getAcceptedCountryCodes(); - if (acceptedCountryCodes != null && acceptedCountryCodes.size() > 0) { + List acceptedBanks = offer.getAcceptedBanks(); + if (acceptedCountryCodes != null && !acceptedCountryCodes.isEmpty()) { if (CountryUtil.containsAllSepaEuroCountries(acceptedCountryCodes)) result += "\n\nAccepted takers seat of bank countries:\nAll Euro countries"; else result += "\n\nAccepted taker seat of bank countries:\n" + CountryUtil.getNamesByCodesString(acceptedCountryCodes); + } else if (acceptedBanks != null && !acceptedBanks.isEmpty()) { + if (offer.getPaymentMethod().equals(PaymentMethod.SAME_BANK)) + result += "\n\nBank name: " + acceptedBanks.get(0); + else if (offer.getPaymentMethod().equals(PaymentMethod.SPECIFIC_BANKS)) + result += "\n\nAccepted banks: " + Joiner.on(", ").join(acceptedBanks); } } return result; @@ -303,7 +307,7 @@ class OfferBookViewModel extends ActivatableViewModel { } boolean isPaymentAccountValidForOffer(Offer offer) { - + // TODO not supporting yet check for acceptedBanks in cae of SpecificBankAccount and SameBankAccount Optional result1 = user.getPaymentAccounts().stream() .filter(paymentAccount -> paymentAccount.getPaymentMethod().equals(offer.getPaymentMethod())) .filter(paymentAccount -> { diff --git a/gui/src/main/java/io/bitsquare/gui/main/popups/ContractPopup.java b/gui/src/main/java/io/bitsquare/gui/main/popups/ContractPopup.java index 7afd2a210b..05b5a47069 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/popups/ContractPopup.java +++ b/gui/src/main/java/io/bitsquare/gui/main/popups/ContractPopup.java @@ -17,6 +17,7 @@ package io.bitsquare.gui.main.popups; +import com.google.common.base.Joiner; import io.bitsquare.arbitration.Dispute; import io.bitsquare.gui.util.BSFormatter; import io.bitsquare.gui.util.Layout; @@ -24,6 +25,7 @@ import io.bitsquare.locale.BSResources; import io.bitsquare.locale.CountryUtil; import io.bitsquare.payment.BlockChainAccountContractData; import io.bitsquare.payment.PaymentAccountContractData; +import io.bitsquare.payment.PaymentMethod; import io.bitsquare.trade.Contract; import io.bitsquare.trade.offer.Offer; import javafx.geometry.Insets; @@ -35,6 +37,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.inject.Inject; +import java.util.List; import java.util.Optional; import static io.bitsquare.gui.util.FormBuilder.*; @@ -90,12 +93,19 @@ public class ContractPopup extends Popup { Contract contract = dispute.getContract(); Offer offer = contract.offer; + List acceptedBanks = offer.getAcceptedBanks(); + boolean showAcceptedBanks = acceptedBanks != null && !acceptedBanks.isEmpty(); + List acceptedCountryCodes = offer.getAcceptedCountryCodes(); + boolean showAcceptedCountryCodes = acceptedCountryCodes != null && !acceptedCountryCodes.isEmpty(); + int rows = 16; if (dispute.getDepositTxSerialized() != null) rows++; if (dispute.getPayoutTxSerialized() != null) rows++; - if (offer.getAcceptedCountryCodes() != null) + if (showAcceptedCountryCodes) + rows++; + if (showAcceptedBanks) rows++; boolean isPaymentIdAvailable = false; @@ -130,20 +140,31 @@ public class ContractPopup extends Popup { addLabelTextField(gridPane, ++rowIndex, "Seller payment ID:", ((BlockChainAccountContractData) sellerPaymentAccountContractData).getPaymentId()); - if (offer.getAcceptedCountryCodes() != null && !offer.getAcceptedCountryCodes().isEmpty()) { + if (showAcceptedCountryCodes) { String countries; Tooltip tooltip = null; - if (CountryUtil.containsAllSepaEuroCountries(offer.getAcceptedCountryCodes())) { + if (CountryUtil.containsAllSepaEuroCountries(acceptedCountryCodes)) { countries = "All Euro countries"; } else { - countries = CountryUtil.getCodesString(offer.getAcceptedCountryCodes()); - tooltip = new Tooltip(CountryUtil.getNamesByCodesString(offer.getAcceptedCountryCodes())); + countries = CountryUtil.getCodesString(acceptedCountryCodes); + tooltip = new Tooltip(CountryUtil.getNamesByCodesString(acceptedCountryCodes)); } TextField acceptedCountries = addLabelTextField(gridPane, ++rowIndex, "Accepted taker countries:", countries).second; if (tooltip != null) acceptedCountries.setTooltip(new Tooltip()); } - //addLabelTextField(gridPane, ++rowIndex, "Buyer Bitsquare account ID:", contract.getBuyerAccountId()).second.setMouseTransparent(false); - //addLabelTextField(gridPane, ++rowIndex, "Seller Bitsquare account ID:", contract.getSellerAccountId()).second.setMouseTransparent(false); + + if (showAcceptedBanks) { + if (offer.getPaymentMethod().equals(PaymentMethod.SAME_BANK)) { + addLabelTextField(gridPane, ++rowIndex, "Bank name:", acceptedBanks.get(0)); + } else if (offer.getPaymentMethod().equals(PaymentMethod.SPECIFIC_BANKS)) { + String value = Joiner.on(", ").join(acceptedBanks); + Tooltip tooltip = new Tooltip("Accepted banks: " + value); + TextField acceptedBanksTextField = addLabelTextField(gridPane, ++rowIndex, "Accepted banks:", value).second; + acceptedBanksTextField.setMouseTransparent(false); + acceptedBanksTextField.setTooltip(tooltip); + } + } + addLabelTxIdTextField(gridPane, ++rowIndex, "Offer fee transaction ID:", offer.getOfferFeePaymentTxID()); addLabelTxIdTextField(gridPane, ++rowIndex, "Trading fee transaction ID:", contract.takeOfferFeeTxID); if (dispute.getDepositTxSerialized() != null) diff --git a/gui/src/main/java/io/bitsquare/gui/main/popups/OfferDetailsPopup.java b/gui/src/main/java/io/bitsquare/gui/main/popups/OfferDetailsPopup.java index 0ccb694d75..78aed8f188 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/popups/OfferDetailsPopup.java +++ b/gui/src/main/java/io/bitsquare/gui/main/popups/OfferDetailsPopup.java @@ -17,6 +17,7 @@ package io.bitsquare.gui.main.popups; +import com.google.common.base.Joiner; import io.bitsquare.common.crypto.KeyRing; import io.bitsquare.common.util.Tuple2; import io.bitsquare.gui.Navigation; @@ -28,6 +29,7 @@ import io.bitsquare.gui.util.BSFormatter; import io.bitsquare.gui.util.Layout; import io.bitsquare.locale.BSResources; import io.bitsquare.locale.CountryUtil; +import io.bitsquare.payment.PaymentMethod; import io.bitsquare.trade.offer.Offer; import io.bitsquare.user.Preferences; import io.bitsquare.user.User; @@ -41,6 +43,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.inject.Inject; +import java.util.List; import java.util.Optional; import java.util.function.Consumer; @@ -134,8 +137,18 @@ public class OfferDetailsPopup extends Popup { private void addContent() { int rows = 5; + + List acceptedBanks = offer.getAcceptedBanks(); + boolean showAcceptedBanks = acceptedBanks != null && !acceptedBanks.isEmpty(); + List acceptedCountryCodes = offer.getAcceptedCountryCodes(); + boolean showAcceptedCountryCodes = acceptedCountryCodes != null && !acceptedCountryCodes.isEmpty(); + if (!takeOfferHandlerOptional.isPresent()) rows++; + if (showAcceptedBanks) + rows++; + if (showAcceptedCountryCodes) + rows++; addTitledGroupBg(gridPane, ++rowIndex, rows, "Offer"); @@ -154,22 +167,46 @@ public class OfferDetailsPopup extends Popup { addLabelTextField(gridPane, ++rowIndex, "Price:", formatter.formatFiat(offer.getPrice()) + " " + offer.getCurrencyCode() + "/" + "BTC"); addLabelTextField(gridPane, ++rowIndex, "Currency:", offer.getCurrencyCode()); - + 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())); + if (showAcceptedBanks) { + if (offer.getPaymentMethod().equals(PaymentMethod.SAME_BANK)) { + addLabelTextField(gridPane, ++rowIndex, "Bank name:", acceptedBanks.get(0)); + } else if (offer.getPaymentMethod().equals(PaymentMethod.SPECIFIC_BANKS)) { + String value = Joiner.on(", ").join(acceptedBanks); + Tooltip tooltip = new Tooltip("Accepted banks: " + value); + TextField acceptedBanksTextField = addLabelTextField(gridPane, ++rowIndex, "Accepted banks:", value).second; + acceptedBanksTextField.setMouseTransparent(false); + acceptedBanksTextField.setTooltip(tooltip); + } + } + + if (showAcceptedCountryCodes) { + String countries; + Tooltip tooltip = null; + if (CountryUtil.containsAllSepaEuroCountries(acceptedCountryCodes)) { + countries = "All Euro countries"; + } else { + countries = CountryUtil.getCodesString(acceptedCountryCodes); + tooltip = new Tooltip(CountryUtil.getNamesByCodesString(acceptedCountryCodes)); + } + TextField acceptedCountries = addLabelTextField(gridPane, ++rowIndex, "Accepted taker countries:", countries).second; + if (tooltip != null) { + acceptedCountries.setMouseTransparent(false); + acceptedCountries.setTooltip(tooltip); + } + } + rows = 3; String paymentMethodCountryCode = offer.getPaymentMethodCountryCode(); if (paymentMethodCountryCode != null) rows++; if (offer.getOfferFeePaymentTxID() != null) rows++; - if (offer.getAcceptedCountryCodes() != null) - rows++; - /* if (placeOfferHandlerOptional.isPresent()) - rows -= 2;*/ addTitledGroupBg(gridPane, ++rowIndex, rows, "Details", Layout.GROUP_DISTANCE); addLabelTextField(gridPane, rowIndex, "Offer ID:", offer.getId(), Layout.FIRST_ROW_AND_GROUP_DISTANCE); @@ -178,21 +215,7 @@ public class OfferDetailsPopup extends Popup { if (paymentMethodCountryCode != null) addLabelTextField(gridPane, ++rowIndex, "Offerers country of bank:", CountryUtil.getNameAndCode(paymentMethodCountryCode)); - if (offer.getAcceptedCountryCodes() != null) { - String countries; - Tooltip tooltip = null; - if (CountryUtil.containsAllSepaEuroCountries(offer.getAcceptedCountryCodes())) { - countries = "All Euro countries"; - } else { - countries = CountryUtil.getCodesString(offer.getAcceptedCountryCodes()); - tooltip = new Tooltip(CountryUtil.getNamesByCodesString(offer.getAcceptedCountryCodes())); - } - TextField acceptedCountries = addLabelTextField(gridPane, ++rowIndex, "Accepted taker countries:", countries).second; - if (tooltip != null) { - acceptedCountries.setMouseTransparent(false); - acceptedCountries.setTooltip(tooltip); - } - } + addLabelTextField(gridPane, ++rowIndex, "Accepted arbitrators:", formatter.arbitratorAddressesToString(offer.getArbitratorNodeAddresses())); if (offer.getOfferFeePaymentTxID() != null) addLabelTxIdTextField(gridPane, ++rowIndex, "Offer fee transaction ID:", offer.getOfferFeePaymentTxID()); diff --git a/gui/src/main/java/io/bitsquare/gui/main/popups/TradeDetailsPopup.java b/gui/src/main/java/io/bitsquare/gui/main/popups/TradeDetailsPopup.java index ce44f09633..9799cf9b39 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/popups/TradeDetailsPopup.java +++ b/gui/src/main/java/io/bitsquare/gui/main/popups/TradeDetailsPopup.java @@ -123,8 +123,10 @@ public class TradeDetailsPopup extends Popup { PaymentAccountContractData buyerPaymentAccountContractData = null; PaymentAccountContractData sellerPaymentAccountContractData = null; - if (offer.getAcceptedCountryCodes() != null) + /* if (offer.getAcceptedCountryCodes() != null) rows++; + if (offer.getAcceptedBanks() != null) + rows++;*/ if (contract != null) { rows++; diff --git a/gui/src/main/java/io/bitsquare/gui/main/portfolio/pendingtrades/PendingTradesViewModel.java b/gui/src/main/java/io/bitsquare/gui/main/portfolio/pendingtrades/PendingTradesViewModel.java index ea52df2e1a..1f59b12299 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/portfolio/pendingtrades/PendingTradesViewModel.java +++ b/gui/src/main/java/io/bitsquare/gui/main/portfolio/pendingtrades/PendingTradesViewModel.java @@ -112,7 +112,7 @@ public class PendingTradesViewModel extends ActivatableWithDataModel - + diff --git a/gui/src/main/java/io/bitsquare/gui/main/settings/network/NetworkSettingsView.java b/gui/src/main/java/io/bitsquare/gui/main/settings/network/NetworkSettingsView.java index f199f1228b..7d96082110 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/settings/network/NetworkSettingsView.java +++ b/gui/src/main/java/io/bitsquare/gui/main/settings/network/NetworkSettingsView.java @@ -80,6 +80,7 @@ public class NetworkSettingsView extends ActivatableViewAndModel. - */ - -package io.bitsquare.gui.main.settings.preferences; - -import com.google.inject.Inject; -import io.bitsquare.gui.common.model.ActivatableDataModel; -import io.bitsquare.user.Preferences; -import javafx.beans.property.BooleanProperty; -import javafx.beans.property.SimpleBooleanProperty; -import javafx.beans.property.SimpleStringProperty; -import javafx.beans.property.StringProperty; -import javafx.beans.value.ChangeListener; -import javafx.collections.FXCollections; -import javafx.collections.ObservableList; - -class PreferencesDataModel extends ActivatableDataModel { - - private final Preferences preferences; - - private final ChangeListener useAnimationsListener; - private final ChangeListener useEffectsListener; - private final ChangeListener btcDenominationListener; - - private final ObservableList btcDenominations; - - private final BooleanProperty useAnimations = new SimpleBooleanProperty(); - private final BooleanProperty useEffects = new SimpleBooleanProperty(); - private final StringProperty btcDenomination = new SimpleStringProperty(); - - - @Inject - public PreferencesDataModel(Preferences preferences) { - this.preferences = preferences; - - btcDenominations = FXCollections.observableArrayList(Preferences.getBtcDenominations()); - btcDenominationListener = (ov, oldValue, newValue) -> preferences.setBtcDenomination(newValue); - useAnimationsListener = (ov, oldValue, newValue) -> preferences.setUseAnimations(newValue); - useEffectsListener = (ov, oldValue, newValue) -> preferences.setUseEffects(newValue); - } - - - @Override - protected void activate() { - useAnimations.set(preferences.getUseAnimations()); - useEffects.set(preferences.getUseEffects()); - btcDenomination.set(preferences.getBtcDenomination()); - - useAnimations.addListener(useAnimationsListener); - useEffects.addListener(useEffectsListener); - btcDenomination.addListener(btcDenominationListener); - } - - @Override - protected void deactivate() { - useAnimations.removeListener(useAnimationsListener); - useEffects.removeListener(useEffectsListener); - btcDenomination.removeListener(btcDenominationListener); - } -} - diff --git a/gui/src/main/java/io/bitsquare/gui/main/settings/preferences/PreferencesView.fxml b/gui/src/main/java/io/bitsquare/gui/main/settings/preferences/PreferencesView.fxml index 5ca36ab5a1..80e5b8b2e9 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/settings/preferences/PreferencesView.fxml +++ b/gui/src/main/java/io/bitsquare/gui/main/settings/preferences/PreferencesView.fxml @@ -32,5 +32,7 @@ + + \ No newline at end of file diff --git a/gui/src/main/java/io/bitsquare/gui/main/settings/preferences/PreferencesView.java b/gui/src/main/java/io/bitsquare/gui/main/settings/preferences/PreferencesView.java index 2827fe8157..327e84c509 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/settings/preferences/PreferencesView.java +++ b/gui/src/main/java/io/bitsquare/gui/main/settings/preferences/PreferencesView.java @@ -17,30 +17,44 @@ package io.bitsquare.gui.main.settings.preferences; +import io.bitsquare.common.UserThread; +import io.bitsquare.common.util.Tuple2; +import io.bitsquare.gui.common.model.Activatable; import io.bitsquare.gui.common.view.ActivatableViewAndModel; import io.bitsquare.gui.common.view.FxmlView; +import io.bitsquare.gui.components.TitledGroupBg; +import io.bitsquare.gui.main.popups.Popup; +import io.bitsquare.gui.util.ImageUtil; import io.bitsquare.gui.util.Layout; -import io.bitsquare.locale.LanguageUtil; -import io.bitsquare.locale.TradeCurrency; +import io.bitsquare.locale.*; import io.bitsquare.user.BlockChainExplorer; import io.bitsquare.user.Preferences; +import javafx.beans.property.SimpleStringProperty; +import javafx.beans.property.StringProperty; import javafx.beans.value.ChangeListener; -import javafx.scene.control.CheckBox; -import javafx.scene.control.ComboBox; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.geometry.Insets; +import javafx.geometry.VPos; +import javafx.scene.control.*; +import javafx.scene.image.ImageView; +import javafx.scene.layout.AnchorPane; import javafx.scene.layout.GridPane; +import javafx.util.Callback; import javafx.util.StringConverter; import javax.inject.Inject; +import java.util.Locale; import static io.bitsquare.gui.util.FormBuilder.*; @FxmlView -public class PreferencesView extends ActivatableViewAndModel { +public class PreferencesView extends ActivatableViewAndModel { // not supported yet //private ComboBox btcDenominationComboBox; private ComboBox blockChainExplorerComboBox; - private ComboBox languageComboBox; + private ComboBox userLanguageComboBox; private ComboBox preferredTradeCurrencyComboBox; private CheckBox useAnimationsCheckBox, useEffectsCheckBox, showNotificationsCheckBox, showInstructionsCheckBox, @@ -50,43 +64,76 @@ public class PreferencesView extends ActivatableViewAndModel transactionFeeFocusedListener; private final Preferences preferences; + private ListView fiatCurrenciesListView; + private ComboBox fiatCurrenciesComboBox; + private ListView cryptoCurrenciesListView; + private ComboBox cryptoCurrenciesComboBox; + // private ListChangeListener displayCurrenciesListChangeListener; + final ObservableList btcDenominations = FXCollections.observableArrayList(Preferences.getBtcDenominations()); + final ObservableList blockExplorers; + final ObservableList languageCodes; + final StringProperty transactionFeePerByte = new SimpleStringProperty(); + public final ObservableList fiatCurrencies; + public final ObservableList allFiatCurrencies; + public final ObservableList cryptoCurrencies; + public final ObservableList allCryptoCurrencies; + public final ObservableList tradeCurrencies; + + + /////////////////////////////////////////////////////////////////////////////////////////// + // Constructor, initialisation + /////////////////////////////////////////////////////////////////////////////////////////// + @Inject - public PreferencesView(PreferencesViewModel model, Preferences preferences) { - super(model); + public PreferencesView(Preferences preferences) { + super(); this.preferences = preferences; + + blockExplorers = FXCollections.observableArrayList(preferences.getBlockChainExplorers()); + languageCodes = FXCollections.observableArrayList(LanguageUtil.getAllLanguageCodes()); + fiatCurrencies = preferences.getFiatCurrenciesAsObservable(); + cryptoCurrencies = preferences.getCryptoCurrenciesAsObservable(); + tradeCurrencies = preferences.getTradeCurrenciesAsObservable(); + + allFiatCurrencies = FXCollections.observableArrayList(CurrencyUtil.getAllSortedFiatCurrencies()); + allCryptoCurrencies = FXCollections.observableArrayList(CurrencyUtil.getAllSortedCryptoCurrencies()); + + allFiatCurrencies.removeAll(fiatCurrencies); + allCryptoCurrencies.removeAll(cryptoCurrencies); } @Override public void initialize() { - addTitledGroupBg(root, gridRow, 4, "Preferences"); - preferredTradeCurrencyComboBox = addLabelComboBox(root, gridRow, "Preferred currency:", Layout.FIRST_ROW_DISTANCE).second; - languageComboBox = addLabelComboBox(root, ++gridRow, "Language:").second; - // btcDenominationComboBox = addLabelComboBox(root, ++gridRow, "Bitcoin denomination:").second; - blockChainExplorerComboBox = addLabelComboBox(root, ++gridRow, "Bitcoin block explorer:").second; - autoSelectArbitratorsCheckBox = addLabelCheckBox(root, ++gridRow, "Auto select arbitrators by language:", "").second; - - // TODO need a bit extra work to separate trade and non trade tx fees before it can be used - /*transactionFeeInputTextField = addLabelInputTextField(root, ++gridRow, "Transaction fee (satoshi/byte):").second; - transactionFeeFocusedListener = (o, oldValue, newValue) -> { - model.onFocusOutTransactionFeeTextField(oldValue, newValue); - };*/ - - addTitledGroupBg(root, ++gridRow, 4, "Display options", Layout.GROUP_DISTANCE); - useAnimationsCheckBox = addLabelCheckBox(root, gridRow, "Use animations:", "", Layout.FIRST_ROW_AND_GROUP_DISTANCE).second; - useEffectsCheckBox = addLabelCheckBox(root, ++gridRow, "Use effects:", "").second; - showNotificationsCheckBox = addLabelCheckBox(root, ++gridRow, "Show notifications:", "").second; - showInstructionsCheckBox = addLabelCheckBox(root, ++gridRow, "Show instruction popups:", "").second; + initializeDisplayCurrencies(); + initializeOtherOptions(); + initializeDisplayOptions(); } + @Override protected void activate() { - /* btcDenominationComboBox.setDisable(true); - btcDenominationComboBox.setItems(model.btcDenominations); - btcDenominationComboBox.getSelectionModel().select(model.getBtcDenomination()); - btcDenominationComboBox.setOnAction(e -> model.onSelectBtcDenomination(btcDenominationComboBox.getSelectionModel().getSelectedItem()));*/ + activateDisplayCurrencies(); + activateOtherOptions(); + activateDisplayPreferences(); + } - preferredTradeCurrencyComboBox.setItems(model.tradeCurrencies); - preferredTradeCurrencyComboBox.getSelectionModel().select(preferences.getPreferredTradeCurrency()); + @Override + protected void deactivate() { + deactivateDisplayCurrencies(); + deactivateOtherOptions(); + deactivateDisplayPreferences(); + } + + + /////////////////////////////////////////////////////////////////////////////////////////// + // Initialize + /////////////////////////////////////////////////////////////////////////////////////////// + + private void initializeDisplayCurrencies() { + TitledGroupBg titledGroupBg = addTitledGroupBg(root, gridRow, 3, "Currencies"); + GridPane.setColumnSpan(titledGroupBg, 4); + + preferredTradeCurrencyComboBox = addLabelComboBox(root, gridRow, "Preferred currency:", Layout.FIRST_ROW_DISTANCE).second; preferredTradeCurrencyComboBox.setConverter(new StringConverter() { @Override public String toString(TradeCurrency tradeCurrency) { @@ -98,11 +145,224 @@ public class PreferencesView extends ActivatableViewAndModel preferences.setPreferredTradeCurrency(preferredTradeCurrencyComboBox.getSelectionModel().getSelectedItem())); - languageComboBox.setItems(model.languageCodes); - languageComboBox.getSelectionModel().select(model.getLanguageCode()); - languageComboBox.setConverter(new StringConverter() { + Tuple2 fiatTuple = addLabelListView(root, ++gridRow, "Display national currencies:"); + GridPane.setValignment(fiatTuple.first, VPos.TOP); + fiatCurrenciesListView = fiatTuple.second; + fiatCurrenciesListView.setMinHeight(2 * Layout.LIST_ROW_HEIGHT + 2); + fiatCurrenciesListView.setMaxHeight(6 * Layout.LIST_ROW_HEIGHT + 2); + Label placeholder = new Label("There are no national currencies selected"); + placeholder.setWrapText(true); + fiatCurrenciesListView.setPlaceholder(placeholder); + fiatCurrenciesListView.setCellFactory(new Callback, ListCell>() { + @Override + public ListCell call(ListView list) { + return new ListCell() { + final Label label = new Label(); + final ImageView icon = ImageUtil.getImageViewById(ImageUtil.REMOVE_ICON); + final Button removeButton = new Button("", icon); + final AnchorPane pane = new AnchorPane(label, removeButton); + + { + label.setLayoutY(5); + removeButton.setId("icon-button"); + AnchorPane.setRightAnchor(removeButton, 0d); + } + + @Override + public void updateItem(final FiatCurrency item, boolean empty) { + super.updateItem(item, empty); + if (item != null && !empty) { + label.setText(item.getNameAndCode()); + removeButton.setOnAction(e -> { + if (item.equals(preferences.getPreferredTradeCurrency())) { + new Popup().warning("You cannot remove your selected preferred display currency").show(); + } else { + preferences.removeFiatCurrency(item); + if (!allFiatCurrencies.contains(item)) + allFiatCurrencies.add(item); + } + }); + setGraphic(pane); + } else { + setGraphic(null); + removeButton.setOnAction(null); + } + } + }; + } + }); + + Tuple2 cryptoCurrenciesTuple = addLabelListView(root, gridRow, "Display crypto currencies:"); + GridPane.setValignment(cryptoCurrenciesTuple.first, VPos.TOP); + GridPane.setMargin(cryptoCurrenciesTuple.first, new Insets(0, 0, 0, 20)); + cryptoCurrenciesListView = cryptoCurrenciesTuple.second; + GridPane.setColumnIndex(cryptoCurrenciesTuple.first, 2); + GridPane.setColumnIndex(cryptoCurrenciesListView, 3); + cryptoCurrenciesListView.setMinHeight(2 * Layout.LIST_ROW_HEIGHT + 2); + cryptoCurrenciesListView.setMaxHeight(6 * Layout.LIST_ROW_HEIGHT + 2); + placeholder = new Label("There are no crypto currencies selected"); + placeholder.setWrapText(true); + cryptoCurrenciesListView.setPlaceholder(placeholder); + cryptoCurrenciesListView.setCellFactory(new Callback, ListCell>() { + @Override + public ListCell call(ListView list) { + return new ListCell() { + final Label label = new Label(); + final ImageView icon = ImageUtil.getImageViewById(ImageUtil.REMOVE_ICON); + final Button removeButton = new Button("", icon); + final AnchorPane pane = new AnchorPane(label, removeButton); + + { + label.setLayoutY(5); + removeButton.setId("icon-button"); + AnchorPane.setRightAnchor(removeButton, 0d); + } + + @Override + public void updateItem(final CryptoCurrency item, boolean empty) { + super.updateItem(item, empty); + if (item != null && !empty) { + label.setText(item.getNameAndCode()); + removeButton.setOnAction(e -> { + if (item.equals(preferences.getPreferredTradeCurrency())) { + new Popup().warning("You cannot remove your selected preferred display currency").show(); + } else { + preferences.removeCryptoCurrency(item); + if (!allCryptoCurrencies.contains(item)) + allCryptoCurrencies.add(item); + } + }); + setGraphic(pane); + } else { + setGraphic(null); + removeButton.setOnAction(null); + } + } + }; + } + }); + + fiatCurrenciesComboBox = addLabelComboBox(root, ++gridRow).second; + fiatCurrenciesComboBox.setPromptText("Add national currency"); + fiatCurrenciesComboBox.setConverter(new StringConverter() { + @Override + public String toString(FiatCurrency tradeCurrency) { + return tradeCurrency.getNameAndCode(); + } + + @Override + public FiatCurrency fromString(String s) { + return null; + } + }); + + Tuple2 labelComboBoxTuple2 = addLabelComboBox(root, gridRow); + cryptoCurrenciesComboBox = labelComboBoxTuple2.second; + GridPane.setColumnIndex(cryptoCurrenciesComboBox, 3); + cryptoCurrenciesComboBox.setPromptText("Add crypto currency"); + cryptoCurrenciesComboBox.setConverter(new StringConverter() { + @Override + public String toString(CryptoCurrency tradeCurrency) { + return tradeCurrency.getNameAndCode(); + } + + @Override + public CryptoCurrency fromString(String s) { + return null; + } + }); + } + + private void initializeOtherOptions() { + TitledGroupBg titledGroupBg = addTitledGroupBg(root, ++gridRow, 3, "Options", Layout.GROUP_DISTANCE); + GridPane.setColumnSpan(titledGroupBg, 4); + userLanguageComboBox = addLabelComboBox(root, gridRow, "Language:", Layout.FIRST_ROW_AND_GROUP_DISTANCE).second; + // btcDenominationComboBox = addLabelComboBox(root, ++gridRow, "Bitcoin denomination:").second; + blockChainExplorerComboBox = addLabelComboBox(root, ++gridRow, "Bitcoin block explorer:").second; + autoSelectArbitratorsCheckBox = addLabelCheckBox(root, ++gridRow, "Auto select arbitrators:", "").second; + + // TODO need a bit extra work to separate trade and non trade tx fees before it can be used + /*transactionFeeInputTextField = addLabelInputTextField(root, ++gridRow, "Transaction fee (satoshi/byte):").second; + transactionFeeFocusedListener = (o, oldValue, newValue) -> { + onFocusOutTransactionFeeTextField(oldValue, newValue); + };*/ + } + + private void initializeDisplayOptions() { + TitledGroupBg titledGroupBg = addTitledGroupBg(root, ++gridRow, 4, "Display options", Layout.GROUP_DISTANCE); + GridPane.setColumnSpan(titledGroupBg, 4); + useAnimationsCheckBox = addLabelCheckBox(root, gridRow, "Use animations:", "", Layout.FIRST_ROW_AND_GROUP_DISTANCE).second; + Tuple2 labelCheckBoxTuple2 = addLabelCheckBox(root, gridRow, "Show notifications:", "", Layout.FIRST_ROW_AND_GROUP_DISTANCE); + showNotificationsCheckBox = labelCheckBoxTuple2.second; + GridPane.setColumnIndex(labelCheckBoxTuple2.first, 2); + GridPane.setColumnIndex(showNotificationsCheckBox, 3); + + useEffectsCheckBox = addLabelCheckBox(root, ++gridRow, "Use effects:", "").second; + labelCheckBoxTuple2 = addLabelCheckBox(root, gridRow, "Show instruction popups:", ""); + showInstructionsCheckBox = labelCheckBoxTuple2.second; + GridPane.setColumnIndex(labelCheckBoxTuple2.first, 2); + GridPane.setColumnIndex(showInstructionsCheckBox, 3); + } + + /////////////////////////////////////////////////////////////////////////////////////////// + // Activate + /////////////////////////////////////////////////////////////////////////////////////////// + + + private void activateDisplayCurrencies() { + preferredTradeCurrencyComboBox.setItems(tradeCurrencies); + preferredTradeCurrencyComboBox.getSelectionModel().select(preferences.getPreferredTradeCurrency()); + preferredTradeCurrencyComboBox.setOnAction(e -> { + TradeCurrency selectedItem = preferredTradeCurrencyComboBox.getSelectionModel().getSelectedItem(); + if (selectedItem != null) + preferences.setPreferredTradeCurrency(selectedItem); + }); + + fiatCurrenciesComboBox.setItems(allFiatCurrencies); + fiatCurrenciesListView.setItems(fiatCurrencies); + fiatCurrenciesComboBox.setOnAction(e -> { + FiatCurrency selectedItem = fiatCurrenciesComboBox.getSelectionModel().getSelectedItem(); + log.error("setOnAction " + selectedItem); + if (selectedItem != null) { + preferences.addFiatCurrency(selectedItem); + if (allFiatCurrencies.contains(selectedItem)) { + UserThread.execute(() -> { + fiatCurrenciesComboBox.getSelectionModel().clearSelection(); + allFiatCurrencies.remove(selectedItem); + + }); + } + } + }); + cryptoCurrenciesComboBox.setItems(allCryptoCurrencies); + cryptoCurrenciesListView.setItems(cryptoCurrencies); + cryptoCurrenciesComboBox.setOnAction(e -> { + CryptoCurrency selectedItem = cryptoCurrenciesComboBox.getSelectionModel().getSelectedItem(); + if (selectedItem != null) { + preferences.addCryptoCurrency(selectedItem); + if (allCryptoCurrencies.contains(selectedItem)) { + UserThread.execute(() -> { + cryptoCurrenciesComboBox.getSelectionModel().clearSelection(); + allCryptoCurrencies.remove(selectedItem); + + }); + } + } + }); + } + + private void activateOtherOptions() { + transactionFeePerByte.set(String.valueOf(preferences.getTxFeePerKB() / 1000)); + + /* btcDenominationComboBox.setDisable(true); + btcDenominationComboBox.setItems(btcDenominations); + btcDenominationComboBox.getSelectionModel().select(getBtcDenomination()); + btcDenominationComboBox.setOnAction(e -> onSelectBtcDenomination(btcDenominationComboBox.getSelectionModel().getSelectedItem()));*/ + + userLanguageComboBox.setItems(languageCodes); + userLanguageComboBox.getSelectionModel().select(preferences.getPreferredLocale().getLanguage()); + userLanguageComboBox.setConverter(new StringConverter() { @Override public String toString(String code) { return LanguageUtil.getDisplayName(code); @@ -113,10 +373,13 @@ public class PreferencesView extends ActivatableViewAndModel model.onSelectLanguageCode(languageComboBox.getSelectionModel().getSelectedItem())); + userLanguageComboBox.setOnAction(e -> { + String code = userLanguageComboBox.getSelectionModel().getSelectedItem(); + preferences.setPreferredLocale(new Locale(code, preferences.getPreferredLocale().getCountry())); + }); - blockChainExplorerComboBox.setItems(model.blockExplorers); + blockChainExplorerComboBox.setItems(blockExplorers); blockChainExplorerComboBox.getSelectionModel().select(preferences.getBlockChainExplorer()); blockChainExplorerComboBox.setConverter(new StringConverter() { @Override @@ -131,9 +394,11 @@ public class PreferencesView extends ActivatableViewAndModel preferences.setBlockChainExplorer(blockChainExplorerComboBox.getSelectionModel().getSelectedItem())); - // transactionFeeInputTextField.textProperty().bindBidirectional(model.transactionFeePerByte); + // transactionFeeInputTextField.textProperty().bindBidirectional(transactionFeePerByte); // transactionFeeInputTextField.focusedProperty().addListener(transactionFeeFocusedListener); + } + private void activateDisplayPreferences() { useAnimationsCheckBox.setSelected(preferences.getUseAnimations()); useAnimationsCheckBox.setOnAction(e -> preferences.setUseAnimations(useAnimationsCheckBox.isSelected())); @@ -150,18 +415,30 @@ public class PreferencesView extends ActivatableViewAndModel preferences.setAutoSelectArbitrators(autoSelectArbitratorsCheckBox.isSelected())); } - @Override - protected void deactivate() { - //btcDenominationComboBox.setOnAction(null); - languageComboBox.setOnAction(null); + /////////////////////////////////////////////////////////////////////////////////////////// + // Deactivate + /////////////////////////////////////////////////////////////////////////////////////////// + + private void deactivateDisplayCurrencies() { preferredTradeCurrencyComboBox.setOnAction(null); + } + + private void deactivateOtherOptions() { + //btcDenominationComboBox.setOnAction(null); + userLanguageComboBox.setOnAction(null); blockChainExplorerComboBox.setOnAction(null); showNotificationsCheckBox.setOnAction(null); showInstructionsCheckBox.setOnAction(null); // transactionFeeInputTextField.textProperty().unbind(); /// transactionFeeInputTextField.focusedProperty().removeListener(transactionFeeFocusedListener); + } + + + private void deactivateDisplayPreferences() { useAnimationsCheckBox.setOnAction(null); useEffectsCheckBox.setOnAction(null); autoSelectArbitratorsCheckBox.setOnAction(null); } + + } diff --git a/gui/src/main/java/io/bitsquare/gui/main/settings/preferences/PreferencesViewModel.java b/gui/src/main/java/io/bitsquare/gui/main/settings/preferences/PreferencesViewModel.java deleted file mode 100644 index ac451a652e..0000000000 --- a/gui/src/main/java/io/bitsquare/gui/main/settings/preferences/PreferencesViewModel.java +++ /dev/null @@ -1,100 +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 . - */ - -package io.bitsquare.gui.main.settings.preferences; - -import com.google.inject.Inject; -import io.bitsquare.common.UserThread; -import io.bitsquare.gui.common.model.ActivatableViewModel; -import io.bitsquare.gui.main.popups.Popup; -import io.bitsquare.locale.LanguageUtil; -import io.bitsquare.locale.TradeCurrency; -import io.bitsquare.user.BlockChainExplorer; -import io.bitsquare.user.Preferences; -import javafx.beans.property.SimpleStringProperty; -import javafx.beans.property.StringProperty; -import javafx.collections.FXCollections; -import javafx.collections.ObservableList; - -import java.util.Locale; -import java.util.concurrent.TimeUnit; - -class PreferencesViewModel extends ActivatableViewModel { - - private final Preferences preferences; - final ObservableList btcDenominations = FXCollections.observableArrayList(Preferences.getBtcDenominations()); - final ObservableList blockExplorers; - final ObservableList tradeCurrencies; - final ObservableList languageCodes; - final StringProperty transactionFeePerByte = new SimpleStringProperty(); - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Constructor, initialisation - /////////////////////////////////////////////////////////////////////////////////////////// - - @Inject - public PreferencesViewModel(Preferences preferences) { - this.preferences = preferences; - - blockExplorers = FXCollections.observableArrayList(preferences.getBlockChainExplorers()); - tradeCurrencies = preferences.getTradeCurrenciesAsObservable(); - languageCodes = FXCollections.observableArrayList(LanguageUtil.getAllLanguageCodes()); - } - - @Override - protected void activate() { - transactionFeePerByte.set(String.valueOf(preferences.getTxFeePerKB() / 1000)); - } - - @Override - protected void deactivate() { - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // UI actions - /////////////////////////////////////////////////////////////////////////////////////////// - - public void onSelectLanguageCode(String code) { - preferences.setPreferredLocale(new Locale(code, preferences.getPreferredLocale().getCountry())); - } - - public void onFocusOutTransactionFeeTextField(Boolean oldValue, Boolean newValue) { - if (oldValue && !newValue) { - try { - preferences.setTxFeePerKB(Long.parseLong(transactionFeePerByte.get()) * 1000); - } catch (Exception e) { - log.warn("Error at onFocusOutTransactionFeeTextField: " + e.getMessage()); - new Popup().warning(e.getMessage()) - .onClose(() -> UserThread.runAfter( - () -> transactionFeePerByte.set(String.valueOf(preferences.getTxFeePerKB() / 1000)), - 100, TimeUnit.MILLISECONDS)) - .show(); - } - } - } - - /////////////////////////////////////////////////////////////////////////////////////////// - // Getters - /////////////////////////////////////////////////////////////////////////////////////////// - - public String getLanguageCode() { - return preferences.getPreferredLocale().getLanguage(); - } - -} diff --git a/gui/src/main/resources/i18n/displayStrings.properties b/gui/src/main/resources/i18n/displayStrings.properties index 250f7724c5..ea621b23c7 100644 --- a/gui/src/main/resources/i18n/displayStrings.properties +++ b/gui/src/main/resources/i18n/displayStrings.properties @@ -153,3 +153,16 @@ SWISH= Swish TRANSFER_WISE=TransferWise US_POSTAL_MONEY_ORDER=US Postal money order BLOCK_CHAINS=Crypto currencies + +OK_PAY_SHORT=OKPay +PERFECT_MONEY_SHORT=Perfect Money +ALI_PAY_SHORT=AliPay +SEPA_SHORT=SEPA +NATIONAL_BANK_SHORT=National Banks +SAME_BANK_SHORT=Same Bank +SPECIFIC_BANKS_SHORT=Specific banks +FED_WIRE_SHORT=Fed Wire +SWISH_SHORT= Swish +TRANSFER_WISE_SHORT=TransferWise +US_POSTAL_MONEY_ORDER_SHORT=Money order +BLOCK_CHAINS_SHORT=Crypto currencies diff --git a/network/src/main/java/io/bitsquare/p2p/storage/P2PDataStorage.java b/network/src/main/java/io/bitsquare/p2p/storage/P2PDataStorage.java index f7fa061604..1f8a675f17 100644 --- a/network/src/main/java/io/bitsquare/p2p/storage/P2PDataStorage.java +++ b/network/src/main/java/io/bitsquare/p2p/storage/P2PDataStorage.java @@ -190,9 +190,6 @@ public class P2PDataStorage implements MessageListener, ConnectionListener { if (result) { map.put(hashOfPayload, protectedStorageEntry); - sequenceNumberMap.put(hashOfPayload, new MapValue(protectedStorageEntry.sequenceNumber, System.currentTimeMillis())); - storage.queueUpForSave(sequenceNumberMap, 100); - StringBuilder sb = new StringBuilder("\n\n------------------------------------------------------------\n"); sb.append("Data set after doAdd (truncated)"); map.values().stream().forEach(e -> sb.append("\n").append(StringUtils.abbreviate(e.toString(), 100))); @@ -200,8 +197,13 @@ public class P2PDataStorage implements MessageListener, ConnectionListener { log.trace(sb.toString()); log.info("Data set after doAdd: size=" + map.values().size()); - broadcast(new AddDataMessage(protectedStorageEntry), sender, listener, isDataOwner); + if (hasSequenceNrIncreased(protectedStorageEntry.sequenceNumber, hashOfPayload)) { + sequenceNumberMap.put(hashOfPayload, new MapValue(protectedStorageEntry.sequenceNumber, System.currentTimeMillis())); + storage.queueUpForSave(sequenceNumberMap, 100); + broadcast(new AddDataMessage(protectedStorageEntry), sender, listener, isDataOwner); + } + hashMapChangedListeners.stream().forEach(e -> e.onAdded(protectedStorageEntry)); } else { log.trace("add failed"); @@ -226,7 +228,7 @@ public class P2PDataStorage implements MessageListener, ConnectionListener { } else { PublicKey ownerPubKey = storedData.getStoragePayload().getOwnerPubKey(); boolean result = checkSignature(ownerPubKey, hashOfDataAndSeqNr, signature) && - isSequenceNrValid(sequenceNumber, hashOfPayload) && + hasSequenceNrIncreased(sequenceNumber, hashOfPayload) && checkIfStoredDataPubKeyMatchesNewDataPubKey(ownerPubKey, hashOfPayload); if (result) { @@ -381,6 +383,22 @@ public class P2PDataStorage implements MessageListener, ConnectionListener { } private boolean isSequenceNrValid(int newSequenceNumber, ByteArray hashOfData) { + if (sequenceNumberMap.containsKey(hashOfData)) { + Integer storedSequenceNumber = sequenceNumberMap.get(hashOfData).sequenceNr; + if (newSequenceNumber >= storedSequenceNumber) { + return true; + } else { + log.info("Sequence number is invalid. sequenceNumber = " + + newSequenceNumber + " / storedSequenceNumber=" + storedSequenceNumber + "\n" + + "That can happen if the data owner gets an old delayed data storage message."); + return false; + } + } else { + return true; + } + } + + private boolean hasSequenceNrIncreased(int newSequenceNumber, ByteArray hashOfData) { if (sequenceNumberMap.containsKey(hashOfData)) { Integer storedSequenceNumber = sequenceNumberMap.get(hashOfData).sequenceNr; if (newSequenceNumber > storedSequenceNumber) {