From f67c252f0a1192f1d76176405002dd12a1236470 Mon Sep 17 00:00:00 2001 From: Adrian Tiberius Date: Wed, 13 Jul 2016 18:53:23 +0300 Subject: [PATCH] Added address validation Former-commit-id: 5392b8d9a230096d7dbe9ba838d95d9af36b3b7d --- src/status_im/components/text_field/view.cljs | 20 +++--- src/status_im/contacts/validations.cljs | 10 ++- src/status_im/contacts/views/new_contact.cljs | 71 +++++++++++++------ src/status_im/translations/en.cljs | 3 + 4 files changed, 73 insertions(+), 31 deletions(-) diff --git a/src/status_im/components/text_field/view.cljs b/src/status_im/components/text_field/view.cljs index 2b78927d0a..4949c00ef2 100644 --- a/src/status_im/components/text_field/view.cljs +++ b/src/status_im/components/text_field/view.cljs @@ -69,7 +69,7 @@ (:label-font-large config) (:label-font-small config))) :float-label? (if (s/blank? value) false true)}] - (log/debug "component-will-mount") + ;(log/debug "component-will-mount") (r/set-state component data))) ; Invoked once, only on the client (not on the server), immediately after the @@ -79,7 +79,8 @@ ; parent components. (defn component-did-mount [component] (let [props (r/props component)] - (log/debug "component-did-mount:"))) + ;(log/debug "component-did-mount:") + )) ; Invoked when a component is receiving new props. This method is not called for ; the initial render. Use this as an opportunity to react to a prop transition @@ -87,7 +88,8 @@ ; The old props can be accessed via this.props. Calling this.setState() within ; this function will not trigger an additional render. (defn component-will-receive-props [component new-props] - (log/debug "component-will-receive-props: new-props=" new-props)) + ;(log/debug "component-will-receive-props: new-props=" new-props) + ) ; Invoked before rendering when new props or state are being received. This method ; is not called for the initial render or when forceUpdate is used. Use this as @@ -97,20 +99,22 @@ ; until the next state change. In addition, componentWillUpdate and ; componentDidUpdate will not be called. (defn should-component-update [component next-props next-state] - (log/debug "should-component-update: " next-props next-state) + ;(log/debug "should-component-update: " next-props next-state) true) ; Invoked immediately before rendering when new props or state are being received. ; This method is not called for the initial render. Use this as an opportunity ; to perform preparation before an update occurs. (defn component-will-update [component next-props next-state] - (log/debug "component-will-update: " next-props next-state)) + ;(log/debug "component-will-update: " next-props next-state) + ) ; Invoked immediately after the component's updates are flushed to the DOM. ; This method is not called for the initial render. Use this as an opportunity ; to operate on the DOM when the component has been updated. (defn component-did-update [component prev-props prev-state] - (log/debug "component-did-update: " prev-props prev-state)) + ;(log/debug "component-did-update: " prev-props prev-state) + ) (defn on-focus [{:keys [component animation onFocus]}] (do @@ -147,7 +151,7 @@ focusLineColor (if error errorColor focusLineColor) labelColor (if (and error (not float-label?)) errorColor labelColor) label (if error (str label " *") label)] - (log/debug "reagent-render: " data) + ;(log/debug "reagent-render: " data) [view (merge st/text-field-container wrapperStyle) [animated-text {:style (st/label label-top label-font-size labelColor)} label] [text-input {:style (merge st/text-input inputStyle) @@ -187,5 +191,5 @@ :component-did-update component-did-update :display-name "text-field" :reagent-render reagent-render}] - (log/debug "Creating text-field component: " data) + ;(log/debug "Creating text-field component: " data) (r/create-class component-data))) \ No newline at end of file diff --git a/src/status_im/contacts/validations.cljs b/src/status_im/contacts/validations.cljs index 1e4d517682..f2aa252434 100644 --- a/src/status_im/contacts/validations.cljs +++ b/src/status_im/contacts/validations.cljs @@ -1,13 +1,21 @@ (ns status-im.contacts.validations (:require [cljs.spec :as s] + [cljsjs.web3] [status-im.persistence.realm :as realm])) +(defn is-address? [s] + (.isAddress js/Web3.prototype s)) + (defn unique-identity? [identity] (println identity) (not (realm/exists? :contacts :whisper-identity identity))) (defn valid-length? [identity] - (= 132 (count identity))) + (let [length (count identity)] + (or + (= 130 length) + (= 132 length) + (is-address? identity)))) (s/def ::identity-length valid-length?) (s/def ::unique-identity unique-identity?) diff --git a/src/status_im/contacts/views/new_contact.cljs b/src/status_im/contacts/views/new_contact.cljs index c0158168d1..d0673a22ea 100644 --- a/src/status_im/contacts/views/new_contact.cljs +++ b/src/status_im/contacts/views/new_contact.cljs @@ -11,6 +11,7 @@ [status-im.components.text-field.view :refer [text-field]] [status-im.utils.identicon :refer [identicon]] [status-im.components.toolbar :refer [toolbar]] + [status-im.utils.utils :refer [log on-error http-post toast]] [status-im.components.styles :refer [color-purple color-white icon-search @@ -43,12 +44,12 @@ :label (label :t/name) :onChangeText #(dispatch [:set-in [:new-contact :name] %])}]) -(defview contact-whisper-id-input [whisper-identity] +(defview contact-whisper-id-input [whisper-identity error] [] - (let [error (if (str/blank? whisper-identity) "" nil) + (let [error (if (str/blank? whisper-identity) "" error) error (if (s/valid? ::v/whisper-identity whisper-identity) error - "Please enter a valid address or scan a QR code")] + (label :t/enter-valid-address))] [view button-input-container [text-field {:error error @@ -56,26 +57,52 @@ :value whisper-identity :wrapperStyle (merge button-input) :label (label :t/address) - :onChangeText #(dispatch [:set-in [:new-contact :whisper-identity] %])}] + :onChangeText #(do + (dispatch [:set-in [:new-contact :whisper-identity] %]) + (dispatch [:set :new-contact-address-error nil]))}] [scan-button {:showLabel (zero? (count whisper-identity)) :handler #(dispatch [:scan-qr-code {:toolbar-title (label :t/new-contact)} :set-new-contact-from-qr])}]])) +(defn on-add-contact [whisper-identity new-contact] + (if (v/is-address? whisper-identity) + (http-post "get-contacts-by-address" {:addresses [whisper-identity]} + (fn [{:keys [contacts]}] + (if (> (count contacts) 0) + (let [contact (first contacts) + new-contact (merge + new-contact + {:address whisper-identity + :whisper-identity (:whisper-identity contact)})] + (dispatch [:add-new-contact new-contact])) + (dispatch [:set :new-contact-address-error (label :t/unknown-address)])))) + (dispatch [:add-new-contact new-contact]))) + +(defn toolbar-action [whisper-identity new-contact error] + (let [valid-contact? (and + (s/valid? ::v/contact new-contact) + (nil? error))] + {:image {:source {:uri (if valid-contact? + :icon_ok_blue + :icon_ok_disabled)} + :style icon-search} + :handler #(when valid-contact? + (let [contact (merge + {:photo-path (identicon whisper-identity)} + new-contact)] + (on-add-contact whisper-identity contact)))})) + (defview new-contact [] - [{:keys [name whisper-identity phone-number] :as new-contact} [:get :new-contact]] - (let [valid-contact? (s/valid? ::v/contact new-contact)] - [view st/contact-form-container - [toolbar {:background-color :white - :nav-action {:image {:source {:uri :icon_back} - :style icon-back} - :handler #(dispatch [:navigate-back])} - :custom-content toolbar-title - :action {:image {:source {:uri (if valid-contact? - :icon_ok_blue - :icon_ok_disabled)} - :style icon-search} - :handler #(when valid-contact? (dispatch [:add-new-contact (merge {:photo-path (identicon whisper-identity)} new-contact)]))}}] - [view st/form-container - [contact-name-input name] - [contact-whisper-id-input whisper-identity]] - [view st/address-explication-container - [text {:style st/address-explication} (label :t/address-explication)]]])) + [{:keys [name whisper-identity phone-number] :as new-contact} [:get :new-contact] + error [:get :new-contact-address-error]] + [view st/contact-form-container + [toolbar {:background-color :white + :nav-action {:image {:source {:uri :icon_back} + :style icon-back} + :handler #(dispatch [:navigate-back])} + :custom-content toolbar-title + :action (toolbar-action whisper-identity new-contact error)}] + [view st/form-container + [contact-name-input name] + [contact-whisper-id-input whisper-identity error]] + [view st/address-explication-container + [text {:style st/address-explication} (label :t/address-explication)]]]) diff --git a/src/status_im/translations/en.cljs b/src/status_im/translations/en.cljs index 1da6f4a519..c663c49399 100644 --- a/src/status_im/translations/en.cljs +++ b/src/status_im/translations/en.cljs @@ -125,6 +125,9 @@ :name "Name" :whisper-identity "Whisper Identity" :address-explication "Maybe here should be some text explaining what an address is and where to look for it" + :enter-valid-address "Please enter a valid address or scan a QR code" + :unknown-address "Unknown address" + ;login :recover-access "Recover access"