mirror of
https://github.com/status-im/status-mobile.git
synced 2025-01-13 10:16:01 +00:00
Lookup/add a contact from home screen (#14477)
This commit is contained in:
parent
d16baca64f
commit
fac9e644a9
BIN
resources/images/icons2/16x16/alert@2x.png
Normal file
BIN
resources/images/icons2/16x16/alert@2x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 822 B |
BIN
resources/images/icons2/16x16/alert@3x.png
Normal file
BIN
resources/images/icons2/16x16/alert@3x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
BIN
resources/images/ui/add-contact@2x.png
Normal file
BIN
resources/images/ui/add-contact@2x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 112 KiB |
BIN
resources/images/ui/add-contact@3x.png
Normal file
BIN
resources/images/ui/add-contact@3x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 192 KiB |
@ -1,7 +1,7 @@
|
||||
(ns status-im.add-new.db
|
||||
(:require [cljs.spec.alpha :as spec]
|
||||
[status-im.ethereum.ens :as ens]
|
||||
[status-im.utils.db :as utils.db]))
|
||||
[status-im2.utils.validators :as validators]))
|
||||
|
||||
(defn own-public-key?
|
||||
[{:keys [multiaccount]} public-key]
|
||||
@ -10,7 +10,7 @@
|
||||
(defn validate-pub-key
|
||||
[db public-key]
|
||||
(cond
|
||||
(or (not (utils.db/valid-public-key? public-key))
|
||||
(or (not (validators/valid-public-key? public-key))
|
||||
(= public-key ens/default-key))
|
||||
:invalid
|
||||
(own-public-key? db public-key)
|
||||
@ -27,4 +27,4 @@
|
||||
[topic]
|
||||
(and topic
|
||||
(spec/valid? ::topic topic)
|
||||
(not (utils.db/valid-public-key? topic))))
|
||||
(not (validators/valid-public-key? topic))))
|
||||
|
@ -35,15 +35,16 @@
|
||||
:on-error #(cb "0x")}))
|
||||
|
||||
(defn pubkey
|
||||
[chain-id ens-name cb]
|
||||
([chain-id ens-name on-success on-error]
|
||||
{:pre [(is-valid-eth-name? ens-name)]}
|
||||
(json-rpc/call {:method "ens_publicKeyOf"
|
||||
:params [chain-id ens-name]
|
||||
:on-success cb
|
||||
;;at some point infura started to return execution reverted error instead of "0x"
|
||||
;;result
|
||||
;;our code expects "0x" result
|
||||
:on-error #(cb "0x")}))
|
||||
:on-success on-success
|
||||
:on-error on-error}))
|
||||
;; At some point, infura started to return "execution reverted" error
|
||||
;; instead of "0x" result. Our code expects "0x" result.
|
||||
([chain-id ens-name cb]
|
||||
(pubkey chain-id ens-name cb #(cb "0x"))))
|
||||
|
||||
(defn owner
|
||||
[chain-id ens-name cb]
|
||||
|
@ -2,7 +2,6 @@
|
||||
(:require
|
||||
clojure.set
|
||||
[re-frame.core :as re-frame]
|
||||
status-im.add-new.core
|
||||
[status-im.async-storage.core :as async-storage]
|
||||
status-im.backup.core
|
||||
status-im.bootnodes.core
|
||||
|
@ -1,7 +1,7 @@
|
||||
(ns status-im.native-module.core
|
||||
(:require ["react-native" :as react-native]
|
||||
[re-frame.core :as re-frame]
|
||||
[status-im.utils.db :as utils.db]
|
||||
[status-im2.utils.validators :as validators]
|
||||
[status-im.utils.platform :as platform]
|
||||
[status-im.utils.react-native :as react-native-utils]
|
||||
[status-im.utils.types :as types]
|
||||
@ -472,13 +472,13 @@
|
||||
"Generate a 3 words random name based on the user public-key, synchronously"
|
||||
[public-key]
|
||||
(log/debug "[native-module] generate-gfycat")
|
||||
(when (utils.db/valid-public-key? public-key)
|
||||
(when (validators/valid-public-key? public-key)
|
||||
(.generateAlias ^js (status) public-key)))
|
||||
|
||||
(defn generate-gfycat-async
|
||||
"Generate a 3 words random name based on the user public-key, asynchronously"
|
||||
[public-key callback]
|
||||
(when (utils.db/valid-public-key? public-key)
|
||||
(when (validators/valid-public-key? public-key)
|
||||
(.generateAliasAsync ^js (status) public-key callback)))
|
||||
|
||||
(defn identicon
|
||||
|
@ -5,6 +5,7 @@
|
||||
{:empty-chats-header (js/require "../resources/images/ui/empty-chats-header.png")
|
||||
:welcome (js/require "../resources/images/ui/welcome.jpg")
|
||||
:welcome-dark (js/require "../resources/images/ui/welcome-dark.jpg")
|
||||
:add-new-contact (js/require "../resources/images/ui/add-contact.png")
|
||||
:chat (js/require "../resources/images/ui/chat.jpg")
|
||||
:chat-dark (js/require "../resources/images/ui/chat-dark.jpg")
|
||||
:wallet (js/require "../resources/images/ui/wallet.jpg")
|
||||
|
@ -9,7 +9,7 @@
|
||||
[status-im.ethereum.eip681 :as eip681]
|
||||
[status-im.ethereum.ens :as ens]
|
||||
[status-im.ethereum.stateofus :as stateofus]
|
||||
[status-im.utils.db :as utils.db]
|
||||
[status-im2.utils.validators :as validators]
|
||||
[status-im.utils.http :as http]
|
||||
[status-im.utils.wallet-connect :as wallet-connect]
|
||||
[taoensso.timbre :as log]
|
||||
@ -71,7 +71,7 @@
|
||||
|
||||
(defn match-contact-async
|
||||
[chain {:keys [user-id ens-name]} callback]
|
||||
(let [valid-key (and (utils.db/valid-public-key? user-id)
|
||||
(let [valid-key (and (validators/valid-public-key? user-id)
|
||||
(not= user-id ens/default-key))]
|
||||
(cond
|
||||
valid-key
|
||||
@ -113,8 +113,8 @@
|
||||
(not (string/blank? chat-name))
|
||||
(> (count chat-id-parts) 1)
|
||||
(not (string/blank? (first chat-id-parts)))
|
||||
(utils.db/valid-public-key? admin-pk)
|
||||
(utils.db/valid-public-key? (last chat-id-parts)))
|
||||
(validators/valid-public-key? admin-pk)
|
||||
(validators/valid-public-key? (last chat-id-parts)))
|
||||
{:type :group-chat
|
||||
:chat-id chat-id
|
||||
:invitation-admin admin-pk
|
||||
@ -228,7 +228,7 @@
|
||||
(= handler :group-chat)
|
||||
(cb (match-group-chat chats query-params))
|
||||
|
||||
(utils.db/valid-public-key? uri)
|
||||
(validators/valid-public-key? uri)
|
||||
(match-contact-async chain {:user-id uri} cb)
|
||||
|
||||
(= handler :community-requests)
|
||||
|
@ -20,7 +20,7 @@
|
||||
[status-im.ui.components.react :as react]
|
||||
[status-im.ui.components.topbar :as topbar]
|
||||
[status-im.ui.screens.chat.photos :as photos]
|
||||
[status-im.utils.db :as utils.db]
|
||||
[status-im2.utils.validators :as validators]
|
||||
[status-im.utils.gfycat.core :as gfycat]
|
||||
[status-im.utils.identicon :as identicon]
|
||||
[status-im.utils.utils :as utils]
|
||||
@ -172,7 +172,7 @@
|
||||
#(do
|
||||
(reset! search-value %)
|
||||
(re-frame/dispatch [:set-in [:contacts/new-identity :state] :empty])
|
||||
(debounce/debounce-and-dispatch [:new-chat/set-new-identity %] 600))
|
||||
(debounce/debounce-and-dispatch [:contacts/set-new-identity %] 600))
|
||||
:on-submit-editing
|
||||
#(when (= state :valid)
|
||||
(debounce/dispatch-and-chill [:contact.ui/contact-code-submitted false nil] 3000))
|
||||
@ -301,7 +301,7 @@
|
||||
(fn []
|
||||
(let [{:keys [state ens-name public-key error]} @(re-frame/subscribe [:contacts/new-identity])
|
||||
blocked? (and
|
||||
(utils.db/valid-public-key? (or public-key ""))
|
||||
(validators/valid-public-key? (or public-key ""))
|
||||
@(re-frame/subscribe [:contacts/contact-blocked?
|
||||
public-key]))]
|
||||
[react/view {:style {:flex 1}}
|
||||
@ -326,7 +326,7 @@
|
||||
{:on-change-text
|
||||
#(do
|
||||
(re-frame/dispatch [:set-in [:contacts/new-identity :state] :searching])
|
||||
(debounce/debounce-and-dispatch [:new-chat/set-new-identity %] 600))
|
||||
(debounce/debounce-and-dispatch [:contacts/set-new-identity %] 600))
|
||||
:on-submit-editing
|
||||
#(when (= state :valid)
|
||||
(debounce/dispatch-and-chill [:contact.ui/contact-code-submitted true @entered-nickname]
|
||||
|
@ -7,7 +7,6 @@
|
||||
[quo2.foundations.colors :as quo2.colors]
|
||||
[re-frame.core :as re-frame]
|
||||
[reagent.core :as reagent]
|
||||
[status-im.add-new.core :as new-chat]
|
||||
[status-im.add-new.db :as db]
|
||||
[utils.i18n :as i18n]
|
||||
[status-im.qr-scanner.core :as qr-scanner]
|
||||
@ -82,15 +81,15 @@
|
||||
:on-blur (fn []
|
||||
(when chats-empty
|
||||
(re-frame/dispatch [:search/home-filter-changed nil]))
|
||||
(re-frame/dispatch [::new-chat/clear-new-identity]))
|
||||
(re-frame/dispatch [:contacts/clear-new-identity]))
|
||||
:on-focus (fn [search-filter]
|
||||
(when-not search-filter
|
||||
(re-frame/dispatch [:search/home-filter-changed ""])
|
||||
(re-frame/dispatch [::new-chat/clear-new-identity])))
|
||||
(re-frame/dispatch [:contacts/clear-new-identity])))
|
||||
:on-change (fn [text]
|
||||
(re-frame/dispatch [:search/home-filter-changed text])
|
||||
(re-frame/dispatch [:set-in [:contacts/new-identity :state] :searching])
|
||||
(debounce/debounce-and-dispatch [:new-chat/set-new-identity text] 300))}]])
|
||||
(debounce/debounce-and-dispatch [:contacts/set-new-identity text] 300))}]])
|
||||
|
||||
(defn search-input-wrapper-old
|
||||
[search-filter chats-empty]
|
||||
@ -104,11 +103,11 @@
|
||||
:on-blur (fn []
|
||||
(when chats-empty
|
||||
(re-frame/dispatch [:search/home-filter-changed nil]))
|
||||
(re-frame/dispatch [::new-chat/clear-new-identity]))
|
||||
(re-frame/dispatch [:contacts/clear-new-identity]))
|
||||
:on-focus (fn [search-filter]
|
||||
(when-not search-filter
|
||||
(re-frame/dispatch [:search/home-filter-changed ""])
|
||||
(re-frame/dispatch [::new-chat/clear-new-identity])))
|
||||
(re-frame/dispatch [:contacts/clear-new-identity])))
|
||||
:on-change (fn [text]
|
||||
(re-frame/dispatch [:search/home-filter-changed text])
|
||||
(re-frame/dispatch [:set-in [:contacts/new-identity :state] :searching])
|
||||
|
@ -1,7 +1,6 @@
|
||||
(ns status-im.ui.screens.screens
|
||||
(:require
|
||||
[quo.design-system.colors :as colors]
|
||||
[status-im.add-new.core :as new-chat.events]
|
||||
[utils.i18n :as i18n]
|
||||
[status-im.ui.components.icons.icons :as icons]
|
||||
[status-im.ui.screens.about-app.views :as about-app]
|
||||
@ -545,7 +544,7 @@
|
||||
|
||||
;[Chat] New Chat
|
||||
{:name :new-chat
|
||||
:on-focus [::new-chat.events/new-chat-focus]
|
||||
:on-focus [:contacts/new-chat-focus]
|
||||
;;TODO accessories
|
||||
:options {:topBar {:visible false}}
|
||||
:component new-chat/new-chat}
|
||||
@ -569,7 +568,7 @@
|
||||
:component contact/nickname}
|
||||
|
||||
{:name :new-chat-aio
|
||||
:on-focus [::new-chat.events/new-chat-focus]
|
||||
:on-focus [:contacts/new-chat-focus]
|
||||
;;TODO accessories
|
||||
:options {:topBar {:visible false}}
|
||||
:component new-chat-aio/contact-selection-list}
|
||||
@ -595,13 +594,6 @@
|
||||
:component communities.invite/invite
|
||||
:insets {:bottom true}}
|
||||
|
||||
;New Contact
|
||||
{:name :new-contact
|
||||
:on-focus [::new-chat.events/new-chat-focus]
|
||||
;;TODO accessories
|
||||
:options {:topBar {:visible false}}
|
||||
:component new-chat/new-contact}
|
||||
|
||||
;[Wallet] Recipient
|
||||
{:name :recipient
|
||||
:insets {:bottom true}
|
||||
|
@ -53,7 +53,7 @@
|
||||
:on-change (fn [text]
|
||||
(re-frame/dispatch [:search/recipient-filter-changed text])
|
||||
(re-frame/dispatch [:set-in [:contacts/new-identity :state] :searching])
|
||||
(debounce/debounce-and-dispatch [:new-chat/set-new-identity text] 300))}]]))
|
||||
(debounce/debounce-and-dispatch [:contacts/set-new-identity text] 300))}]]))
|
||||
|
||||
(defn section
|
||||
[_ _ _]
|
||||
|
@ -1,5 +0,0 @@
|
||||
(ns status-im.utils.db)
|
||||
|
||||
(defn valid-public-key?
|
||||
[s]
|
||||
(and (string? s) (not-empty s) (boolean (re-matches #"0x04[0-9a-f]{128}" s))))
|
96
src/status_im2/contexts/add_new_contact/events.cljs
Normal file
96
src/status_im2/contexts/add_new_contact/events.cljs
Normal file
@ -0,0 +1,96 @@
|
||||
(ns status-im2.contexts.add-new-contact.events
|
||||
(:require [utils.re-frame :as rf]
|
||||
[status-im.utils.types :as types]
|
||||
[re-frame.core :as re-frame]
|
||||
[status-im.ethereum.core :as ethereum]
|
||||
[status-im.ethereum.ens :as ens]
|
||||
[status-im.ethereum.stateofus :as stateofus]
|
||||
[status-im.native-module.core :as status]
|
||||
[status-im2.utils.validators :as validators]
|
||||
[status-im.utils.utils :as utils]))
|
||||
|
||||
(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)))
|
||||
|
||||
(re-frame/reg-fx
|
||||
:contacts/decompress-public-key
|
||||
(fn [{:keys [public-key on-success on-error]}]
|
||||
(status/compressed-key->public-key
|
||||
public-key
|
||||
(fn [resp]
|
||||
(let [{:keys [error]} (types/json->clj resp)]
|
||||
(if error
|
||||
(on-error error)
|
||||
(on-success
|
||||
(str "0x" (subs resp 5)))))))))
|
||||
|
||||
(rf/defn set-new-identity
|
||||
{:events [:contacts/set-new-identity]}
|
||||
[{:keys [db]} input]
|
||||
(let [input (utils/safe-trim input)
|
||||
public-key? (validators/valid-public-key? input)
|
||||
compressed-key? (validators/valid-compressed-key? input)
|
||||
ens-name (if compressed-key? nil (stateofus/ens-name-parse input))
|
||||
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]))]
|
||||
(cond
|
||||
(empty? input) {:db (dissoc db :contacts/new-identity)}
|
||||
public-key? {:db (assoc db
|
||||
:contacts/new-identity
|
||||
{:input input
|
||||
:public-key input
|
||||
:ens-name nil
|
||||
:state :error
|
||||
:error :uncompressed-key})}
|
||||
:else
|
||||
(cond->
|
||||
{:db (assoc db
|
||||
:contacts/new-identity
|
||||
{:input input
|
||||
:public-key nil
|
||||
:ens-name nil
|
||||
:state :searching
|
||||
:error nil})}
|
||||
compressed-key? (assoc :contacts/decompress-public-key
|
||||
{:public-key input
|
||||
:on-success on-success
|
||||
:on-error on-error})
|
||||
(not compressed-key?) (assoc :contacts/resolve-public-key-from-ens-name
|
||||
{:chain-id (ethereum/chain-id db)
|
||||
:ens-name ens-name
|
||||
:on-success on-success
|
||||
:on-error on-error})))))
|
||||
|
||||
(rf/defn set-new-identity-success
|
||||
{:events [:contacts/set-new-identity-success]}
|
||||
[{:keys [db]} input ens-name pubkey]
|
||||
{:db (assoc db
|
||||
:contacts/new-identity
|
||||
{:input input
|
||||
:public-key pubkey
|
||||
:ens-name ens-name
|
||||
:state :valid
|
||||
:error nil})})
|
||||
|
||||
(rf/defn set-new-identity-error
|
||||
{:events [:contacts/set-new-identity-error]}
|
||||
[{:keys [db]} error input]
|
||||
{:db (assoc db
|
||||
:contacts/new-identity
|
||||
{:input input
|
||||
:public-key nil
|
||||
:ens-name nil
|
||||
:state :error
|
||||
:error :invalid})})
|
||||
|
||||
(rf/defn clear-new-identity
|
||||
{:events [:contacts/clear-new-identity :contacts/new-chat-focus]}
|
||||
[{:keys [db]}]
|
||||
{:db (dissoc db :contacts/new-identity)})
|
53
src/status_im2/contexts/add_new_contact/events_test.cljs
Normal file
53
src/status_im2/contexts/add_new_contact/events_test.cljs
Normal file
@ -0,0 +1,53 @@
|
||||
(ns status-im2.contexts.add-new-contact.events-test
|
||||
(:require [cljs.test :refer-macros [deftest is]]
|
||||
[status-im2.contexts.add-new-contact.events :as core]))
|
||||
|
||||
(def init-db
|
||||
{:networks/current-network "mainnet_rpc"
|
||||
:networks/networks {"mainnet_rpc"
|
||||
{:id "mainnet_rpc"
|
||||
:config {:NetworkId 1}}}})
|
||||
|
||||
(defn search-db
|
||||
[input]
|
||||
{:contacts/new-identity {:input input
|
||||
:public-key nil
|
||||
:ens-name nil
|
||||
:state :searching
|
||||
:error nil}})
|
||||
|
||||
(deftest search-empty-string
|
||||
(let [input ""
|
||||
expected {:db init-db}
|
||||
actual (core/set-new-identity {:db init-db} input)]
|
||||
(is (= actual expected))))
|
||||
|
||||
(deftest search-ens
|
||||
(let [input "esep"
|
||||
clean (fn [db]
|
||||
(-> db
|
||||
(assoc-in [:contacts/resolve-public-key-from-ens-name :on-success] nil)
|
||||
(assoc-in [:contacts/resolve-public-key-from-ens-name :on-error] nil)))
|
||||
expected {:db (merge init-db (search-db input))
|
||||
:contacts/resolve-public-key-from-ens-name
|
||||
{:chain-id 1
|
||||
:ens-name (str input ".stateofus.eth")
|
||||
:on-success nil
|
||||
:on-error nil}}
|
||||
actual (core/set-new-identity {:db init-db} input)]
|
||||
(is (= (clean actual) expected))))
|
||||
|
||||
(deftest search-compressed-key
|
||||
(let [input "zQ3shWj4WaBdf2zYKCkXe6PHxDxNTzZyid1i75879Ue9cX9gA"
|
||||
clean (fn [db]
|
||||
(-> db
|
||||
(assoc-in [:contacts/decompress-public-key :on-success] nil)
|
||||
(assoc-in [:contacts/decompress-public-key :on-error] nil)))
|
||||
expected {:db (merge init-db (search-db input))
|
||||
:contacts/decompress-public-key
|
||||
{:public-key input
|
||||
:on-success nil
|
||||
:on-error nil}}
|
||||
actual (core/set-new-identity {:db init-db} input)]
|
||||
(is (= (clean actual) expected))))
|
||||
|
138
src/status_im2/contexts/add_new_contact/style.cljs
Normal file
138
src/status_im2/contexts/add_new_contact/style.cljs
Normal file
@ -0,0 +1,138 @@
|
||||
(ns status-im2.contexts.add-new-contact.style
|
||||
(:require [quo2.foundations.colors :as colors]
|
||||
[status-im.react-native.resources :as resources]
|
||||
[react-native.platform :as platform]
|
||||
[quo2.foundations.typography :as typography]))
|
||||
|
||||
(defn container-kbd
|
||||
[]
|
||||
{:style {:flex 1}
|
||||
:keyboardVerticalOffset 60})
|
||||
|
||||
(def container-image
|
||||
{:style {:flex 1
|
||||
:flex-direction :row}})
|
||||
|
||||
(defn image
|
||||
[file]
|
||||
{:source (resources/get-image file)
|
||||
:style {:flex 1}})
|
||||
|
||||
(defn container-outer
|
||||
[]
|
||||
{:style {:flex (if platform/ios? 4.5 5)
|
||||
:background-color (colors/theme-colors colors/white colors/neutral-95)
|
||||
:justify-content :space-between
|
||||
:align-items :center
|
||||
:padding 16
|
||||
:border-radius 20}})
|
||||
|
||||
(def container-inner
|
||||
{:style {:flex-direction :column
|
||||
:align-items :flex-start}})
|
||||
|
||||
(def container-text-input
|
||||
{:style {:flex-direction :row
|
||||
:justify-content :space-between}})
|
||||
|
||||
(def container-error
|
||||
{:style {:flex-direction :row
|
||||
:align-items :center
|
||||
:margin-top 8}})
|
||||
|
||||
(defn text-title
|
||||
[]
|
||||
{:size :heading-1
|
||||
:weight :semi-bold
|
||||
:style {:margin-bottom 6
|
||||
:color (colors/theme-colors
|
||||
colors/neutral-100
|
||||
colors/white)}})
|
||||
|
||||
(defn text-subtitle
|
||||
[]
|
||||
{:size :paragraph-1
|
||||
:weight :regular
|
||||
:style {:margin-bottom 20
|
||||
:color (colors/theme-colors
|
||||
colors/neutral-100
|
||||
colors/white)}})
|
||||
|
||||
(defn text-description
|
||||
[]
|
||||
{:size :paragraph-2
|
||||
:weight :medium
|
||||
:style {:margin-bottom 6
|
||||
:color (colors/theme-colors
|
||||
colors/neutral-50
|
||||
colors/neutral-40)}})
|
||||
|
||||
(def icon-error
|
||||
{:size 16
|
||||
:color colors/danger-50})
|
||||
|
||||
(def text-error
|
||||
{:size :paragraph-2
|
||||
:align :left
|
||||
:style {:margin-left 4
|
||||
:color colors/danger-50}})
|
||||
|
||||
(defn text-input
|
||||
[error?]
|
||||
{:accessibility-label :enter-contact-code-input
|
||||
:auto-capitalize :none
|
||||
:placeholder-text-color (colors/theme-colors
|
||||
colors/neutral-40
|
||||
colors/neutral-50)
|
||||
:multiline true
|
||||
:style
|
||||
(merge typography/monospace
|
||||
typography/paragraph-1
|
||||
{:padding-top 8
|
||||
:padding-left 12
|
||||
:padding-right 12
|
||||
:padding-bottom 8
|
||||
:margin-right 10
|
||||
:flex 1
|
||||
:color (colors/theme-colors
|
||||
colors/black
|
||||
colors/white)
|
||||
:background-color (colors/theme-colors
|
||||
colors/white
|
||||
colors/neutral-95)
|
||||
:border-width 1
|
||||
:border-radius 12
|
||||
:border-color (if error?
|
||||
colors/danger-50-opa-40
|
||||
(colors/theme-colors
|
||||
colors/neutral-20
|
||||
colors/neutral-80))})})
|
||||
|
||||
(defn button-close
|
||||
[]
|
||||
{:type :grey
|
||||
:icon true
|
||||
:accessibility-label :new-contact-close-button
|
||||
:size 32
|
||||
:override-background-color (colors/theme-colors
|
||||
colors/white-opa-60
|
||||
colors/neutral-80-opa-60)
|
||||
:style {:position :absolute
|
||||
:left 20
|
||||
:top 20}})
|
||||
|
||||
(def button-qr
|
||||
{:type :outline
|
||||
:icon true
|
||||
:size 40})
|
||||
|
||||
(defn button-view-profile
|
||||
[state]
|
||||
{:type :primary
|
||||
:size 40
|
||||
:width 335
|
||||
:style {:margin-top 24
|
||||
:margin-bottom 24}
|
||||
:accessibility-label :new-contact-button
|
||||
:before :i/profile
|
||||
:disabled (not= state :valid)})
|
56
src/status_im2/contexts/add_new_contact/views.cljs
Normal file
56
src/status_im2/contexts/add_new_contact/views.cljs
Normal file
@ -0,0 +1,56 @@
|
||||
(ns status-im2.contexts.add-new-contact.views
|
||||
(:require [status-im2.contexts.add-new-contact.style :as style]
|
||||
[utils.i18n :as i18n]
|
||||
[utils.debounce :as debounce]
|
||||
[utils.re-frame :as rf]
|
||||
[react-native.core :as rn]
|
||||
[quo2.core :as quo]))
|
||||
|
||||
(defn new-contact
|
||||
[]
|
||||
(let [{:keys [input public-key state error]} (rf/sub
|
||||
[:contacts/new-identity])
|
||||
error? (and (= state :error)
|
||||
(= error :uncompressed-key))]
|
||||
[rn/keyboard-avoiding-view (style/container-kbd)
|
||||
[rn/view style/container-image
|
||||
[rn/image (style/image :add-new-contact)]
|
||||
[quo/button
|
||||
(merge (style/button-close)
|
||||
{:on-press
|
||||
(fn []
|
||||
(rf/dispatch [:contacts/clear-new-identity])
|
||||
(rf/dispatch [:navigate-back]))}) :i/close]]
|
||||
[rn/view (style/container-outer)
|
||||
[rn/view style/container-inner
|
||||
[quo/text (style/text-title)
|
||||
(i18n/label :t/add-a-contact)]
|
||||
[quo/text (style/text-subtitle)
|
||||
(i18n/label :t/find-your-friends)]
|
||||
[quo/text (style/text-description)
|
||||
(i18n/label :t/ens-or-chat-key)]
|
||||
[rn/view style/container-text-input
|
||||
[rn/text-input
|
||||
(merge (style/text-input error?)
|
||||
{:default-value input
|
||||
:placeholder (i18n/label :t/type-some-chat-key)
|
||||
:on-change-text #(debounce/debounce-and-dispatch
|
||||
[:contacts/set-new-identity %]
|
||||
600)})]
|
||||
[quo/button
|
||||
(merge style/button-qr
|
||||
{:on-press #(js/alert "TODO: to be implemented")})
|
||||
: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)]])]
|
||||
[rn/view
|
||||
[quo/button
|
||||
(merge (style/button-view-profile state)
|
||||
{:on-press
|
||||
(fn []
|
||||
(rf/dispatch [:contacts/clear-new-identity])
|
||||
(rf/dispatch [:navigate-back])
|
||||
(rf/dispatch [:chat.ui/show-profile public-key]))})
|
||||
(i18n/label :t/view-profile)]]]]))
|
@ -9,6 +9,7 @@
|
||||
[status-im2.common.json-rpc.events]
|
||||
[status-im2.common.theme.core :as theme]
|
||||
[status-im2.common.toasts.events]
|
||||
[status-im2.contexts.add-new-contact.events]
|
||||
[status-im2.navigation.events :as navigation]
|
||||
[status-im2.db :as db]
|
||||
[utils.re-frame :as rf]
|
||||
|
@ -3,6 +3,7 @@
|
||||
[status-im.ui.screens.screens :as old-screens]
|
||||
[status-im2.contexts.activity-center.view :as activity-center]
|
||||
[status-im2.contexts.chat.messages.view :as chat]
|
||||
[status-im2.contexts.add-new-contact.views :as add-new-contact]
|
||||
[status-im2.contexts.communities.discover.view :as communities.discover]
|
||||
[status-im2.contexts.communities.overview.view :as communities.overview]
|
||||
[status-im2.contexts.quo-preview.main :as quo.preview]
|
||||
@ -47,6 +48,10 @@
|
||||
:decelerate}}]}}}
|
||||
:component images-horizontal/images-horizontal}
|
||||
|
||||
{:name :new-contact
|
||||
:options {:topBar {:visible false}}
|
||||
:component add-new-contact/new-contact}
|
||||
|
||||
{:name :discover-communities
|
||||
:options {:topBar {:visible false}}
|
||||
:component communities.discover/discover}
|
||||
|
14
src/status_im2/utils/validators.cljs
Normal file
14
src/status_im2/utils/validators.cljs
Normal file
@ -0,0 +1,14 @@
|
||||
(ns status-im2.utils.validators)
|
||||
|
||||
(defn valid-public-key?
|
||||
[s]
|
||||
(and (string? s)
|
||||
(not-empty s)
|
||||
(boolean (re-matches #"0x04[0-9a-f]{128}" s))))
|
||||
|
||||
(defn valid-compressed-key?
|
||||
[s]
|
||||
(and (string? s)
|
||||
(not-empty s)
|
||||
(boolean (re-matches #"^zQ3[0-9A-HJ-NP-Za-km-z]{46}" s))))
|
||||
|
33
src/status_im2/utils/validators_test.cljs
Normal file
33
src/status_im2/utils/validators_test.cljs
Normal file
@ -0,0 +1,33 @@
|
||||
(ns status-im2.utils.validators-test
|
||||
(:require [cljs.test :refer-macros [deftest testing is]]
|
||||
[status-im2.utils.validators :refer [valid-compressed-key?]]))
|
||||
|
||||
(deftest test-valid-compressed-key
|
||||
(testing "valid"
|
||||
(is (valid-compressed-key?
|
||||
"zQ3shWj4WaBdf2zYKCkXe6PHxDxNTzZyid1i75879Ue9cX9gA")))
|
||||
(testing "uncompressed key"
|
||||
(is
|
||||
(not
|
||||
(valid-compressed-key?
|
||||
"0x048a6773339d11ccf5fd81677b7e54daeec544a1287bd92b725047ad6faa9a9b9f8ea86ed5a226d2a994f5f46d0b43321fd8de7b7997a166e67905c8c73cd37ce"))))
|
||||
(testing "nil"
|
||||
(is (not (valid-compressed-key? nil))))
|
||||
(testing "empty string"
|
||||
(is (not (valid-compressed-key? ""))))
|
||||
(testing "too short"
|
||||
(is (not (valid-compressed-key? "zQ3FGR5y6U5a6"))))
|
||||
(testing "too long"
|
||||
(is (not (valid-compressed-key?
|
||||
"zQ3shWj4WaBdf2zYKCkXe6PHxDxNTzZyid1i75879Ue9cX9gA2"))))
|
||||
(testing "0x prefix"
|
||||
(is (not (valid-compressed-key? "0xFGR5y6U5a6"))))
|
||||
(testing "contains I"
|
||||
(is (not (valid-compressed-key?
|
||||
"zQ3shWj4WaBdf2zYKCkXe6PHxDxNTzZyid1i75879Ue9cX9gI"))))
|
||||
(testing "contains O"
|
||||
(is (not (valid-compressed-key?
|
||||
"zQ3shWj4WaBdf2zYKCkXe6PHxDxNTzZyid1i75879Ue9cX9gO"))))
|
||||
(testing "contains l"
|
||||
(is (not (valid-compressed-key?
|
||||
"zQ3shWj4WaBdf2zYKCkXe6PHxDxNTzZyid1i75879Ue9cX9gl")))))
|
@ -570,6 +570,7 @@
|
||||
"skip": "Skip",
|
||||
"password-placeholder": "Password...",
|
||||
"confirm-password-placeholder": "Confirm your password...",
|
||||
"ens-or-chat-key": "ENS or Chat key",
|
||||
"enter-pin": "Enter 6-digit passcode",
|
||||
"enter-puk-code": "Enter PUK code",
|
||||
"enter-puk-code-description": "6-digit passcode has been blocked.\n Please enter PUK code to unblock passcode.",
|
||||
@ -597,6 +598,7 @@
|
||||
"fetch-messages": "Fetch messages",
|
||||
"fetch-timeline": "↓ Fetch",
|
||||
"find": "Find",
|
||||
"find-your-friends": "Find your friends with their ENS or Chatkey",
|
||||
"finish": "Finish",
|
||||
"finishing-card-setup": "Finishing card setup",
|
||||
"fleet": "Fleet",
|
||||
@ -986,6 +988,7 @@
|
||||
"node-version": "Node version",
|
||||
"nonce": "Nonce",
|
||||
"none": "None",
|
||||
"not-a-chatkey": "This is not a chatkey",
|
||||
"not-applicable": "Not applicable for unsigned transactions",
|
||||
"not-keycard-text": "The card you used is not a Keycard. You need to purchase a Keycard to use it",
|
||||
"not-keycard-title": "Not a Keycard",
|
||||
@ -1871,6 +1874,7 @@
|
||||
"identity-verification-request": "Identity verification",
|
||||
"identity-verification-request-sent": "asks",
|
||||
"type-something": "Type something",
|
||||
"type-some-chat-key": "0x123abc",
|
||||
"your-answer": "Your answer",
|
||||
"membership": "Membership",
|
||||
"jump-to": "Jump to",
|
||||
|
Loading…
x
Reference in New Issue
Block a user