feat: Keypair name handling (#19423)

feat: Keypair name handling (#19423)
This commit is contained in:
Omar Basem 2024-04-02 15:44:48 +04:00 committed by GitHub
parent a03cc3c3ab
commit e2b9e12038
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 142 additions and 91 deletions

View File

@ -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?

View File

@ -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

View File

@ -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

View File

@ -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]

View File

@ -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))))

View File

@ -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")))))

View File

@ -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)))

View File

@ -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?))

View File

@ -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))))

View File

@ -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]

View File

@ -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]

View File

@ -143,4 +143,3 @@
:delete-key? true}])]))))
(def view (quo.theme/with-theme view-internal))

View File

@ -7,3 +7,7 @@
(def bottom-action
{:margin-horizontal -20})
(def error-container
{:margin-left 20
:margin-vertical 8})

View File

@ -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)])]]))

View File

@ -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"
}