Profile validations (#235)

This commit is contained in:
alwxndr 2016-09-23 16:22:35 +03:00 committed by Alexander Pantyuhov
parent 54633394ed
commit 80ec3eeef3
25 changed files with 260 additions and 195 deletions

View File

@ -27,7 +27,8 @@
"react-native-image-resizer",
"react-native-image-crop-picker",
"react-native-webview-bridge",
"react-native-drawer-layout"
"react-native-drawer-layout",
"homoglyph-finder"
],
"imageDirs": [
"images"

View File

@ -18,6 +18,7 @@
"dns.js": "^1.0.1",
"domain-browser": "^1.1.7",
"events": "^1.1.1",
"homoglyph-finder": "^1.1.1",
"https-browserify": "0.0.1",
"identicon.js": "github:status-im/identicon.js",
"os-browserify": "^0.1.2",

View File

@ -14,7 +14,8 @@
status-im.accounts.recover.handlers
[clojure.string :as str]
[status-im.utils.datetime :as time]
[status-im.utils.handlers :as u]))
[status-im.utils.handlers :as u]
[status-im.constants :refer [console-chat-id]]))
(defn save-account [_ [_ account]]
@ -117,7 +118,7 @@
:content (label :t/keypair-generated)}
:content-type content-type-command-request
:outgoing false
:from "console"
:from console-chat-id
:to "me"}])
db))

View File

@ -26,7 +26,8 @@
[status-im.utils.listview :as lw]
[status-im.accounts.views.account :refer [account-view]]
[status-im.i18n :refer [label]]
[status-im.accounts.styles :as st]))
[status-im.accounts.styles :as st]
[status-im.constants :refer [console-chat-id]]))
(defn toolbar-title []
(let [style (merge toolbar-title-text {:color color-white})]
@ -46,7 +47,7 @@
(dispatch-sync [:reset-app])
; add accounts screen to history ( maybe there is a better way ? )
(dispatch [:navigate-to-clean :accounts])
(dispatch [:navigate-to :chat "console"]))
(dispatch [:navigate-to :chat console-chat-id]))
(defview accounts []
[accounts [:get :accounts]

View File

@ -13,7 +13,9 @@
:bar-style "default"
:translucent? true
:color styles/color-transparent}}
:bottom-gradient {:height 3}})
:bottom-gradient {:height 3}
:input-label {:left 4}
:input-error-text {:margin-left 4}})
(def fonts
{:default {:font-family "sans-serif"}

View File

@ -25,6 +25,7 @@
[status-im.utils.types :refer [json->clj]]
[status-im.chat.handlers.commands :refer [command-prefix]]
[status-im.chat.utils :refer [console? not-console?]]
[status-im.constants :refer [console-chat-id]]
status-im.chat.handlers.animation
status-im.chat.handlers.requests
status-im.chat.handlers.unviewed-messages
@ -142,7 +143,7 @@
(str command-prefix text)
text)]
(dispatch [::stage-command-with-content command text']))
(dispatch [::check-suggestions "console" text])))))
(dispatch [::check-suggestions console-chat-id text])))))
(register-handler ::stage-command-with-content
(u/side-effect!
@ -172,9 +173,8 @@
(defn init-console-chat
[{:keys [chats] :as db} existing-account?]
(let [chat-id "console"
new-chat sign-up-service/console-chat]
(if (chats chat-id)
(let [new-chat sign-up-service/console-chat]
(if (chats console-chat-id)
db
(do
(chats/create-chat new-chat)
@ -183,9 +183,9 @@
(sign-up-service/start-signup))
(-> db
(assoc :new-chat new-chat)
(update :chats assoc chat-id new-chat)
(update :chats-ids conj chat-id)
(assoc :current-chat-id "console"))))))
(update :chats assoc console-chat-id new-chat)
(update :chats-ids conj console-chat-id)
(assoc :current-chat-id console-chat-id))))))
(register-handler :init-console-chat
(fn [db _]

View File

@ -1,10 +1,11 @@
(ns status-im.chat.handlers.wallet-chat
(:require [re-frame.core :refer [after enrich path dispatch]]
[status-im.utils.handlers :refer [register-handler] :as u]))
[status-im.utils.handlers :refer [register-handler] :as u]
[status-im.constants :refer [wallet-chat-id]]))
(register-handler :init-wallet-chat
(u/side-effect!
(fn []
(dispatch [:add-chat "wallet" {:name "Wallet"
(dispatch [:add-chat wallet-chat-id {:name "Wallet"
:dapp-url "http://127.0.0.1:3450"}]))))

View File

@ -26,6 +26,7 @@
[status-im.chat.views.bottom-info :refer [bottom-info-view]]
[status-im.i18n :refer [label label-pluralize]]
[status-im.components.animation :as anim]
[status-im.constants :refer [console-chat-id]]
[reagent.core :as r]
[clojure.string :as str]
[cljs-time.core :as t]))
@ -82,7 +83,7 @@
(<= last-online-date now-date))
(time/time-ago last-online-date)
(label :t/active-unknown)))
(if (= chat-id "console")
(if (= chat-id console-chat-id)
(label :t/active-online)
(label :t/active-unknown))))

