Add validation for when adding a contact (#15192)
This commit is contained in:
parent
a502da6ea4
commit
c238ebe36e
|
@ -1,5 +1,6 @@
|
|||
(ns status-im.mobile-sync-settings.core
|
||||
(:require [status-im2.common.bottom-sheet.events :as bottom-sheet]
|
||||
[status-im2.contexts.add-new-contact.events :as add-new-contact]
|
||||
[status-im.mailserver.core :as mailserver]
|
||||
[status-im.multiaccounts.model :as multiaccounts.model]
|
||||
[status-im.multiaccounts.update.core :as multiaccounts.update]
|
||||
|
@ -42,7 +43,8 @@
|
|||
(and logged-in? initialized?)
|
||||
[(mailserver/process-next-messages-request)
|
||||
(bottom-sheet/hide-bottom-sheet)
|
||||
(wallet/restart-wallet-service nil)]
|
||||
(wallet/restart-wallet-service nil)
|
||||
(add-new-contact/set-new-identity-reconnected)]
|
||||
|
||||
logged-in?
|
||||
[(mailserver/process-next-messages-request)
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
(ns status-im2.contexts.add-new-contact.events
|
||||
(:require [utils.re-frame :as rf]
|
||||
(:require [clojure.string :as string]
|
||||
[utils.re-frame :as rf]
|
||||
[status-im.utils.types :as types]
|
||||
[re-frame.core :as re-frame]
|
||||
[status-im.ethereum.core :as ethereum]
|
||||
|
@ -11,11 +12,120 @@
|
|||
[status-im2.contexts.contacts.events :as data-store.contacts]
|
||||
[status-im.utils.utils :as utils]))
|
||||
|
||||
(defn init-contact
|
||||
"Create a new contact (persisted to app-db as [:contacts/new-identity]).
|
||||
The following options are available:
|
||||
|
||||
| key | description |
|
||||
| -------------------|-------------|
|
||||
| `:user-public-key` | user's public key (not the contact)
|
||||
| `:input` | raw user input (untrimmed)
|
||||
| `:scanned` | scanned user input (untrimmed)
|
||||
| `:id` | public-key|compressed-key|ens
|
||||
| `:type` | :empty|:public-key|:compressed-key|:ens
|
||||
| `:ens` | id.eth|id.ens-stateofus
|
||||
| `:public-key` | public-key (from decompression or ens resolution)
|
||||
| `:state` | :empty|:invalid|:decompress-key|:resolve-ens|:valid
|
||||
| `:msg` | keyword i18n msg"
|
||||
([]
|
||||
(-> [:user-public-key :input :scanned :id :type :ens :public-key :state :msg]
|
||||
(zipmap (repeat nil))))
|
||||
([kv] (-> (init-contact) (merge kv))))
|
||||
|
||||
(def url-regex #"^https?://join.status.im/u/(.+)")
|
||||
|
||||
(defn ->id
|
||||
[{:keys [input] :as contact}]
|
||||
(let [trimmed-input (utils/safe-trim input)]
|
||||
(->> {:id (if (empty? trimmed-input)
|
||||
nil
|
||||
(if-some [[_ id] (re-matches url-regex trimmed-input)]
|
||||
id
|
||||
trimmed-input))}
|
||||
(merge contact))))
|
||||
|
||||
(defn ->type
|
||||
[{:keys [id] :as contact}]
|
||||
(->> (cond
|
||||
(empty? id)
|
||||
{:type :empty}
|
||||
|
||||
(validators/valid-public-key? id)
|
||||
{:type :public-key
|
||||
:public-key id}
|
||||
|
||||
(validators/valid-compressed-key? id)
|
||||
{:type :compressed-key}
|
||||
|
||||
:else
|
||||
{:type :ens
|
||||
:ens (stateofus/ens-name-parse id)})
|
||||
(merge contact)))
|
||||
|
||||
(defn ->state
|
||||
[{:keys [id type public-key user-public-key] :as contact}]
|
||||
(->> (cond
|
||||
(empty? id)
|
||||
{:state :empty}
|
||||
|
||||
(= type :public-key)
|
||||
{:state :invalid
|
||||
:msg :t/not-a-chatkey}
|
||||
|
||||
(= public-key user-public-key)
|
||||
{:state :invalid
|
||||
:msg :t/can-not-add-yourself}
|
||||
|
||||
(and (= type :compressed-key) (empty? public-key))
|
||||
{:state :decompress-key}
|
||||
|
||||
(and (= type :ens) (empty? public-key))
|
||||
{:state :resolve-ens}
|
||||
|
||||
(and (or (= type :compressed-key) (= type :ens))
|
||||
(validators/valid-public-key? public-key))
|
||||
{:state :valid})
|
||||
(merge contact)))
|
||||
|
||||
(def validate-contact (comp ->state ->type ->id))
|
||||
|
||||
(defn dispatcher [event input] (fn [arg] (rf/dispatch [event input arg])))
|
||||
|
||||
(rf/defn set-new-identity
|
||||
{:events [:contacts/set-new-identity]}
|
||||
[{:keys [db]} input scanned]
|
||||
(let [user-public-key (get-in db [:multiaccount :public-key])
|
||||
{:keys [input id ens state]
|
||||
:as contact} (-> {:user-public-key user-public-key
|
||||
:input input
|
||||
:scanned scanned}
|
||||
init-contact
|
||||
validate-contact)]
|
||||
(case state
|
||||
|
||||
:empty {:db (dissoc db :contacts/new-identity)}
|
||||
(:valid :invalid) {:db (assoc db :contacts/new-identity contact)}
|
||||
:decompress-key {:db (assoc db :contacts/new-identity contact)
|
||||
:contacts/decompress-public-key
|
||||
{:compressed-key id
|
||||
:on-success
|
||||
(dispatcher :contacts/set-new-identity-success input)
|
||||
:on-error
|
||||
(dispatcher :contacts/set-new-identity-error input)}}
|
||||
:resolve-ens {:db (assoc db :contacts/new-identity contact)
|
||||
:contacts/resolve-public-key-from-ens
|
||||
{:chain-id (ethereum/chain-id db)
|
||||
:ens ens
|
||||
:on-success
|
||||
(dispatcher :contacts/set-new-identity-success input)
|
||||
:on-error
|
||||
(dispatcher :contacts/set-new-identity-error input)}})))
|
||||
|
||||
(re-frame/reg-fx
|
||||
:contacts/decompress-public-key
|
||||
(fn [{:keys [public-key on-success on-error]}]
|
||||
(fn [{:keys [compressed-key on-success on-error]}]
|
||||
(status/compressed-key->public-key
|
||||
public-key
|
||||
compressed-key
|
||||
(fn [resp]
|
||||
(let [{:keys [error]} (types/json->clj resp)]
|
||||
(if error
|
||||
|
@ -23,74 +133,16 @@
|
|||
(on-success (str "0x" (subs resp 5)))))))))
|
||||
|
||||
(re-frame/reg-fx
|
||||
:contacts/resolve-public-key-from-ens-name
|
||||
(fn [{:keys [chain-id ens-name on-success on-error]}]
|
||||
(ens/pubkey chain-id ens-name on-success on-error)))
|
||||
|
||||
(defn fx-callbacks
|
||||
[input ens-name]
|
||||
{:on-success (fn [pubkey]
|
||||
(rf/dispatch [:contacts/set-new-identity-success input ens-name pubkey]))
|
||||
:on-error (fn [err]
|
||||
(rf/dispatch [:contacts/set-new-identity-error err input]))})
|
||||
|
||||
(defn identify-type
|
||||
[input]
|
||||
(let [regex #"^https?://join.status.im/u/(.+)"
|
||||
id (as-> (utils/safe-trim input) $
|
||||
(if-some [[_ match] (re-matches regex $)]
|
||||
match
|
||||
$)
|
||||
(if (empty? $) nil $))
|
||||
public-key? (validators/valid-public-key? id)
|
||||
compressed-key? (validators/valid-compressed-key? id)
|
||||
type (cond (empty? id) :empty
|
||||
public-key? :public-key
|
||||
compressed-key? :compressed-key
|
||||
:else :ens-name)
|
||||
ens-name (when (= type :ens-name)
|
||||
(stateofus/ens-name-parse id))]
|
||||
{:input input
|
||||
:id id
|
||||
:type type
|
||||
:ens-name ens-name}))
|
||||
|
||||
(rf/defn set-new-identity
|
||||
{:events [:contacts/set-new-identity]}
|
||||
[{:keys [db]} input]
|
||||
(let [{:keys [input id type ens-name]} (identify-type input)]
|
||||
(case type
|
||||
:empty {:db (dissoc db :contacts/new-identity)}
|
||||
:public-key {:db (assoc db
|
||||
:contacts/new-identity
|
||||
{:input input
|
||||
:public-key id
|
||||
:state :error
|
||||
:error :uncompressed-key})}
|
||||
:compressed-key {:db
|
||||
(assoc db
|
||||
:contacts/new-identity
|
||||
{:input input
|
||||
:state :searching})
|
||||
:contacts/decompress-public-key
|
||||
(merge {:public-key id}
|
||||
(fx-callbacks id ens-name))}
|
||||
:ens-name {:db
|
||||
(assoc db
|
||||
:contacts/new-identity
|
||||
{:input input
|
||||
:state :searching})
|
||||
:contacts/resolve-public-key-from-ens-name
|
||||
(merge {:chain-id (ethereum/chain-id db)
|
||||
:ens-name ens-name}
|
||||
(fx-callbacks id ens-name))})))
|
||||
:contacts/resolve-public-key-from-ens
|
||||
(fn [{:keys [chain-id ens on-success on-error]}]
|
||||
(ens/pubkey chain-id ens on-success on-error)))
|
||||
|
||||
(rf/defn build-contact
|
||||
{:events [:contacts/build-contact]}
|
||||
[_ pubkey ens-name open-profile-modal?]
|
||||
[_ pubkey ens open-profile-modal?]
|
||||
{:json-rpc/call [{:method "wakuext_buildContact"
|
||||
:params [{:publicKey pubkey
|
||||
:ENSName ens-name}]
|
||||
:ENSName ens}]
|
||||
:js-response true
|
||||
:on-success #(rf/dispatch [:contacts/contact-built
|
||||
pubkey
|
||||
|
@ -106,24 +158,25 @@
|
|||
|
||||
(rf/defn set-new-identity-success
|
||||
{:events [:contacts/set-new-identity-success]}
|
||||
[{:keys [db] :as cofx} input ens-name pubkey]
|
||||
(rf/merge cofx
|
||||
{:db (assoc db
|
||||
[{:keys [db]} input pubkey]
|
||||
(let [contact (get-in db [:contacts/new-identity])]
|
||||
(when (= (:input contact) input)
|
||||
(rf/merge {:db (assoc db
|
||||
:contacts/new-identity
|
||||
{:input input
|
||||
:public-key pubkey
|
||||
:ens-name ens-name
|
||||
:state :valid})}
|
||||
(build-contact pubkey ens-name false)))
|
||||
(->state (assoc contact :public-key pubkey)))}
|
||||
(build-contact pubkey (:ens contact) false)))))
|
||||
|
||||
(rf/defn set-new-identity-error
|
||||
{:events [:contacts/set-new-identity-error]}
|
||||
[{:keys [db]} error input]
|
||||
{:db (assoc db
|
||||
:contacts/new-identity
|
||||
{:input input
|
||||
:state :error
|
||||
:error :invalid})})
|
||||
[{:keys [db]} input err]
|
||||
(let [contact (get-in db [:contacts/new-identity])]
|
||||
(when (= (:input contact) input)
|
||||
(let [state (cond
|
||||
(or (string/includes? (:message err) "fallback failed")
|
||||
(string/includes? (:message err) "no such host"))
|
||||
{:state :invalid :msg :t/lost-connection}
|
||||
:else {:state :invalid})]
|
||||
{:db (assoc db :contacts/new-identity (merge contact state))}))))
|
||||
|
||||
(rf/defn clear-new-identity
|
||||
{:events [:contacts/clear-new-identity :contacts/new-chat-focus]}
|
||||
|
@ -132,7 +185,13 @@
|
|||
|
||||
(rf/defn qr-code-scanned
|
||||
{:events [:contacts/qr-code-scanned]}
|
||||
[{:keys [db] :as cofx} input]
|
||||
[{:keys [db] :as cofx} scanned]
|
||||
(rf/merge cofx
|
||||
(set-new-identity input)
|
||||
(set-new-identity scanned scanned)
|
||||
(navigation/navigate-back)))
|
||||
|
||||
(rf/defn set-new-identity-reconnected
|
||||
[{:keys [db]}]
|
||||
(let [input (get-in db [:contacts/new-identity :input])
|
||||
resubmit? (and input (= :new-contact (get-in db [:view-id])))]
|
||||
(rf/dispatch [:contacts/set-new-identity input])))
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
(ns status-im2.contexts.add-new-contact.events-test
|
||||
(:require [cljs.test :refer-macros [deftest is are]]
|
||||
[status-im2.contexts.add-new-contact.events :as core]))
|
||||
(:require [cljs.test :refer-macros [deftest are]]
|
||||
[status-im2.contexts.add-new-contact.events :as events]))
|
||||
|
||||
(def user-ukey
|
||||
"0x04ca27ed9c7c4099d230c6d8853ad0cfaf084a019c543e9e433d3c04fac6de9147cf572b10e247cfe52f396b5aa10456b56dd1cf1d8a681e2b93993d44594b2e85")
|
||||
(def user-ckey "zQ3shtFEo4PxpQiYGcNZZ8xhJmhD6WBXwnHPBueu5SRnvPXjk")
|
||||
(def ukey
|
||||
"0x045596a7ff87da36860a84b0908191ce60a504afc94aac93c1abd774f182967ce694f1bf2d8773cd59f4dd0863e951f9b7f7351c5516291a0fceb73f8c392a0e88")
|
||||
(def ckey "zQ3shWj4WaBdf2zYKCkXe6PHxDxNTzZyid1i75879Ue9cX9gA")
|
||||
|
@ -10,47 +13,118 @@
|
|||
(def link-ckey (str "https://join.status.im/u/" ckey))
|
||||
(def link-ens (str "https://join.status.im/u/" ens))
|
||||
|
||||
(deftest identify-type-test
|
||||
(are [input expected] (= (core/identify-type input) expected)
|
||||
"" {:input ""
|
||||
:id nil
|
||||
:type :empty
|
||||
:ens-name nil}
|
||||
;;; unit tests (no app-db involved)
|
||||
|
||||
ukey {:input ukey
|
||||
(deftest validate-contact-test
|
||||
(are [i e] (= (events/validate-contact (events/init-contact
|
||||
{:user-public-key user-ukey
|
||||
:input i}))
|
||||
(events/init-contact e))
|
||||
|
||||
"" {:user-public-key user-ukey
|
||||
:input ""
|
||||
:type :empty
|
||||
:state :empty}
|
||||
|
||||
" " {:user-public-key user-ukey
|
||||
:input " "
|
||||
:type :empty
|
||||
:state :empty}
|
||||
|
||||
ukey {:user-public-key user-ukey
|
||||
:input ukey
|
||||
:id ukey
|
||||
:type :public-key
|
||||
:ens-name nil}
|
||||
|
||||
ens {:input ens
|
||||
:id ens
|
||||
:type :ens-name
|
||||
:ens-name ens-stateofus-eth}
|
||||
|
||||
ckey {:input ckey
|
||||
:id ckey
|
||||
:type :compressed-key
|
||||
:ens-name nil}
|
||||
|
||||
link-ckey {:input link-ckey
|
||||
:id ckey
|
||||
:type :compressed-key
|
||||
:ens-name nil}
|
||||
|
||||
link-ens {:input link-ens
|
||||
:id ens
|
||||
:type :ens-name
|
||||
:ens-name ens-stateofus-eth}))
|
||||
|
||||
(deftest search-empty-string-test
|
||||
(is (= (core/set-new-identity {:db {:contacts/new-identity :foo}} "")
|
||||
{:db {}})))
|
||||
|
||||
(deftest search-uncompressed-key-test
|
||||
(is (= (core/set-new-identity {:db {}} ukey)
|
||||
{:db {:contacts/new-identity
|
||||
{:input ukey
|
||||
:public-key ukey
|
||||
:state :error
|
||||
:error :uncompressed-key}}})))
|
||||
:state :invalid
|
||||
:msg :t/not-a-chatkey}
|
||||
|
||||
ens {:user-public-key user-ukey
|
||||
:input ens
|
||||
:id ens
|
||||
:type :ens
|
||||
:ens ens-stateofus-eth
|
||||
:state :resolve-ens}
|
||||
|
||||
(str " " ens) {:user-public-key user-ukey
|
||||
:input (str " " ens)
|
||||
:id ens
|
||||
:type :ens
|
||||
:ens ens-stateofus-eth
|
||||
:state :resolve-ens}
|
||||
|
||||
ckey {:user-public-key user-ukey
|
||||
:input ckey
|
||||
:id ckey
|
||||
:type :compressed-key
|
||||
:state :decompress-key}
|
||||
|
||||
link-ckey {:user-public-key user-ukey
|
||||
:input link-ckey
|
||||
:id ckey
|
||||
:type :compressed-key
|
||||
:state :decompress-key}
|
||||
|
||||
link-ens {:user-public-key user-ukey
|
||||
:input link-ens
|
||||
:id ens
|
||||
:type :ens
|
||||
:ens ens-stateofus-eth
|
||||
:state :resolve-ens}))
|
||||
|
||||
;;; event handler tests (no callbacks)
|
||||
|
||||
(def db
|
||||
{:multiaccount {:public-key user-ukey}
|
||||
:networks/current-network "mainnet_rpc"
|
||||
:networks/networks {"mainnet_rpc"
|
||||
{:id "mainnet_rpc"
|
||||
:config {:NetworkId 1}}}})
|
||||
|
||||
(deftest set-new-identity-test
|
||||
(with-redefs [events/dispatcher (fn [& args] args)]
|
||||
(are [i edb] (= (events/set-new-identity {:db db} i nil) edb)
|
||||
|
||||
"" {:db db}
|
||||
|
||||
ukey {:db (assoc db
|
||||
:contacts/new-identity
|
||||
(events/init-contact
|
||||
{:user-public-key user-ukey
|
||||
:input ukey
|
||||
:id ukey
|
||||
:type :public-key
|
||||
:public-key ukey
|
||||
:state :invalid
|
||||
:msg :t/not-a-chatkey}))}
|
||||
|
||||
ens {:db (assoc db
|
||||
:contacts/new-identity
|
||||
(events/init-contact
|
||||
{:user-public-key user-ukey
|
||||
:input ens
|
||||
:id ens
|
||||
:type :ens
|
||||
:ens ens-stateofus-eth
|
||||
:public-key nil ; not yet...
|
||||
:state :resolve-ens}))
|
||||
:contacts/resolve-public-key-from-ens
|
||||
{:chain-id 1
|
||||
:ens ens-stateofus-eth
|
||||
:on-success [:contacts/set-new-identity-success ens]
|
||||
:on-error [:contacts/set-new-identity-error ens]}}
|
||||
|
||||
;; compressed-key & add-self-as-contact
|
||||
user-ckey {:db (assoc db
|
||||
:contacts/new-identity
|
||||
(events/init-contact
|
||||
{:user-public-key user-ukey
|
||||
:input user-ckey
|
||||
:id user-ckey
|
||||
:type :compressed-key
|
||||
:public-key nil ; not yet...
|
||||
:state :decompress-key}))
|
||||
:contacts/decompress-public-key
|
||||
{:compressed-key user-ckey
|
||||
:on-success [:contacts/set-new-identity-success user-ckey]
|
||||
:on-error [:contacts/set-new-identity-error user-ckey]}})))
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
{:style {:flex-direction :row
|
||||
:justify-content :space-between}})
|
||||
|
||||
(def container-error
|
||||
(def container-invalid
|
||||
{:style {:flex-direction :row
|
||||
:align-items :center
|
||||
:margin-top 8}})
|
||||
|
@ -64,18 +64,18 @@
|
|||
colors/neutral-50
|
||||
colors/neutral-40)}})
|
||||
|
||||
(def icon-error
|
||||
(def icon-invalid
|
||||
{:size 16
|
||||
:color colors/danger-50})
|
||||
|
||||
(def text-error
|
||||
(def text-invalid
|
||||
{:size :paragraph-2
|
||||
:align :left
|
||||
:style {:margin-left 4
|
||||
:color colors/danger-50}})
|
||||
|
||||
(defn text-input-container
|
||||
[error?]
|
||||
[invalid?]
|
||||
{:style {:padding-top 1
|
||||
:padding-left 12
|
||||
:padding-right 7
|
||||
|
@ -88,7 +88,7 @@
|
|||
colors/neutral-95)
|
||||
:border-width 1
|
||||
:border-radius 12
|
||||
:border-color (if error?
|
||||
:border-color (if invalid?
|
||||
colors/danger-50-opa-40
|
||||
(colors/theme-colors
|
||||
colors/neutral-20
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
[quo2.core :as quo]
|
||||
[react-native.core :as rn]
|
||||
[react-native.clipboard :as clipboard]
|
||||
[reagent.core :as reagent]
|
||||
[status-im2.common.resources :as resources]
|
||||
[status-im.qr-scanner.core :as qr-scanner]
|
||||
[status-im.utils.utils :as utils]
|
||||
|
@ -44,9 +45,16 @@
|
|||
|
||||
(defn new-contact
|
||||
[]
|
||||
(let [{:keys [input public-key state error ens-name]} (rf/sub [:contacts/new-identity])
|
||||
error? (and (= state :error)
|
||||
(= error :uncompressed-key))]
|
||||
(let [clipboard (reagent/atom nil)
|
||||
default-value (reagent/atom nil)]
|
||||
(fn []
|
||||
(clipboard/get-string #(reset! clipboard %))
|
||||
(let [{:keys [input scanned public-key ens state msg]}
|
||||
(rf/sub [:contacts/new-identity])
|
||||
invalid? (= state :invalid)
|
||||
show-paste-button? (and (not (string/blank? @clipboard))
|
||||
(string/blank? @default-value)
|
||||
(string/blank? input))]
|
||||
[rn/keyboard-avoiding-view (style/container-kbd)
|
||||
[rn/view style/container-image
|
||||
[rn/image
|
||||
|
@ -56,6 +64,8 @@
|
|||
(merge (style/button-close)
|
||||
{:on-press
|
||||
(fn []
|
||||
(reset! clipboard nil)
|
||||
(reset! default-value nil)
|
||||
(rf/dispatch [:contacts/clear-new-identity])
|
||||
(rf/dispatch [:navigate-back]))}) :i/close]]
|
||||
[rn/view (style/container-outer)
|
||||
|
@ -67,29 +77,36 @@
|
|||
[quo/text (style/text-description)
|
||||
(i18n/label :t/ens-or-chat-key)]
|
||||
[rn/view style/container-text-input
|
||||
[rn/view (style/text-input-container error?)
|
||||
[rn/view (style/text-input-container invalid?)
|
||||
[rn/text-input
|
||||
(merge (style/text-input)
|
||||
{:default-value input
|
||||
{:default-value (or scanned @default-value input)
|
||||
:placeholder (i18n/label :t/type-some-chat-key)
|
||||
:on-change-text #(debounce/debounce-and-dispatch
|
||||
[:contacts/set-new-identity %]
|
||||
600)})]
|
||||
(when (string/blank? input)
|
||||
:on-change-text (fn [v]
|
||||
(reset! default-value v)
|
||||
(debounce/debounce-and-dispatch
|
||||
[:contacts/set-new-identity v nil]
|
||||
600))})]
|
||||
(when show-paste-button?
|
||||
[quo/button
|
||||
(merge style/button-paste
|
||||
{:on-press (fn []
|
||||
(clipboard/get-string #(rf/dispatch [:contacts/set-new-identity %])))})
|
||||
{:on-press
|
||||
(fn []
|
||||
(reset! default-value @clipboard)
|
||||
(rf/dispatch
|
||||
[:contacts/set-new-identity @clipboard nil]))})
|
||||
(i18n/label :t/paste)])]
|
||||
[quo/button
|
||||
(merge style/button-qr
|
||||
{:on-press #(rf/dispatch [::qr-scanner/scan-code
|
||||
{:on-press #(rf/dispatch
|
||||
[::qr-scanner/scan-code
|
||||
{:handler :contacts/qr-code-scanned}])})
|
||||
:i/scan]]
|
||||
(when error?
|
||||
[rn/view style/container-error
|
||||
[quo/icon :i/alert style/icon-error]
|
||||
[quo/text style/text-error (i18n/label :t/not-a-chatkey)]])
|
||||
(when invalid?
|
||||
[rn/view style/container-invalid
|
||||
[quo/icon :i/alert style/icon-invalid]
|
||||
[quo/text style/text-invalid
|
||||
(i18n/label (or msg :t/invalid-ens-or-key))]])
|
||||
(when (= state :valid)
|
||||
[found-contact public-key])]
|
||||
[rn/view
|
||||
|
@ -97,7 +114,9 @@
|
|||
(merge (style/button-view-profile state)
|
||||
{:on-press
|
||||
(fn []
|
||||
(reset! clipboard nil)
|
||||
(reset! default-value nil)
|
||||
(rf/dispatch [:contacts/clear-new-identity])
|
||||
(rf/dispatch [:navigate-back])
|
||||
(rf/dispatch [:chat.ui/show-profile public-key ens-name]))})
|
||||
(i18n/label :t/view-profile)]]]]))
|
||||
(rf/dispatch [:chat.ui/show-profile public-key ens]))})
|
||||
(i18n/label :t/view-profile)]]]]))))
|
||||
|
|
|
@ -719,6 +719,7 @@
|
|||
"invalid-pairing-password": "Invalid pairing password",
|
||||
"invalid-range": "Invalid format, must be between {{min}} and {{max}}",
|
||||
"invalid-username-or-key": "Invalid username or chat key",
|
||||
"invalid-ens-or-key": "Invalid ENS or Chat key",
|
||||
"join-me": "Hey join me on Status: {{url}}",
|
||||
"join-a-community": "or join a community",
|
||||
"join-open-community": "Join Community",
|
||||
|
|
Loading…
Reference in New Issue