From 28d13d8f5d505fafecb2e15f035f81403ceaaa1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Daubensch=C3=BCtz?= Date: Tue, 25 Jun 2019 12:04:00 +0200 Subject: [PATCH] Improve validation logic --- src/components/Cashout.js | 201 ++++++++++++++++++++++++++++++++------ 1 file changed, 171 insertions(+), 30 deletions(-) diff --git a/src/components/Cashout.js b/src/components/Cashout.js index eefc0ac0b..08630432b 100644 --- a/src/components/Cashout.js +++ b/src/components/Cashout.js @@ -10,12 +10,59 @@ const P = styled.p` color: gray; `; +const Error = styled.span` + padding: 6px 0 0 0; + color: red; + font-size: 0.7em; +`; + +// See: https://doc.bity.com/exchange/v2.html#place-an-order +const SUPPORTED_COUNTRIES = [ + "AT", + "BE", + "BG", + "HR", + "CY", + "CZ", + "DK", + "EE", + "FI", + "FR", + "DE", + "GR", + "HU", + "IS", + "IE", + "IT", + "LV", + "LI", + "LT", + "LU", + "MT", + "MC", + "NL", + "NO", + "PL", + "PT", + "RO", + "SM", + "ES", + "SK", + "SI", + "SE", + "CH", + "GB" +]; + +const MIN_AMOUNT_DOLLARS = 11; + +let prevFormattedIBAN; + class Cashout extends Component { constructor(props) { super(props); this.state = { - IBAN: "", fields: { name: { value: null, @@ -31,6 +78,85 @@ class Cashout extends Component { } } }; + + this.validate = this.validate.bind(this); + this.formatIBAN = this.formatIBAN.bind(this); + this.cashout = this.cashout.bind(this); + } + + cashout() { + const { IBAN } = this.validate("IBAN")(); + if (IBAN.valid) { + // submit the order + } + } + + customIsValid(IBAN) { + // https://en.wikipedia.org/wiki/International_Bank_Account_Number#IBAN_formats_by_country + const countryCode = IBAN.slice(0, 2); + const supportedCountry = SUPPORTED_COUNTRIES.includes(countryCode); + if (supportedCountry) { + if (isValid(IBAN)) { + return "valid"; + } else { + return "invalid"; + } + } else { + return "country"; + } + } + + setCaretPosition(ctrl, pos) { + // Modern browsers + if (ctrl.setSelectionRange) { + ctrl.focus(); + ctrl.setSelectionRange(pos, pos); + + // IE8 and below + } else if (ctrl.createTextRange) { + var range = ctrl.createTextRange(); + range.collapse(true); + range.moveEnd("character", pos); + range.moveStart("character", pos); + range.select(); + } + } + + formatIBAN(e) { + const cursorPosition = e.target.selectionStart; + const { fields } = this.state; + const IBAN = this.refs.IBAN.value; + const formattedIBAN = format(IBAN); + + const newFields = Object.assign(fields, { + IBAN: { + value: formattedIBAN, + valid: fields.IBAN.valid + } + }); + this.setState(newFields, () => { + // All IBANs are grouped in digits and letters of four + // e.g. AEkk bbbc cccc cccc cccc ccc + // NOTE: The author isn't very proud of the solution below :D + let adjustedPosition = cursorPosition; + if ( + prevFormattedIBAN && + prevFormattedIBAN.length > formattedIBAN.length && + cursorPosition % 5 === 0 && + cursorPosition !== 0 + ) { + adjustedPosition--; + } else if ( + prevFormattedIBAN && + prevFormattedIBAN.length < formattedIBAN.length && + cursorPosition % 5 === 0 && + cursorPosition !== 0 + ) { + adjustedPosition++; + } + this.setCaretPosition(this.refs.IBAN, adjustedPosition); + prevFormattedIBAN = formattedIBAN; + }); } validate(input) { @@ -42,22 +168,46 @@ class Cashout extends Component { newFields = Object.assign(fields, { name: { value: name, - valid: name !== "" + valid: name !== "", + message: name === "" ? "This field is required." : null } }); } else if (input === "IBAN") { const IBAN = this.refs.IBAN.value; + const validReason = this.customIsValid(IBAN); + let valid, message; + + if (validReason === "valid") { + valid = true; + } else if (validReason === "country") { + valid = false; + message = "This country is not yet supported by bity.com."; + } else { + valid = false; + message = "The IBAN you've entered is incorrect."; + } newFields = Object.assign(fields, { - IBAN: { value: format(IBAN), valid: isValid(IBAN) } + IBAN: { + value: format(IBAN), + valid, + message + } }); } else if (input === "amount") { const amount = this.refs.amount.value; newFields = Object.assign(fields, { - amount: { value: amount, valid: parseFloat(amount) >= 11 } + amount: { + value: amount, + valid: parseFloat(amount) >= MIN_AMOUNT_DOLLARS, + message: + parseFloat(amount) < MIN_AMOUNT_DOLLARS + ? "You can only cash out amounts greater than $X." + : null + } }); } this.setState(newFields); - console.log(this.state.fields); + return newFields; }; } @@ -67,14 +217,17 @@ class Cashout extends Component {

- Transfer ether directly to your bank account using Bity.com, the - secure swiss crypto gateway.{" "} + Transfer your ether directly to your bank account with{" "} + just one click using bity.com, the secure swiss crypto + gateway. No cumbersome "Know Your Customer" (KYC) is required. Just + filling out the three fields below. For more information, visit + bity.com by{" "} - For more information, visit bity.com by clicking here + clicking here .

@@ -84,11 +237,12 @@ class Cashout extends Component { type="text" ref="name" placeholder="e.g. Satoshi Nakamoto" - onChange={this.validate.bind(this)("name")} + onChange={this.validate("name")} borderColor={ fields.name.valid === null || fields.name.valid ? "grey" : "red" } /> + {fields.name.message ? {fields.name.message} : null} + {fields.IBAN.message ? {fields.IBAN.message} : null} + {fields.amount.message ? ( + {fields.amount.message} + ) : null}
-

- Currently supported countries: Austria, Belgium, Bulgaria, Croatia, - Cyprus, Czech Republic, Denmark, Estonia, Finland, France, Germany, - Greece, Hungary, Iceland, Ireland, Italy, Latvia, Liechtenstein, - Lithuania, Luxembourg, Malta, Monaco, Netherlands, Norway, Poland, - Portugal, Romania, San Marino, Spain, Slovakia, Slovenia, Sweden, - Switzerland, UK and Northern Ireland.{" "} - - For more information, click here - - . -

Cash out!