From de08c041589ebf5680fca9b215a4309c17baf1cc Mon Sep 17 00:00:00 2001
From: Alex Liebowitz <alex@alexliebowitz.com>
Date: Fri, 1 Sep 2017 02:14:38 -0400
Subject: [PATCH] Add regexp option to FormField

Still needs logic to notify the form when there's invalid input

Add address validation to Send page

Trim address input on Send page

Adds trim prop to FormField

Improve LBRY address regexp

On Send page, don't prevent form submit, and only show error on blur

This isn't a full fix (it only handles blur, it's still the form's
job to do validation on submit). A proper solution to this (that can
generalize to other forms) will probably involve looking at all of the
inputs and asking each one whether it has an error, which will
require some tricky state management.

On Send page, use error message from daemon

Update CHANGELOG.md

Further improve invalid address regexp

 - Remove incorrect check for second character
 - Add "O" to chars excluded from b58 section
 - Allow for 33-character addresses

Don't internationalize daemon error message on Send page

remove console, add i18n
---
 CHANGELOG.md                        |  2 +-
 ui/js/component/formField/view.jsx  | 33 ++++++++++++++++++++++++++++-
 ui/js/component/walletSend/index.js |  2 ++
 ui/js/component/walletSend/view.jsx |  6 +++++-
 ui/js/lbryuri.js                    |  1 +
 ui/js/selectors/wallet.js           |  5 +++++
 6 files changed, 46 insertions(+), 3 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2a94afa13..1fa1b399b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -19,7 +19,7 @@ Web UI version numbers should always match the corresponding version of LBRY App
 
 ### Fixed
   * Some CSS changes to prevent the card row from clipping the scroll arrows after the window width is reduced below a certain point
-  *
+  * Clearly notify user when they try to send credits to an invalid address
 
 ### Deprecated
   *
diff --git a/ui/js/component/formField/view.jsx b/ui/js/component/formField/view.jsx
index 2473eaf8d..224b607ec 100644
--- a/ui/js/component/formField/view.jsx
+++ b/ui/js/component/formField/view.jsx
@@ -12,6 +12,15 @@ class FormField extends React.PureComponent {
     prefix: React.PropTypes.string,
     postfix: React.PropTypes.string,
     hasError: React.PropTypes.bool,
+    trim: React.PropTypes.bool,
+    regexp: React.PropTypes.oneOfType([
+      React.PropTypes.instanceOf(RegExp),
+      React.PropTypes.string,
+    ]),
+  };
+
+  static defaultProps = {
+    trim: false,
   };
 
   constructor(props) {
@@ -77,6 +86,13 @@ class FormField extends React.PureComponent {
     });
   }
 
+  clearError() {
+    this.setState({
+      isError: false,
+      errorMessage: "",
+    });
+  }
+
   focus() {
     this.refs.field.focus();
   }
@@ -87,7 +103,9 @@ class FormField extends React.PureComponent {
     } else if (this.props.type == "SimpleMDE") {
       return this.refs.field.simplemde.value();
     } else {
-      return this.refs.field.value;
+      return this.props.trim
+        ? this.refs.field.value.trim()
+        : this.refs.field.value;
     }
   }
 
@@ -99,6 +117,16 @@ class FormField extends React.PureComponent {
     return this.refs.field.options;
   }
 
+  validate() {
+    if ("regexp" in this.props) {
+      if (!this.getValue().match(this.props.regexp)) {
+        this.showError(__("Invalid format."));
+      } else {
+        this.clearError();
+      }
+    }
+  }
+
   render() {
     // Pass all unhandled props to the field element
     const otherProps = Object.assign({}, this.props),
@@ -116,6 +144,8 @@ class FormField extends React.PureComponent {
     delete otherProps.postfix;
     delete otherProps.prefix;
     delete otherProps.dispatch;
+    delete otherProps.regexp;
+    delete otherProps.trim;
 
     const element = (
       <this._element
@@ -124,6 +154,7 @@ class FormField extends React.PureComponent {
         name={this.props.name}
         ref="field"
         placeholder={this.props.placeholder}
+        onBlur={() => this.validate()}
         className={
           "form-field__input form-field__input-" +
           this.props.type +
diff --git a/ui/js/component/walletSend/index.js b/ui/js/component/walletSend/index.js
index ab13440db..57e761c30 100644
--- a/ui/js/component/walletSend/index.js
+++ b/ui/js/component/walletSend/index.js
@@ -10,6 +10,7 @@ import { selectCurrentModal } from "selectors/app";
 import {
   selectDraftTransactionAmount,
   selectDraftTransactionAddress,
+  selectDraftTransactionError,
 } from "selectors/wallet";
 
 import WalletSend from "./view";
@@ -18,6 +19,7 @@ const select = state => ({
   modal: selectCurrentModal(state),
   address: selectDraftTransactionAddress(state),
   amount: selectDraftTransactionAmount(state),
+  error: selectDraftTransactionError(state),
 });
 
 const perform = dispatch => ({
diff --git a/ui/js/component/walletSend/view.jsx b/ui/js/component/walletSend/view.jsx
index 680106584..2751c42ab 100644
--- a/ui/js/component/walletSend/view.jsx
+++ b/ui/js/component/walletSend/view.jsx
@@ -2,6 +2,7 @@ import React from "react";
 import Link from "component/link";
 import Modal from "modal/modal";
 import { FormRow } from "component/form";
+import lbryuri from "lbryuri";
 
 const WalletSend = props => {
   const {
@@ -12,6 +13,7 @@ const WalletSend = props => {
     setAddress,
     amount,
     address,
+    error,
   } = props;
 
   return (
@@ -41,6 +43,8 @@ const WalletSend = props => {
             size="60"
             onChange={setAddress}
             value={address}
+            regexp={lbryuri.REGEXP_ADDRESS}
+            trim={true}
           />
           <div className="form-row-submit">
             <Link
@@ -77,7 +81,7 @@ const WalletSend = props => {
           contentLabel={__("Transaction failed")}
           onConfirmed={closeModal}
         >
-          {__("Something went wrong")}:
+          {error}
         </Modal>}
     </section>
   );
diff --git a/ui/js/lbryuri.js b/ui/js/lbryuri.js
index a7f43890a..922cbfd33 100644
--- a/ui/js/lbryuri.js
+++ b/ui/js/lbryuri.js
@@ -4,6 +4,7 @@ const CLAIM_ID_MAX_LEN = 40;
 const lbryuri = {};
 
 lbryuri.REGEXP_INVALID_URI = /[^A-Za-z0-9-]/g;
+lbryuri.REGEXP_ADDRESS = /^b(?=[^0OIl]{32,33})[0-9A-Za-z]{32,33}$/;
 
 /**
  * Parses a LBRY name into its component parts. Throws errors with user-friendly
diff --git a/ui/js/selectors/wallet.js b/ui/js/selectors/wallet.js
index bf8fcb61b..de7fd337b 100644
--- a/ui/js/selectors/wallet.js
+++ b/ui/js/selectors/wallet.js
@@ -82,6 +82,11 @@ export const selectDraftTransactionAddress = createSelector(
   draft => draft.address
 );
 
+export const selectDraftTransactionError = createSelector(
+  selectDraftTransaction,
+  draft => draft.error
+);
+
 export const selectBlocks = createSelector(_selectState, state => state.blocks);
 
 export const makeSelectBlockDate = block => {