View File

@ -6,7 +6,8 @@
[status-im.utils.sms-listener :refer [add-sms-listener
remove-sms-listener]]
[status-im.utils.phone-number :refer [format-phone-number]]
[status-im.constants :refer [text-content-type
[status-im.constants :refer [console-chat-id
text-content-type
content-type-command
content-type-command-request
content-type-status]]
@ -15,7 +16,7 @@
(defn send-console-message [text]
{:message-id (random/id)
:from "me"
:to "console"
:to console-chat-id
:content text
:content-type text-content-type
:outgoing true})
@ -36,7 +37,7 @@
(or message (label :t/confirmation-code)))
:content-type content-type-command-request
:outgoing false
:from "console"
:from console-chat-id
:to "me"}])))
(defn handle-sms [{body :body}]
@ -60,7 +61,7 @@
:content (label :t/contacts-syncronized)
:content-type text-content-type
:outgoing false
:from "console"
:from console-chat-id
:to "me"}])
(dispatch [:set-signed-up true]))
@ -74,7 +75,7 @@
:content (:message body)
:content-type text-content-type
:outgoing false
:from "console"
:from console-chat-id
:to "me"}])
(let [status (keyword (:status body))]
(when (= :confirmed status)
@ -95,7 +96,7 @@
(label :t/phone-number-required))
:content-type content-type-command-request
:outgoing false
:from "console"
:from console-chat-id
:to "me"}])))
;; -- Saving password ----------------------------------------
@ -106,7 +107,7 @@
:content (label :t/password-saved)
:content-type text-content-type
:outgoing false
:from "console"
:from console-chat-id
:to "me"
:new? false}])
(dispatch [:received-message
@ -114,7 +115,7 @@
:content (label :t/generate-passphrase)
:content-type text-content-type
:outgoing false
:from "console"
:from console-chat-id
:to "me"
:new? false}])
(dispatch [:received-message
@ -122,7 +123,7 @@
:content (label :t/here-is-your-passphrase)
:content-type text-content-type
:outgoing false
:from "console"
:from console-chat-id
:to "me"
:new? false}])
(dispatch [:received-message
@ -130,7 +131,7 @@
:content mnemonic
:content-type text-content-type
:outgoing false
:from "console"
:from console-chat-id
:to "me"
:new? false}])
(dispatch [:received-message
@ -138,7 +139,7 @@
:content (label :t/written-down)
:content-type text-content-type
:outgoing false
:from "console"
:from console-chat-id
:to "me"
:new? false}])
;; TODO highlight '!phone'
@ -149,8 +150,8 @@
(def intro-status
{:message-id "intro-status"
:content (label :t/intro-status)
:from "console"
:chat-id "console"
:from console-chat-id
:chat-id console-chat-id
:content-type content-type-status
:outgoing false
:to "me"})
@ -162,7 +163,7 @@
:content (label :t/intro-message1)
:content-type text-content-type
:outgoing false
:from "console"
:from console-chat-id
:to "me"}])
(when-not logged-in?
(dispatch [:received-message
@ -170,7 +171,7 @@
:content (label :t/intro-message2)
:content-type text-content-type
:outgoing false
:from "console"
:from console-chat-id
:to "me"}])
(dispatch [:received-message
@ -180,12 +181,12 @@
(label :t/keypair-generated))
:content-type content-type-command-request
:outgoing false
:from "console"
:from console-chat-id
:to "me"}])))
(def console-chat
{:chat-id "console"
:name "console"
{:chat-id console-chat-id
:name console-chat-id
; todo remove/change dapp config fot console
:dapp-url "http://localhost:8185/resources"
:dapp-hash 858845357
@ -193,6 +194,6 @@
:group-chat false
:is-active true
:timestamp (.getTime (js/Date.))
:contacts [{:identity "console"
:contacts [{:identity console-chat-id
:text-color "#FFFFFF"
:background-color "#AB7967"}]})

View File

@ -1,7 +1,8 @@
(ns status-im.chat.utils)
(ns status-im.chat.utils
(:require [status-im.constants :refer [console-chat-id]]))
(defn console? [s]
(= "console" s))
(= console-chat-id s))
(def not-console?
(complement console?))

View File

@ -6,7 +6,8 @@
[status-im.utils.types :refer [json->clj]]
[status-im.commands.utils :refer [generate-hiccup reg-handler]]
[clojure.string :as s]
[status-im.components.react :as r]))
[status-im.components.react :as r]
[status-im.constants :refer [console-chat-id]]))
(defn init-render-command!
[_ [chat-id command message-id data]]
@ -37,7 +38,7 @@
parameters' (assoc parameters :command command')]
(if transaction-hash
(dispatch [:wait-for-transaction transaction-hash parameters'])
(let [events (if (= "console" chat-id)
(let [events (if (= console-chat-id chat-id)
(merge regular-events console-events)
regular-events)
parameters'' (if-let [handler (events (keyword event))]

View File

@ -7,7 +7,8 @@
[status-im.persistence.realm.core :as realm]
[status-im.components.status :as status]
[status-im.utils.types :refer [json->clj]]
[status-im.commands.utils :refer [reg-handler]]))
[status-im.commands.utils :refer [reg-handler]]
[status-im.constants :refer [console-chat-id wallet-chat-id]]))
(def commands-js "commands.js")
@ -25,10 +26,10 @@
(when true
;-let [url (get-in db [:chats identity :dapp-url])]
(cond
(= "console" identity)
(= console-chat-id identity)
(dispatch [::validate-hash identity (slurp "resources/console.js")])
(= "wallet" identity)
(= wallet-chat-id identity)
(dispatch [::validate-hash identity (slurp "resources/wallet.js")])
:else

View File

@ -8,6 +8,7 @@
[status-im.components.icons.custom-icons :refer [oct-icon]]
[status-im.components.chat-icon.styles :as st]
[status-im.components.styles :refer [default-chat-color]]
[status-im.constants :refer [console-chat-id]]
[clojure.string :as s]))
(defn default-chat-icon [name styles]
@ -33,7 +34,7 @@
(defview chat-icon-view [chat-id group-chat name online styles]
[photo-path [:chat-photo chat-id]]
[view (:container styles)
(if-not (or (s/blank? photo-path) (= chat-id "console"))
(if-not (or (s/blank? photo-path) (= chat-id console-chat-id))
[chat-icon photo-path styles]
[default-chat-icon name styles])
(when-not group-chat

View File

@ -15,6 +15,7 @@
(def color-light-gray "#EEF2F5")
(def text1-color color-black)
(def text1-disabled-color "#555555")
(def text2-color color-gray)
(def text3-color color-blue)
(def text4-color color-white)

View File

@ -1,4 +1,5 @@
(ns status-im.components.text-field.styles)
(ns status-im.components.text-field.styles
(:require [status-im.utils.platform :refer [platform-specific]]))
(def text-field-container
@ -15,12 +16,13 @@
:text-align-vertical :top})
(defn label [top font-size color]
(let [input-label-style (get-in platform-specific [:component-styles :input-label])]
(merge input-label-style
{:position :absolute
:top top
:left 0
:color color
:font-size font-size
:background-color :transparent})
:background-color :transparent})))
(def label-float
{})
@ -36,6 +38,8 @@
:width width})
(defn error-text [color]
(let [input-error-text-style (get-in platform-specific [:component-styles :input-error-text])]
(merge input-error-text-style
{:color color
:background-color :transparent
:font-size 12})
:font-size 12})))

View File

@ -126,13 +126,12 @@
(when onFocus (onFocus))))
(defn on-input-blur [{:keys [component value animation onBlur]}]
(do
(log/debug "Input blurred")
(r/set-state component {:has-focus false
:float-label? (if (s/blank? value) false true)})
(when (s/blank? value)
(field-animation animation))
(when onBlur (onBlur))))
(when onBlur (onBlur)))
(defn get-width [event]
(.-width (.-layout (.-nativeEvent event))))
@ -144,6 +143,7 @@
label-top
label-font-size
line-width
current-value
max-line-width] :as state} (r/state component)
{:keys [wrapper-style input-style line-color focus-line-color secure-text-entry
label-color error-color error label value on-focus on-blur
@ -167,7 +167,7 @@
:to-line-width max-line-width}
:onFocus on-focus})
:on-blur #(on-input-blur {:component component
:value value
:value (or current-value value)
:animation {:top label-top
:to-top (:label-bottom config)
:font-size label-font-size
@ -175,7 +175,9 @@
:line-width line-width
:to-line-width 0}
:onBlur on-blur})
:on-change-text #(on-change-text %)
:on-change-text (fn [text]
(r/set-state component {:current-value text})
(on-change-text text))
:on-change #(on-change %)
:default-value value}]
[view {:style (st/underline-container line-color)

View File

@ -19,3 +19,6 @@
(def default-number-of-messages 20)
(def default-number-of-discovery-search-results 20)
(def console-chat-id "console")
(def wallet-chat-id "wallet")

View File

@ -159,7 +159,7 @@
:left 0})
(def form-container
{:marginLeft 16
{:margin-left 16
:margin-top 16})
(def address-explication-container

View File

@ -1,7 +1,8 @@
(ns status-im.db
(:require [schema.core :as s :include-macros true]
[status-im.components.react :refer [animated]]
[status-im.components.animation :as anim]))
[status-im.components.animation :as anim]
[status-im.constants :refer [console-chat-id]]))
;; schema of app-db
(def schema {:greeting s/Str})
@ -31,7 +32,7 @@
:chats {}
:chat {:command nil
:last-message nil}
:current-chat-id "console"
:current-chat-id console-chat-id
:contacts-ids #{}
:selected-contacts #{}

