feat: Keypair name handling (#19423)
feat: Keypair name handling (#19423)
This commit is contained in:
parent
a03cc3c3ab
commit
e2b9e12038
|
@ -23,7 +23,8 @@
|
|||
:text-color colors/white ;; text color override
|
||||
:icon-color colors/white ;; icon color override
|
||||
:no-icon-color? false ;; disable tint color for icon"
|
||||
[{:keys [type size theme icon text-color icon-color no-icon-color? style accessibility-label]} message]
|
||||
[{:keys [type size theme icon text-color icon-color no-icon-color? style accessibility-label
|
||||
container-style]} message]
|
||||
(let [weight (if (= size :default) :regular :medium)
|
||||
icon-size (if (= size :default) 16 12)
|
||||
size (if (= size :default) :paragraph-2 :label)
|
||||
|
@ -32,7 +33,8 @@
|
|||
[rn/view
|
||||
{:style (merge {:flex-direction :row
|
||||
:align-items :center}
|
||||
style)}
|
||||
style
|
||||
container-style)}
|
||||
[quo.icons/icon icon
|
||||
{:color icon-color
|
||||
:no-color no-icon-color?
|
||||
|
|
|
@ -134,9 +134,9 @@
|
|||
:align-items :flex-end})
|
||||
|
||||
(defn counter-color
|
||||
[current-chars char-limit variant-colors]
|
||||
[{:keys [current-chars char-limit variant-colors theme]}]
|
||||
{:color (if (> current-chars char-limit)
|
||||
colors/danger-60
|
||||
(colors/resolve-color :danger theme)
|
||||
(:label variant-colors))})
|
||||
|
||||
(defn button
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
[react-native.platform :as platform]))
|
||||
|
||||
(defn- label-&-counter
|
||||
[{:keys [label current-chars char-limit variant-colors]}]
|
||||
[{:keys [label current-chars char-limit variant-colors theme]}]
|
||||
[rn/view
|
||||
{:accessibility-label :input-labels
|
||||
:style style/texts-container}
|
||||
|
@ -22,7 +22,10 @@
|
|||
(str current-chars "/"))]
|
||||
[rn/view {:style style/counter-container}
|
||||
[text/text
|
||||
{:style (style/counter-color current-chars char-limit variant-colors)
|
||||
{:style (style/counter-color {:current-chars current-chars
|
||||
:char-limit char-limit
|
||||
:variant-colors variant-colors
|
||||
:theme theme})
|
||||
:weight :regular
|
||||
:size :paragraph-2}
|
||||
count-text]])])
|
||||
|
@ -114,7 +117,8 @@
|
|||
{:variant-colors variant-colors
|
||||
:label label
|
||||
:current-chars char-count
|
||||
:char-limit char-limit}])
|
||||
:char-limit char-limit
|
||||
:theme theme}])
|
||||
[rn/view {:style (style/input-container colors-by-status small? disabled?)}
|
||||
(when-let [{:keys [icon-name]} left-icon]
|
||||
[left-accessory
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
[legacy.status-im.ethereum.ens :as ens]
|
||||
[native-module.core :as native-module]
|
||||
[re-frame.core :as re-frame]
|
||||
[status-im.common.validators :as validators]
|
||||
[status-im.common.validation.general :as validators]
|
||||
[status-im.constants :as constants]
|
||||
[status-im.contexts.chat.events :as chat.events]
|
||||
[taoensso.timbre :as log]
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
(ns status-im.common.validation.general
|
||||
(:require
|
||||
[status-im.constants :as constants]
|
||||
[utils.emojilib :as emoji]))
|
||||
|
||||
(defn valid-public-key?
|
||||
[s]
|
||||
(and (string? s)
|
||||
(not-empty s)
|
||||
(boolean (re-matches constants/regx-public-key s))))
|
||||
|
||||
(defn valid-compressed-key?
|
||||
[s]
|
||||
(and (string? s)
|
||||
(not-empty s)
|
||||
(boolean (re-matches constants/regx-compressed-key s))))
|
||||
|
||||
(defn has-emojis? [s] (boolean (re-find emoji/emoji-regex s)))
|
||||
|
||||
(def no-special-chars-regex #"^[a-zA-Z0-9\-_ ]+$")
|
||||
(defn has-special-characters?
|
||||
[s]
|
||||
(and (not (= s ""))
|
||||
(not (re-find no-special-chars-regex s))))
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
(ns status-im.common.validators-test
|
||||
(ns status-im.common.validation.general-test
|
||||
(:require
|
||||
[cljs.test :refer-macros [deftest testing is]]
|
||||
[status-im.common.validators :refer [valid-compressed-key?]]))
|
||||
[status-im.common.validation.general :refer [valid-compressed-key?]]))
|
||||
|
||||
(deftest test-valid-compressed-key
|
||||
(testing "valid"
|
||||
|
@ -32,3 +32,4 @@
|
|||
(testing "contains l"
|
||||
(is (not (valid-compressed-key?
|
||||
"zQ3shWj4WaBdf2zYKCkXe6PHxDxNTzZyid1i75879Ue9cX9gl")))))
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
(ns status-im.common.validation.profile
|
||||
(:require [clojure.string :as string]
|
||||
[status-im.common.validation.general :as validators]
|
||||
[status-im.constants :as constants]
|
||||
utils.emojilib
|
||||
[utils.i18n :as i18n]))
|
||||
|
@ -8,16 +9,10 @@
|
|||
;; https://github.com/status-im/status-desktop/blob/2ba96803168461088346bf5030df750cb226df4c/ui/imports/utils/Constants.qml#L468
|
||||
(def min-length 5)
|
||||
|
||||
(def status-regex #"^[a-zA-Z0-9\-_ ]+$")
|
||||
|
||||
(def common-names ["Ethereum" "Bitcoin"])
|
||||
|
||||
(defn has-emojis? [s] (boolean (re-find utils.emojilib/emoji-regex s)))
|
||||
|
||||
(defn has-common-names? [s] (pos? (count (filter #(string/includes? s %) common-names))))
|
||||
|
||||
(defn has-special-characters? [s] (not (re-find status-regex s)))
|
||||
|
||||
(defn name-too-short? [s] (< (count (string/trim (str s))) min-length))
|
||||
|
||||
(defn name-too-long? [s] (> (count (string/trim (str s))) constants/profile-name-max-length))
|
||||
|
@ -27,33 +22,37 @@
|
|||
(defn validation-name
|
||||
[s]
|
||||
(cond
|
||||
(string/blank? s) nil
|
||||
(string/ends-with? s "-eth") (i18n/label :t/ending-not-allowed {:ending "-eth"})
|
||||
(string/ends-with? s "_eth") (i18n/label :t/ending-not-allowed {:ending "_eth"})
|
||||
(string/ends-with? s ".eth") (i18n/label :t/ending-not-allowed {:ending ".eth"})
|
||||
(string/starts-with? s " ") (i18n/label :t/start-with-space)
|
||||
(string/ends-with? s " ") (i18n/label :t/ends-with-space)
|
||||
(has-common-names? s) (i18n/label :t/are-not-allowed {:check (i18n/label :t/common-names)})
|
||||
(has-emojis? s) (i18n/label :t/are-not-allowed {:check (i18n/label :t/emojis)})
|
||||
(has-special-characters? s) (i18n/label :t/are-not-allowed
|
||||
{:check (i18n/label :t/special-characters)})
|
||||
(name-too-short? s) (i18n/label :t/minimum-characters {:min-chars min-length})
|
||||
(name-too-long? s) (i18n/label :t/profile-name-is-too-long)))
|
||||
(string/blank? s) nil
|
||||
(string/ends-with? s "-eth") (i18n/label :t/ending-not-allowed {:ending "-eth"})
|
||||
(string/ends-with? s "_eth") (i18n/label :t/ending-not-allowed {:ending "_eth"})
|
||||
(string/ends-with? s ".eth") (i18n/label :t/ending-not-allowed {:ending ".eth"})
|
||||
(string/starts-with? s " ") (i18n/label :t/start-with-space)
|
||||
(string/ends-with? s " ") (i18n/label :t/ends-with-space)
|
||||
(has-common-names? s) (i18n/label :t/are-not-allowed
|
||||
{:check (i18n/label :t/common-names)})
|
||||
(validators/has-emojis? s) (i18n/label :t/are-not-allowed
|
||||
{:check (i18n/label :t/emojis)})
|
||||
(validators/has-special-characters? s) (i18n/label :t/are-not-allowed
|
||||
{:check (i18n/label :t/special-characters)})
|
||||
(name-too-short? s) (i18n/label :t/minimum-characters {:min-chars min-length})
|
||||
(name-too-long? s) (i18n/label :t/profile-name-is-too-long)))
|
||||
|
||||
(defn validation-bio
|
||||
[s]
|
||||
(cond
|
||||
(string/blank? s) nil
|
||||
(has-emojis? s) (i18n/label :t/are-not-allowed {:check (i18n/label :t/emojis)})
|
||||
(has-special-characters? s) (i18n/label :t/are-not-allowed
|
||||
{:check (i18n/label :t/special-characters)})
|
||||
(bio-too-long? s) (i18n/label :t/bio-is-too-long)))
|
||||
(string/blank? s) nil
|
||||
(validators/has-emojis? s) (i18n/label :t/are-not-allowed
|
||||
{:check (i18n/label :t/emojis)})
|
||||
(validators/has-special-characters? s) (i18n/label :t/are-not-allowed
|
||||
{:check (i18n/label :t/special-characters)})
|
||||
(bio-too-long? s) (i18n/label :t/bio-is-too-long)))
|
||||
|
||||
(defn validation-nickname
|
||||
[s]
|
||||
(cond
|
||||
(string/blank? s) nil
|
||||
(has-emojis? s) (i18n/label :t/are-not-allowed {:check (i18n/label :t/emojis)})
|
||||
(has-special-characters? s) (i18n/label :t/are-not-allowed
|
||||
{:check (i18n/label :t/special-characters)})
|
||||
(name-too-long? s) (i18n/label :t/nickname-is-too-long)))
|
||||
(string/blank? s) nil
|
||||
(validators/has-emojis? s) (i18n/label :t/are-not-allowed
|
||||
{:check (i18n/label :t/emojis)})
|
||||
(validators/has-special-characters? s) (i18n/label :t/are-not-allowed
|
||||
{:check (i18n/label :t/special-characters)})
|
||||
(name-too-long? s) (i18n/label :t/nickname-is-too-long)))
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
(ns status-im.common.validation.profile-test
|
||||
(:require
|
||||
[cljs.test :refer-macros [deftest are]]
|
||||
[status-im.common.validation.general :as validator]
|
||||
[status-im.common.validation.profile :as profile-validator]
|
||||
[utils.i18n :as i18n]))
|
||||
|
||||
(deftest has-emojis-test
|
||||
(are [arg expected]
|
||||
(expected (profile-validator/has-emojis? arg))
|
||||
(expected (validator/has-emojis? arg))
|
||||
"Hello 😊" true?
|
||||
"Hello" false?))
|
||||
|
||||
|
@ -18,7 +19,7 @@
|
|||
|
||||
(deftest has-special-characters-test
|
||||
(are [arg expected]
|
||||
(expected (profile-validator/has-special-characters? arg))
|
||||
(expected (validator/has-special-characters? arg))
|
||||
"@name" true?
|
||||
"name" false?))
|
||||
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
(ns status-im.common.validators
|
||||
(:require
|
||||
[status-im.constants :as constants]))
|
||||
|
||||
(defn valid-public-key?
|
||||
[s]
|
||||
(and (string? s)
|
||||
(not-empty s)
|
||||
(boolean (re-matches constants/regx-public-key s))))
|
||||
|
||||
(defn valid-compressed-key?
|
||||
[s]
|
||||
(and (string? s)
|
||||
(not-empty s)
|
||||
(boolean (re-matches constants/regx-compressed-key s))))
|
|
@ -2,7 +2,7 @@
|
|||
(:require
|
||||
[clojure.string :as string]
|
||||
[re-frame.core :as re-frame]
|
||||
[status-im.common.validators :as validators]
|
||||
[status-im.common.validation.general :as validators]
|
||||
[status-im.contexts.chat.contacts.events :as data-store.contacts]
|
||||
status-im.contexts.chat.home.add-new-contact.effects
|
||||
[utils.ens.stateofus :as stateofus]
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
[react-native.hooks :as hooks]
|
||||
[status-im.common.router :as router]
|
||||
[status-im.common.scan-qr-code.view :as scan-qr-code]
|
||||
[status-im.common.validators :as validators]
|
||||
[status-im.common.validation.general :as validators]
|
||||
[status-im.contexts.communities.events]
|
||||
[status-im.contexts.wallet.common.validation :as wallet-validation]
|
||||
[utils.debounce :as debounce]
|
||||
|
|
|
@ -143,4 +143,3 @@
|
|||
:delete-key? true}])]))))
|
||||
|
||||
(def view (quo.theme/with-theme view-internal))
|
||||
|
||||
|
|
|
@ -7,3 +7,7 @@
|
|||
|
||||
(def bottom-action
|
||||
{:margin-horizontal -20})
|
||||
|
||||
(def error-container
|
||||
{:margin-left 20
|
||||
:margin-vertical 8})
|
||||
|
|
|
@ -2,44 +2,72 @@
|
|||
(:require
|
||||
[quo.core :as quo]
|
||||
[react-native.core :as rn]
|
||||
[reagent.core :as reagent]
|
||||
[status-im.common.floating-button-page.view :as floating-button-page]
|
||||
[status-im.common.validation.general :as validators]
|
||||
[status-im.contexts.wallet.create-account.new-keypair.keypair-name.style :as style]
|
||||
[utils.i18n :as i18n]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(def keypair-name-max-length 15)
|
||||
(def keypair-name-min-length 5)
|
||||
|
||||
(def error-messages
|
||||
{:length (i18n/label :t/key-name-error-length)
|
||||
:emoji (i18n/label :t/key-name-error-emoji)
|
||||
:special-char (i18n/label :t/key-name-error-special-char)})
|
||||
|
||||
(defn navigate-back [] (rf/dispatch [:navigate-back]))
|
||||
|
||||
(defn view
|
||||
[]
|
||||
(let [keypair-name (reagent/atom "")]
|
||||
(fn []
|
||||
(let [customization-color (rf/sub [:profile/customization-color])]
|
||||
[rn/view {:style {:flex 1}}
|
||||
[floating-button-page/view
|
||||
{:header [quo/page-nav
|
||||
{:icon-name :i/arrow-left
|
||||
:on-press #(rf/dispatch [:navigate-back])
|
||||
:accessibility-label :top-bar}]
|
||||
:footer [quo/bottom-actions
|
||||
{:actions :one-action
|
||||
:button-one-label (i18n/label :t/continue)
|
||||
:button-one-props {:disabled? (or (zero? (count @keypair-name))
|
||||
(> (count @keypair-name)
|
||||
keypair-name-max-length))
|
||||
:customization-color customization-color
|
||||
:on-press #(rf/dispatch [:wallet/new-keypair-continue
|
||||
{:keypair-name
|
||||
@keypair-name}])}
|
||||
:container-style style/bottom-action}]}
|
||||
[quo/text-combinations
|
||||
{:container-style style/header-container
|
||||
:title (i18n/label :t/keypair-name)
|
||||
:description (i18n/label :t/keypair-name-description)}]
|
||||
[quo/input
|
||||
{:container-style {:margin-horizontal 20}
|
||||
:placeholder (i18n/label :t/keypair-name-input-placeholder)
|
||||
:label (i18n/label :t/keypair-name)
|
||||
:char-limit keypair-name-max-length
|
||||
:auto-focus true
|
||||
:on-change-text #(reset! keypair-name %)}]]]))))
|
||||
(let [[keypair-name set-keypair-name] (rn/use-state "")
|
||||
customization-color (rf/sub [:profile/customization-color])
|
||||
[error set-error] (rn/use-state false)
|
||||
on-change-text (rn/use-callback (fn [value]
|
||||
(set-keypair-name value)
|
||||
(cond
|
||||
(> (count value) keypair-name-max-length)
|
||||
(set-error :length)
|
||||
(validators/has-emojis? value) (set-error
|
||||
:emoji)
|
||||
(validators/has-special-characters? value)
|
||||
(set-error :special-char)
|
||||
:else (set-error nil))))
|
||||
on-continue (rn/use-callback #(rf/dispatch [:wallet/new-keypair-continue
|
||||
{:keypair-name
|
||||
keypair-name}])
|
||||
[keypair-name])]
|
||||
[rn/view {:style {:flex 1}}
|
||||
[floating-button-page/view
|
||||
{:header [quo/page-nav
|
||||
{:icon-name :i/arrow-left
|
||||
:on-press navigate-back
|
||||
:accessibility-label :top-bar}]
|
||||
:footer [quo/bottom-actions
|
||||
{:actions :one-action
|
||||
:button-one-label (i18n/label :t/continue)
|
||||
:button-one-props {:disabled? (or (pos? error)
|
||||
(<= (count keypair-name)
|
||||
keypair-name-min-length))
|
||||
:customization-color customization-color
|
||||
:on-press on-continue}
|
||||
:container-style style/bottom-action}]}
|
||||
[quo/text-combinations
|
||||
{:container-style style/header-container
|
||||
:title (i18n/label :t/keypair-name)
|
||||
:description (i18n/label :t/keypair-name-description)}]
|
||||
[quo/input
|
||||
{:container-style {:margin-horizontal 20}
|
||||
:placeholder (i18n/label :t/keypair-name-input-placeholder)
|
||||
:label (i18n/label :t/keypair-name)
|
||||
:char-limit keypair-name-max-length
|
||||
:auto-focus true
|
||||
:on-change-text on-change-text
|
||||
:error error}]
|
||||
(when error
|
||||
[quo/info-message
|
||||
{:type :error
|
||||
:size :default
|
||||
:icon :i/info
|
||||
:container-style style/error-container}
|
||||
(get error-messages error)])]]))
|
||||
|
|
|
@ -2562,5 +2562,8 @@
|
|||
"one": "1 address",
|
||||
"other": "{{count}} addresses"
|
||||
},
|
||||
"max": "Max: {{number}}"
|
||||
"max": "Max: {{number}}",
|
||||
"key-name-error-length": "Key name too long",
|
||||
"key-name-error-emoji": "Emojis are not allowed",
|
||||
"key-name-error-special-char": "Special characters are not allowed"
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue