diff --git a/resources/images/icons2/12x12/seed-phrase@2x.png b/resources/images/icons2/12x12/seed-phrase@2x.png new file mode 100644 index 000000000000..250bc7ae6e94 Binary files /dev/null and b/resources/images/icons2/12x12/seed-phrase@2x.png differ diff --git a/resources/images/icons2/12x12/seed-phrase@3x.png b/resources/images/icons2/12x12/seed-phrase@3x.png new file mode 100644 index 000000000000..4fe09f129901 Binary files /dev/null and b/resources/images/icons2/12x12/seed-phrase@3x.png differ diff --git a/src/status_im/common/validation/keypair.cljs b/src/status_im/common/validation/keypair.cljs new file mode 100644 index 000000000000..69e1c92a33bc --- /dev/null +++ b/src/status_im/common/validation/keypair.cljs @@ -0,0 +1,18 @@ +(ns status-im.common.validation.keypair + (:require [clojure.string :as string] + [status-im.common.validation.general :as validators] + [status-im.constants :as constants] + utils.emojilib + [utils.i18n :as i18n])) + +(defn keypair-too-short? [s] (< (count (string/trim (str s))) constants/keypair-name-min-length)) +(defn keypair-too-long? [s] (> (count (string/trim (str s))) constants/keypair-name-max-length)) + +(defn validation-keypair-name + [s] + (cond + (string/blank? s) nil + (validators/has-emojis? s) (i18n/label :t/key-name-error-emoji) + (validators/has-special-characters? s) (i18n/label :t/key-name-error-special-char) + (keypair-too-short? s) (i18n/label :t/your-key-pair-name-is-too-short) + (keypair-too-long? s) (i18n/label :t/your-key-pair-name-is-too-long))) diff --git a/src/status_im/common/validation/keypair_test.cljs b/src/status_im/common/validation/keypair_test.cljs new file mode 100644 index 000000000000..08d83c0a65b6 --- /dev/null +++ b/src/status_im/common/validation/keypair_test.cljs @@ -0,0 +1,27 @@ +(ns status-im.common.validation.keypair-test + (:require + [cljs.test :refer-macros [deftest are]] + [status-im.common.validation.keypair :as keypair-validator] + [utils.i18n :as i18n])) + +(deftest keypair-name-too-short-test + (are [arg expected] + (expected (keypair-validator/keypair-too-short? arg)) + "abc" true? + "abcdef" false?)) + +(deftest keypair-name-too-long-test + (are [arg expected] + (expected (keypair-validator/keypair-too-long? arg)) + (apply str (repeat 25 "a")) true? + "abcdef" false?)) + +(deftest validation-keypair-name-test + (are [arg expected] + (= (keypair-validator/validation-keypair-name arg) expected) + nil nil + "" nil + "name !" (i18n/label :t/key-name-error-special-char) + "Hello 😊" (i18n/label :t/key-name-error-emoji) + "abc" (i18n/label :t/your-key-pair-name-is-too-short) + (apply str (repeat 25 "a")) (i18n/label :t/your-key-pair-name-is-too-long))) diff --git a/src/status_im/constants.cljs b/src/status_im/constants.cljs index 0422622bb5cf..67d65779acde 100644 --- a/src/status_im/constants.cljs +++ b/src/status_im/constants.cljs @@ -77,6 +77,9 @@ (def ^:const contact-request-message-state-declined 3) (def ^:const contact-request-message-max-length 280) +(def ^:const keypair-name-max-length 20) +(def ^:const keypair-name-min-length 5) + (def request-to-join-pending-state 1) (def reactions diff --git a/src/status_im/contexts/settings/wallet/keypairs_and_accounts/actions/view.cljs b/src/status_im/contexts/settings/wallet/keypairs_and_accounts/actions/view.cljs index 5b0c999b4b16..39421e225c3c 100644 --- a/src/status_im/contexts/settings/wallet/keypairs_and_accounts/actions/view.cljs +++ b/src/status_im/contexts/settings/wallet/keypairs_and_accounts/actions/view.cljs @@ -1,6 +1,19 @@ (ns status-im.contexts.settings.wallet.keypairs-and-accounts.actions.view - (:require [quo.core :as quo])) + (:require [quo.core :as quo] + [utils.i18n :as i18n] + [utils.re-frame :as rf])) + +(defn on-rename-request + [data] + (rf/dispatch [:open-modal :screen/settings.rename-keypair data])) (defn view - [props] - [quo/drawer-top props]) + [props data] + [:<> + [quo/drawer-top props] + [quo/action-drawer + [(when (= (:type props) :keypair) + [{:icon :i/edit + :accessibility-label :rename-key-pair + :label (i18n/label :t/rename-key-pair) + :on-press #(on-rename-request data)}])]]]) diff --git a/src/status_im/contexts/settings/wallet/keypairs_and_accounts/rename/style.cljs b/src/status_im/contexts/settings/wallet/keypairs_and_accounts/rename/style.cljs new file mode 100644 index 000000000000..ae4d0edb8a7a --- /dev/null +++ b/src/status_im/contexts/settings/wallet/keypairs_and_accounts/rename/style.cljs @@ -0,0 +1,11 @@ +(ns status-im.contexts.settings.wallet.keypairs-and-accounts.rename.style) + +(def header-container + {:margin-bottom 8}) + +(def bottom-action + {:margin-horizontal -20}) + +(def error-container + {:margin-left 20 + :margin-vertical 8}) diff --git a/src/status_im/contexts/settings/wallet/keypairs_and_accounts/rename/view.cljs b/src/status_im/contexts/settings/wallet/keypairs_and_accounts/rename/view.cljs new file mode 100644 index 000000000000..a99610464e68 --- /dev/null +++ b/src/status_im/contexts/settings/wallet/keypairs_and_accounts/rename/view.cljs @@ -0,0 +1,82 @@ +(ns status-im.contexts.settings.wallet.keypairs-and-accounts.rename.view + (:require [clojure.string :as string] + [quo.core :as quo] + [react-native.core :as rn] + [status-im.common.floating-button-page.view :as floating-button-page] + [status-im.common.validation.keypair :as keypair-validator] + [status-im.constants :as constants] + [status-im.contexts.settings.wallet.keypairs-and-accounts.rename.style :as style] + [utils.debounce :as debounce] + [utils.i18n :as i18n] + [utils.re-frame :as rf])) + +(defn navigation-back [] (rf/dispatch [:navigate-back])) + +(defn view + [] + (let [{:keys [name]} (rf/sub [:get-screen-params]) + customization-color (rf/sub [:profile/customization-color]) + [unsaved-keypair-name set-unsaved-keypair-name] (rn/use-state name) + [error-msg set-error-msg] (rn/use-state nil) + [typing? set-typing?] (rn/use-state false) + validate-keypair-name (rn/use-callback + (debounce/debounce + (fn [name] + (set-error-msg + (keypair-validator/validation-keypair-name + name)) + (set-typing? false)) + 300)) + on-change-text (rn/use-callback (fn [text] + (set-typing? true) + (set-unsaved-keypair-name + text) + (validate-keypair-name text))) + on-clear (rn/use-callback (fn [] + (on-change-text ""))) + on-continue (rn/use-callback #(rf/dispatch + [:wallet/edit-keypair-name + unsaved-keypair-name]) + [unsaved-keypair-name])] + [floating-button-page/view + {:header [quo/page-nav + {:icon-name :i/close + :on-press navigation-back + :accessibility-label :top-bar}] + :footer [quo/bottom-actions + {:actions :one-action + :button-one-label (i18n/label :t/save) + :button-one-props {:disabled? (or typing? + (string/blank? unsaved-keypair-name) + (not (string/blank? error-msg))) + :customization-color customization-color + :on-press on-continue} + :container-style style/bottom-action}]} + [quo/page-top + {:container-style style/header-container + :title (i18n/label :t/rename-key-pair) + :description :context-tag + :context-tag {:type :icon + :size 24 + :context name + :icon :i/seed-phrase}}] + [quo/input + {:container-style {:margin-horizontal 20} + :placeholder (i18n/label :t/keypair-name-input-placeholder) + :label (i18n/label :t/keypair-name) + :default-value unsaved-keypair-name + :char-limit constants/keypair-name-max-length + :max-length constants/keypair-name-max-length + :auto-focus true + :clearable? (not (string/blank? unsaved-keypair-name)) + :on-clear on-clear + :on-change-text on-change-text + :error? (not (string/blank? error-msg))}] + (when-not (string/blank? error-msg) + [quo/info-message + {:type :error + :size :default + :icon :i/info + :container-style style/error-container} + error-msg])])) + diff --git a/src/status_im/contexts/wallet/add_account/create_account/new_keypair/keypair_name/view.cljs b/src/status_im/contexts/wallet/add_account/create_account/new_keypair/keypair_name/view.cljs index 6b48ce35674f..77ba5ca10bf4 100644 --- a/src/status_im/contexts/wallet/add_account/create_account/new_keypair/keypair_name/view.cljs +++ b/src/status_im/contexts/wallet/add_account/create_account/new_keypair/keypair_name/view.cljs @@ -4,13 +4,11 @@ [react-native.core :as rn] [status-im.common.floating-button-page.view :as floating-button-page] [status-im.common.validation.general :as validators] + [status-im.constants :as constants] [status-im.contexts.wallet.add-account.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) @@ -26,7 +24,8 @@ on-change-text (rn/use-callback (fn [value] (set-keypair-name value) (cond - (> (count value) keypair-name-max-length) + (> (count value) + constants/keypair-name-max-length) (set-error :length) (validators/has-emojis? value) (set-error :emoji) @@ -48,9 +47,9 @@ :button-one-label (i18n/label :t/continue) :button-one-props {:disabled? (or (pos? error) (< (count keypair-name) - keypair-name-min-length) + constants/keypair-name-min-length) (> (count keypair-name) - keypair-name-max-length)) + constants/keypair-name-max-length)) :customization-color customization-color :on-press on-continue} :container-style style/bottom-action}]} @@ -62,7 +61,7 @@ {: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 + :char-limit constants/keypair-name-max-length :auto-focus true :on-change-text on-change-text :error error}] diff --git a/src/status_im/navigation/screens.cljs b/src/status_im/navigation/screens.cljs index 553ca53c994f..05781fe21585 100644 --- a/src/status_im/navigation/screens.cljs +++ b/src/status_im/navigation/screens.cljs @@ -57,6 +57,7 @@ [status-im.contexts.profile.settings.screens.password.change-password.view :as change-password] [status-im.contexts.profile.settings.screens.password.view :as settings-password] [status-im.contexts.profile.settings.view :as settings] + [status-im.contexts.settings.wallet.keypairs-and-accounts.rename.view :as keypair-rename] [status-im.contexts.settings.wallet.keypairs-and-accounts.view :as keypairs-and-accounts] [status-im.contexts.settings.wallet.saved-addresses.view :as saved-addresses-settings] [status-im.contexts.settings.wallet.wallet-options.view :as wallet-options] @@ -498,6 +499,10 @@ :options options/transparent-modal-screen-options :component wallet-options/view} + {:name :screen/settings.rename-keypair + :options (assoc options/dark-screen :sheet? true) + :component keypair-rename/view} + {:name :screen/settings.saved-addresses :options options/transparent-modal-screen-options :component saved-addresses-settings/view} diff --git a/translations/en.json b/translations/en.json index 7d21a57d84de..8ae47b78805f 100644 --- a/translations/en.json +++ b/translations/en.json @@ -1255,6 +1255,7 @@ "remove-network": "Remove network", "remove-token": "Remove token", "removed": "removed", + "rename-key-pair": "Rename key pair", "repeat-pin": "Repeat new 6-digit passcode", "repeat-puk": "Repeat new 12-digit PUK", "report-bug-email-template": "1. Issue Description\n{{description}}\n\n\n2. Steps to reproduce\n{{steps}}\n\n\n3. Attach screenshots that can demo the problem, please\n", @@ -2590,6 +2591,8 @@ "other": "{{count}} addresses" }, "max": "Max: {{number}}", + "your-key-pair-name-is-too-long": "Your key pair name is too long", + "your-key-pair-name-is-too-short": "Your key pair name is too short", "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",