From fb7239f786659100759e427af0224b33b40dcb1e Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Tue, 10 Sep 2019 12:05:23 -0300 Subject: [PATCH] Integrate PhoneNumberValidator into HalCashForm Added country selector to HalCashForm, requiring the following changes. 1. A new CountryUtil method getAllHalCashCountries, returns [ES, PL]. 2. HalCashValidator now extends PhoneNumberValidator. 3. HalCashAccountPayload now extends CountryBasedPaymentAccountPayload. 4. HalCashAccount now extends CountryBasedPaymentAccount. 5. Added two new fields to pb.proto. a. HalCashAccountPayload hal_cash_account_payload field to CountryBasedPaymentAccountPayload message. b. accepted_country_codes field to HalCashAccountPayload message. Note: pb.proto changes may break backward compatibility. 6. Reformatted all modified source files except pb.proto. This patch is part of solution to Issue #3042. --- common/src/main/proto/pb.proto | 2 + .../java/bisq/core/locale/CountryUtil.java | 8 ++ .../bisq/core/payment/HalCashAccount.java | 23 ++++- .../payload/HalCashAccountPayload.java | 58 ++++++----- .../paymentmethods/HalCashForm.java | 98 +++++++++++++++---- .../util/validation/HalCashValidator.java | 5 +- 6 files changed, 144 insertions(+), 50 deletions(-) diff --git a/common/src/main/proto/pb.proto b/common/src/main/proto/pb.proto index 60547d7f476..c061ea48733 100644 --- a/common/src/main/proto/pb.proto +++ b/common/src/main/proto/pb.proto @@ -836,6 +836,7 @@ message CountryBasedPaymentAccountPayload { WesternUnionAccountPayload western_union_account_payload = 5; SepaInstantAccountPayload sepa_instant_account_payload = 6; F2FAccountPayload f2f_account_payload = 7; + HalCashAccountPayload hal_cash_account_payload = 8; } } @@ -888,6 +889,7 @@ message MoneyGramAccountPayload { message HalCashAccountPayload { string mobile_nr = 1; + repeated string accepted_country_codes = 2; } message WesternUnionAccountPayload { diff --git a/core/src/main/java/bisq/core/locale/CountryUtil.java b/core/src/main/java/bisq/core/locale/CountryUtil.java index 44320b8f5c9..c82aefc2d6d 100644 --- a/core/src/main/java/bisq/core/locale/CountryUtil.java +++ b/core/src/main/java/bisq/core/locale/CountryUtil.java @@ -34,6 +34,14 @@ @Slf4j public class CountryUtil { + public static List getAllHalCashCountries() { + List list = new ArrayList<>(); + String[] codes = {"ES", "PL"}; + populateCountryListByCodes(list, codes); + list.sort((a, b) -> a.name.compareTo(b.name)); + return list; + } + public static List getAllSepaEuroCountries() { List list = new ArrayList<>(); String[] codes = {"AT", "BE", "CY", "DE", "EE", "FI", "FR", "GR", "IE", diff --git a/core/src/main/java/bisq/core/payment/HalCashAccount.java b/core/src/main/java/bisq/core/payment/HalCashAccount.java index dba8ee82f11..f42d8e37e57 100644 --- a/core/src/main/java/bisq/core/payment/HalCashAccount.java +++ b/core/src/main/java/bisq/core/payment/HalCashAccount.java @@ -17,15 +17,18 @@ package bisq.core.payment; +import bisq.core.locale.CountryUtil; import bisq.core.locale.FiatCurrency; import bisq.core.payment.payload.HalCashAccountPayload; import bisq.core.payment.payload.PaymentAccountPayload; import bisq.core.payment.payload.PaymentMethod; +import java.util.List; + import lombok.EqualsAndHashCode; @EqualsAndHashCode(callSuper = true) -public final class HalCashAccount extends PaymentAccount { +public final class HalCashAccount extends CountryBasedPaymentAccount { public HalCashAccount() { super(PaymentMethod.HAL_CASH); setSingleTradeCurrency(new FiatCurrency("EUR")); @@ -33,14 +36,26 @@ public HalCashAccount() { @Override protected PaymentAccountPayload createPayload() { - return new HalCashAccountPayload(paymentMethod.getId(), id); + return new HalCashAccountPayload(paymentMethod.getId(), id, CountryUtil.getAllHalCashCountries()); } - public void setMobileNr(String mobileNr) { - ((HalCashAccountPayload) paymentAccountPayload).setMobileNr(mobileNr); + public List getAcceptedCountryCodes() { + return ((HalCashAccountPayload) paymentAccountPayload).getAcceptedCountryCodes(); + } + + public void addAcceptedCountry(String countryCode) { + ((HalCashAccountPayload) paymentAccountPayload).addAcceptedCountry(countryCode); + } + + public void removeAcceptedCountry(String countryCode) { + ((HalCashAccountPayload) paymentAccountPayload).removeAcceptedCountry(countryCode); } public String getMobileNr() { return ((HalCashAccountPayload) paymentAccountPayload).getMobileNr(); } + + public void setMobileNr(String mobileNr) { + ((HalCashAccountPayload) paymentAccountPayload).setMobileNr(mobileNr); + } } diff --git a/core/src/main/java/bisq/core/payment/payload/HalCashAccountPayload.java b/core/src/main/java/bisq/core/payment/payload/HalCashAccountPayload.java index df349f9a276..fbdef5d8c6f 100644 --- a/core/src/main/java/bisq/core/payment/payload/HalCashAccountPayload.java +++ b/core/src/main/java/bisq/core/payment/payload/HalCashAccountPayload.java @@ -17,6 +17,8 @@ package bisq.core.payment.payload; +import bisq.core.locale.Country; +import bisq.core.locale.CountryUtil; import bisq.core.locale.Res; import com.google.protobuf.Message; @@ -25,8 +27,12 @@ import java.nio.charset.Charset; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; import lombok.EqualsAndHashCode; import lombok.Getter; @@ -41,11 +47,16 @@ @Setter @Getter @Slf4j -public final class HalCashAccountPayload extends PaymentAccountPayload { +public final class HalCashAccountPayload extends CountryBasedPaymentAccountPayload { + // Dont use a set here as we need a deterministic ordering, otherwise the contract hash does not match + private final List acceptedCountryCodes; private String mobileNr = ""; - public HalCashAccountPayload(String paymentMethod, String id) { + public HalCashAccountPayload(String paymentMethod, String id, List acceptedCountries) { super(paymentMethod, id); + Set acceptedCountryCodesAsSet = acceptedCountries.stream().map(e -> e.code).collect(Collectors.toSet()); + acceptedCountryCodes = new ArrayList<>(acceptedCountryCodesAsSet); + acceptedCountryCodes.sort(String::compareTo); } @@ -53,46 +64,49 @@ public HalCashAccountPayload(String paymentMethod, String id) { // PROTO BUFFER /////////////////////////////////////////////////////////////////////////////////////////// - private HalCashAccountPayload(String paymentMethod, String id, + private HalCashAccountPayload(String paymentMethod, + String id, + String countryCode, String mobileNr, + List acceptedCountryCodes, long maxTradePeriod, @Nullable Map excludeFromJsonDataMap) { - super(paymentMethod, - id, - maxTradePeriod, - excludeFromJsonDataMap); + super(paymentMethod, id, countryCode, maxTradePeriod, excludeFromJsonDataMap); this.mobileNr = mobileNr; - } - - @Override - public Message toProtoMessage() { - return getPaymentAccountPayloadBuilder() - .setHalCashAccountPayload(protobuf.HalCashAccountPayload.newBuilder() - .setMobileNr(mobileNr)) - .build(); + this.acceptedCountryCodes = acceptedCountryCodes; } public static HalCashAccountPayload fromProto(protobuf.PaymentAccountPayload proto) { - return new HalCashAccountPayload(proto.getPaymentMethodId(), - proto.getId(), - proto.getHalCashAccountPayload().getMobileNr(), - proto.getMaxTradePeriod(), - CollectionUtils.isEmpty(proto.getExcludeFromJsonDataMap()) ? null : new HashMap<>(proto.getExcludeFromJsonDataMap())); + protobuf.CountryBasedPaymentAccountPayload countryBasedPaymentAccountPayload = proto.getCountryBasedPaymentAccountPayload(); + protobuf.HalCashAccountPayload halCashAccountPayloadPB = countryBasedPaymentAccountPayload.getHalCashAccountPayload(); + return new HalCashAccountPayload(proto.getPaymentMethodId(), proto.getId(), countryBasedPaymentAccountPayload.getCountryCode(), proto.getHalCashAccountPayload().getMobileNr(), new ArrayList<>(halCashAccountPayloadPB.getAcceptedCountryCodesList()), proto.getMaxTradePeriod(), CollectionUtils.isEmpty(proto.getExcludeFromJsonDataMap()) ? null : new HashMap<>(proto.getExcludeFromJsonDataMap())); } + @Override + public Message toProtoMessage() { + return getPaymentAccountPayloadBuilder().setHalCashAccountPayload(protobuf.HalCashAccountPayload.newBuilder().setMobileNr(mobileNr).addAllAcceptedCountryCodes(acceptedCountryCodes)).build(); + } /////////////////////////////////////////////////////////////////////////////////////////// // API /////////////////////////////////////////////////////////////////////////////////////////// + public void addAcceptedCountry(String countryCode) { + if (!acceptedCountryCodes.contains(countryCode)) acceptedCountryCodes.add(countryCode); + } + + public void removeAcceptedCountry(String countryCode) { + if (acceptedCountryCodes.contains(countryCode)) acceptedCountryCodes.remove(countryCode); + } + @Override public String getPaymentDetails() { - return Res.get(paymentMethodId) + " - " + Res.getWithCol("payment.mobile") + " " + mobileNr; + return Res.get(paymentMethodId) + " - " + Res.getWithCol("payment.mobile") + " " + mobileNr + ", " + Res.getWithCol("payment.bank.country") + " " + getCountryCode(); } @Override public String getPaymentDetailsForTradePopup() { - return Res.getWithCol("payment.mobile") + " " + mobileNr; + return Res.getWithCol("payment.mobile") + " " + mobileNr + "\n" + Res.getWithCol("payment.bank.country") + " " + CountryUtil.getNameByCode(countryCode); } @Override diff --git a/desktop/src/main/java/bisq/desktop/components/paymentmethods/HalCashForm.java b/desktop/src/main/java/bisq/desktop/components/paymentmethods/HalCashForm.java index 2aaff157a57..3e44b231d36 100644 --- a/desktop/src/main/java/bisq/desktop/components/paymentmethods/HalCashForm.java +++ b/desktop/src/main/java/bisq/desktop/components/paymentmethods/HalCashForm.java @@ -23,8 +23,11 @@ import bisq.desktop.util.validation.HalCashValidator; import bisq.core.account.witness.AccountAgeWitnessService; +import bisq.core.locale.Country; +import bisq.core.locale.CountryUtil; import bisq.core.locale.Res; import bisq.core.locale.TradeCurrency; +import bisq.core.payment.CountryBasedPaymentAccount; import bisq.core.payment.HalCashAccount; import bisq.core.payment.PaymentAccount; import bisq.core.payment.payload.HalCashAccountPayload; @@ -32,37 +35,61 @@ import bisq.core.util.BSFormatter; import bisq.core.util.validation.InputValidator; +import com.jfoenix.controls.JFXComboBox; + +import javafx.scene.control.ComboBox; import javafx.scene.control.TextField; import javafx.scene.layout.GridPane; +import javafx.scene.layout.HBox; + +import javafx.collections.FXCollections; + +import javafx.util.StringConverter; import static bisq.desktop.util.FormBuilder.addCompactTopLabelTextField; +import static bisq.desktop.util.FormBuilder.addCompactTopLabelTextFieldWithCopyIcon; import static bisq.desktop.util.FormBuilder.addTopLabelTextField; +import static bisq.desktop.util.FormBuilder.addTopLabelWithVBox; public class HalCashForm extends PaymentMethodForm { private final HalCashAccount halCashAccount; private final HalCashValidator halCashValidator; private InputTextField mobileNrInputTextField; - public static int addFormForBuyer(GridPane gridPane, int gridRow, - PaymentAccountPayload paymentAccountPayload) { - addCompactTopLabelTextField(gridPane, ++gridRow, Res.get("payment.mobile"), - ((HalCashAccountPayload) paymentAccountPayload).getMobileNr()); - return gridRow; - } - - public HalCashForm(PaymentAccount paymentAccount, AccountAgeWitnessService accountAgeWitnessService, HalCashValidator halCashValidator, - InputValidator inputValidator, GridPane gridPane, int gridRow, BSFormatter formatter) { + public HalCashForm(PaymentAccount paymentAccount, + AccountAgeWitnessService accountAgeWitnessService, + HalCashValidator halCashValidator, + InputValidator inputValidator, + GridPane gridPane, + int gridRow, + BSFormatter formatter) { super(paymentAccount, accountAgeWitnessService, inputValidator, gridPane, gridRow, formatter); this.halCashAccount = (HalCashAccount) paymentAccount; this.halCashValidator = halCashValidator; } + public static int addFormForBuyer(GridPane gridPane, int gridRow, PaymentAccountPayload paymentAccountPayload) { + HalCashAccountPayload halCashAccountPayload = (HalCashAccountPayload) paymentAccountPayload; + addCompactTopLabelTextFieldWithCopyIcon(gridPane, gridRow, 1, Res.get("payment.bank.country"), CountryUtil.getNameAndCode(halCashAccountPayload.getCountryCode())); + addCompactTopLabelTextField(gridPane, ++gridRow, Res.get("payment.mobile"), ((HalCashAccountPayload) paymentAccountPayload).getMobileNr()); + return gridRow; + } + @Override public void addFormForAddAccount() { gridRowFrom = gridRow + 1; - mobileNrInputTextField = FormBuilder.addInputTextField(gridPane, ++gridRow, - Res.get("payment.mobile")); + ComboBox countryComboBox = addCountrySelection(); + setCountryComboBoxAction(countryComboBox, halCashAccount); + countryComboBox.setItems(FXCollections.observableArrayList(CountryUtil.getAllHalCashCountries())); + Country country = CountryUtil.getDefaultCountry(); + if (CountryUtil.getAllHalCashCountries().contains(country)) { + countryComboBox.getSelectionModel().select(country); + halCashAccount.setCountry(country); + updateFromInputs(); + } + + mobileNrInputTextField = FormBuilder.addInputTextField(gridPane, ++gridRow, Res.get("payment.mobile")); mobileNrInputTextField.setValidator(halCashValidator); mobileNrInputTextField.textProperty().addListener((ov, oldValue, newValue) -> { halCashAccount.setMobileNr(newValue); @@ -74,6 +101,7 @@ public void addFormForAddAccount() { addTopLabelTextField(gridPane, ++gridRow, Res.get("shared.currency"), nameAndCode); addLimitations(false); addAccountNameTextFieldWithAutoFillToggleButton(); + updateFromInputs(); } @Override @@ -84,12 +112,9 @@ protected void autoFillNameTextField() { @Override public void addFormForDisplayAccount() { gridRowFrom = gridRow; - addTopLabelTextField(gridPane, gridRow, Res.get("payment.account.name"), - halCashAccount.getAccountName(), Layout.FIRST_ROW_AND_GROUP_DISTANCE); - addCompactTopLabelTextField(gridPane, ++gridRow, Res.get("shared.paymentMethod"), - Res.get(halCashAccount.getPaymentMethod().getId())); - TextField field = addCompactTopLabelTextField(gridPane, ++gridRow, Res.get("payment.mobile"), - halCashAccount.getMobileNr()).second; + addTopLabelTextField(gridPane, gridRow, Res.get("payment.account.name"), halCashAccount.getAccountName(), Layout.FIRST_ROW_AND_GROUP_DISTANCE); + addCompactTopLabelTextField(gridPane, ++gridRow, Res.get("shared.paymentMethod"), Res.get(halCashAccount.getPaymentMethod().getId())); + TextField field = addCompactTopLabelTextField(gridPane, ++gridRow, Res.get("payment.mobile"), halCashAccount.getMobileNr()).second; field.setMouseTransparent(false); TradeCurrency singleTradeCurrency = halCashAccount.getSingleTradeCurrency(); String nameAndCode = singleTradeCurrency != null ? singleTradeCurrency.getNameAndCode() : "null"; @@ -99,8 +124,41 @@ public void addFormForDisplayAccount() { @Override public void updateAllInputsValid() { - allInputsValid.set(isAccountNameValid() - && halCashValidator.validate(halCashAccount.getMobileNr()).isValid - && halCashAccount.getTradeCurrencies().size() > 0); + if (halCashValidator.validate(halCashAccount.getMobileNr()).isValid) { + halCashAccount.setMobileNr(halCashValidator.getNormalizedPhoneNumber()); + } + allInputsValid.set(isAccountNameValid() && halCashValidator.validate(halCashAccount.getMobileNr()).isValid && halCashAccount.getTradeCurrencies().size() > 0); + } + + private void setCountryComboBoxAction(ComboBox countryComboBox, + CountryBasedPaymentAccount paymentAccount) { + countryComboBox.setOnAction(e -> { + Country selectedItem = countryComboBox.getSelectionModel().getSelectedItem(); + paymentAccount.setCountry(selectedItem); + halCashValidator.setIsoCountryCode(selectedItem.code); + updateFromInputs(); + }); + } + + private ComboBox addCountrySelection() { + HBox hBox = new HBox(); + hBox.setSpacing(10); + ComboBox countryComboBox = new JFXComboBox<>(); + hBox.getChildren().addAll(countryComboBox); + addTopLabelWithVBox(gridPane, ++gridRow, Res.get("payment.bank.country"), hBox, 0); + + countryComboBox.setPromptText(Res.get("payment.select.bank.country")); + countryComboBox.setConverter(new StringConverter<>() { + @Override + public String toString(Country country) { + return country.name + " (" + country.code + ")"; + } + + @Override + public Country fromString(String s) { + return null; + } + }); + return countryComboBox; } } diff --git a/desktop/src/main/java/bisq/desktop/util/validation/HalCashValidator.java b/desktop/src/main/java/bisq/desktop/util/validation/HalCashValidator.java index b90fdd20ee3..463094d0cb2 100644 --- a/desktop/src/main/java/bisq/desktop/util/validation/HalCashValidator.java +++ b/desktop/src/main/java/bisq/desktop/util/validation/HalCashValidator.java @@ -17,9 +17,7 @@ package bisq.desktop.util.validation; -import bisq.core.util.validation.InputValidator; - -public final class HalCashValidator extends InputValidator { +public final class HalCashValidator extends PhoneNumberValidator { /////////////////////////////////////////////////////////////////////////////////////////// // Public methods @@ -27,7 +25,6 @@ public final class HalCashValidator extends InputValidator { @Override public ValidationResult validate(String input) { - // TODO return super.validate(input); }