View File

@ -22,7 +22,8 @@
status-im.accounts.handlers
status-im.protocol.handlers
status-im.transactions.handlers
[status-im.utils.types :as t]))
[status-im.utils.types :as t]
[status-im.constants :refer [console-chat-id]]))
;; -- Common --------------------------------------------------------------
@ -49,7 +50,7 @@
(fn [db _]
(assoc db
:chats {}
:current-chat-id "console")))
:current-chat-id console-chat-id)))
(register-handler :initialize-account
(u/side-effect!
@ -69,7 +70,7 @@
(dispatch [:initialize-db])
(dispatch [:load-accounts])
(dispatch [:init-console-chat])
(dispatch [:load-commands! "console"]))))
(dispatch [:load-commands! console-chat-id]))))
(register-handler :initialize-crypt
(u/side-effect!

View File

@ -18,7 +18,9 @@
:border-bottom-width 0.5}
:chat {:new-message {:border-top-color styles/color-gray3
:border-top-width 0.5}}
:bottom-gradient {:height 1}})
:bottom-gradient {:height 1}
:input-label {:left 0}
:input-error-text {:margin-left 0}})
(def fonts
{:default {:font-family "SFUIDisplay-Regular"}

View File

@ -1,7 +1,8 @@
(ns status-im.profile.screen
(:require-macros [status-im.utils.views :refer [defview]])
(:require [reagent.core :as r]
[re-frame.core :refer [subscribe dispatch]]
(:require [re-frame.core :refer [subscribe dispatch]]
[clojure.string :as str]
[cljs.spec :as s]
[status-im.components.react :refer [view
text
text-input
@ -12,24 +13,22 @@
touchable-opacity
show-image-picker]]
[status-im.components.icons.custom-icons :refer [oct-icon]]
[status-im.components.chat-icon.screen :refer [profile-icon
my-profile-icon]]
[status-im.components.chat-icon.screen :refer [my-profile-icon]]
[status-im.components.status-bar :refer [status-bar]]
[status-im.profile.styles :as st]
[status-im.utils.handlers :refer [get-hashtags]]
[status-im.profile.handlers :refer [message-user
update-profile]]
[status-im.components.text-field.view :refer [text-field]]
[status-im.components.qr-code :refer [qr-code]]
[status-im.utils.phone-number :refer [format-phone-number
valid-mobile-number?]]
[status-im.utils.fs :refer [read-file]]
[status-im.utils.types :refer [clj->json]]
[status-im.utils.handlers :refer [get-hashtags]]
[status-im.utils.phone-number :refer [format-phone-number]]
[status-im.utils.image-processing :refer [img->base64]]
[status-im.utils.platform :refer [platform-specific]]
[status-im.i18n :refer [label]]
[clojure.string :as str]))
[status-im.profile.handlers :refer [message-user
update-profile]]
[status-im.profile.validations :as v]
[status-im.profile.styles :as st]
[status-im.i18n :refer [label]]))
(defview toolbar [{:keys [account profile-edit-data edit?]}]
(defn toolbar [{:keys [account profile-edit-data edit?]}]
(let [profile-edit-data-valid? (s/valid? ::v/profile profile-edit-data)]
[view
[touchable-highlight {:style st/back-btn-touchable
:on-press (fn []
@ -43,16 +42,18 @@
[icon :back st/back-btn-icon]]]
[touchable-highlight {:style st/actions-btn-touchable
:on-press (fn []
(when edit?
(update-profile account profile-edit-data))
(dispatch [:set-in [:profile-edit :edit?] (not edit?)]))}
(if edit?
(when profile-edit-data-valid?
(update-profile account profile-edit-data)
(dispatch [:set-in [:profile-edit :edit?] false]))
(dispatch [:set-in [:profile-edit :edit?] true])))}
[view st/actions-btn-container
(if edit?
[oct-icon {:name :check
:style st/ok-btn-icon}]
[icon :dots st/edit-btn-icon])]]])
:style (st/ok-btn-icon profile-edit-data-valid?)}]
[icon :dots st/edit-btn-icon])]]]))
(defview status-image-view [{{address :address
(defn status-image-view [{{address :address
username :name} :account
photo-path :photo-path
status :status
@ -78,24 +79,8 @@
[text-input {:style st/status-input
:editable edit?
:placeholder (label :t/profile-no-status)
:on-change-text #(dispatch [:set-in [:profile-edit :status] %])}
status]])
(defview profile-property-view [{name :name
value :value
empty-value :empty-value
on-change-text :on-change-text
{edit-mode? :edit?} :profile-data}]
[view st/profile-property-view-container
[view st/profile-property-view-sub-container
[text {:style st/profile-property-view-label
:font :medium}
name]
[text-input {:style st/profile-property-view-value
:editable (and on-change-text edit-mode?)
:on-change-text on-change-text}
(or value (when-not edit-mode? empty-value))]]])
:on-change-text #(dispatch [:set-in [:profile-edit :status] %])
:value status}]])
(defview profile []
[{whisper-identity :whisper-identity
@ -136,18 +121,35 @@
[icon :more_vertical_blue st/more-btn-image]]]]]
[scroll-view st/profile-properties-container
[profile-property-view {:name (label :t/username)
:value (if (not= username address)
username)
:empty-value (label :t/not-specified)}]
[profile-property-view {:name (label :t/phone-number)
:value (if-not (or (not phone) (str/blank? phone))
(format-phone-number phone))
:empty-value (label :t/not-specified)}]
[profile-property-view {:name (label :t/email)
:value (if-not (or (not email) (str/blank? email))
email)
:empty-value (label :t/not-specified)}]
[text-field
{:editable false
:input-style st/profile-input-text
:wrapper-style st/profile-input-wrapper
:value (if (and (not= username address)
username
(not (str/blank? username)))
username
(label :t/not-specified))
:label (label :t/username)}]
[text-field
{:editable false
:input-style st/profile-input-text
:wrapper-style st/profile-input-wrapper
:value (if (and phone (not (str/blank? phone)))
(format-phone-number phone)
(label :t/not-specified))
:label (label :t/phone-number)}]
[text-field
{:editable false
:input-style st/profile-input-text
:wrapper-style st/profile-input-wrapper
:value (if (and email (not (str/blank? email)))
email
(label :t/not-specified))
:label (label :t/email)}]
[view st/report-user-container
[touchable-highlight {:on-press (fn []
;; TODO not implemented
@ -164,6 +166,8 @@
status :status
:as account} [:get-current-account]
{edit? :edit?
new-name :name
new-email :email
new-status :status
new-photo-path :photo-path
:as profile-edit-data} [:get :profile-edit]]
@ -179,23 +183,42 @@
:edit? edit?}]
[scroll-view st/profile-properties-container
[profile-property-view {:name (label :t/username)
[text-field
{:error (if-not (s/valid? ::v/name new-name)
(label :t/error-incorrect-name))
:error-color "#7099e6"
:editable edit?
:input-style (if edit?
st/profile-input-text
st/profile-input-text-non-editable)
:wrapper-style st/profile-input-wrapper
:value (if (not= username address)
username)
:empty-value (label :t/not-specified)
:on-change-text #(dispatch [:set-in [:profile-edit :name] %])
:profile-data profile-edit-data}]
[profile-property-view {:name (label :t/phone-number)
:value (if-not (or (not phone) (str/blank? phone))
:label (label :t/username)
:on-change-text #(dispatch [:set-in [:profile-edit :name] %])}]
[text-field
{:editable false
:input-style st/profile-input-text-non-editable
:wrapper-style st/profile-input-wrapper
:value (if (and phone (not (str/blank? phone)))
(format-phone-number phone))
:empty-value (label :t/not-specified)
:profile-data profile-edit-data}]
[profile-property-view {:name (label :t/email)
:value (if-not (or (not email) (str/blank? email))
:label (label :t/phone-number)}]
[text-field
{:error (if-not (s/valid? ::v/email new-email)
(label :t/error-incorrect-email))
:error-color "#7099e6"
:editable edit?
:input-style (if edit?
st/profile-input-text
st/profile-input-text-non-editable)
:wrapper-style st/profile-input-wrapper
:value (if (and email (not (str/blank? email)))
email)
:empty-value (label :t/not-specified)
:on-change-text #(dispatch [:set-in [:profile-edit :email] %])
:profile-data profile-edit-data}]
:label (label :t/email)
:on-change-text #(dispatch [:set-in [:profile-edit :email] %])}]
[view st/qr-code-container
;; TODO: this public key should be replaced by address
[qr-code {:value (str "ethereum:" public-key)

View File

@ -1,6 +1,7 @@
(ns status-im.profile.styles
(:require [status-im.components.styles :refer [color-light-blue-transparent
color-white
color-gray
color-black
color-blue
color-blue-transparent
@ -8,6 +9,7 @@
online-color
separator-color
text1-color
text1-disabled-color
text2-color]]))
(def profile
@ -42,9 +44,9 @@
{:width 4
:height 16})
(def ok-btn-icon
(defn ok-btn-icon [enabled?]
{:font-size 22
:color :black})
:color (if enabled? color-black color-gray)})
(def user-photo-container
{:margin-top 22})
@ -113,32 +115,21 @@
(def profile-properties-container
{:margin-top 20
:margin-left 16
:align-items :stretch
:flex-firection :column})
(def profile-property-view-container
{:padding-left 16})
(def profile-input-wrapper
{:margin-bottom 16})
(def profile-property-view-sub-container
{:border-bottom-width 1
:border-bottom-color separator-color
:padding-right 16})
(def profile-input-text
{:color text1-color})
(def profile-property-view-label
{:margin-top 18
:font-size 14
:color text2-color})
(def profile-property-view-value
{:margin-top 8
:margin-bottom 8
:padding 0
:height 40
:font-size 16
:color text1-color})
(def profile-input-text-non-editable
{:color text1-disabled-color})
(def report-user-container
{:margin-top 50
{:margin-top 32
:margin-bottom 43
:align-items :center})

View File

@ -0,0 +1,22 @@
(ns status-im.profile.validations
(:require [cljs.spec :as s]
[status-im.utils.phone-number :refer [valid-mobile-number?]]
[status-im.constants :refer [console-chat-id wallet-chat-id]]
[clojure.string :as str]))
(def homoglyph-finder (js/require "homoglyph-finder"))
(defn correct-name? [username]
(let [username (some-> username (str/trim))]
(and (not (.isMatches homoglyph-finder username console-chat-id))
(not (.isMatches homoglyph-finder username wallet-chat-id)))))
(defn correct-email? [email]
(let [pattern #"[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?"]
(or (str/blank? email)
(and (string? email) (re-matches pattern email)))))
(s/def ::name correct-name?)
(s/def ::email correct-email?)
(s/def ::profile (s/keys :req-un [::name ::email]))

View File

@ -56,6 +56,8 @@
:email "Email"
:profile-no-status "No status"
:add-to-contacts "Add to contacts"
:error-incorrect-name "Please, select another name"
:error-incorrect-email "Incorrect e-mail"
;;make_photo
:image-source-title "Profile image"