Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eabe4e657f |
+19
@@ -940,6 +940,25 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL
|
||||
StatusThreadPoolExecutor.getInstance().execute(r);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void multiformatDeserializePublicKey(final String key, final String outBase, final Callback callback) {
|
||||
Log.d(TAG, "multiformatDeserializePublicKey");
|
||||
if (!checkAvailability()) {
|
||||
callback.invoke(false);
|
||||
return;
|
||||
}
|
||||
|
||||
Runnable r = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
String res = Statusgo.multiformatDeserializePublicKey(message, outBase);
|
||||
callback.invoke(res);
|
||||
}
|
||||
};
|
||||
|
||||
StatusThreadPoolExecutor.getInstance().execute(r);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void getConnectionStringForBootstrappingAnotherDevice(final String configJSON, final Callback callback) throws JSONException {
|
||||
final JSONObject jsonConfig = new JSONObject(configJSON);
|
||||
|
||||
@@ -1873,7 +1873,6 @@ void _ConnectionChange(const FunctionCallbackInfo<Value>& args) {
|
||||
ConnectionChange(arg0, arg1);
|
||||
}
|
||||
|
||||
|
||||
void init(Local<Object> exports) {
|
||||
NODE_SET_METHOD(exports, "multiAccountGenerateAndDeriveAddresses", _MultiAccountGenerateAndDeriveAddresses);
|
||||
NODE_SET_METHOD(exports, "multiAccountImportPrivateKey", _MultiAccountImportPrivateKey);
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 112 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 192 KiB |
+106
-27
@@ -1,5 +1,6 @@
|
||||
(ns status-im.add-new.core
|
||||
(:require [clojure.string :as string]
|
||||
[utils.re-frame :as rf]
|
||||
[re-frame.core :as re-frame]
|
||||
[status-im.add-new.db :as db]
|
||||
[status-im.chat.models :as chat]
|
||||
@@ -7,59 +8,136 @@
|
||||
[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-im.i18n.i18n :as i18n]
|
||||
[status-im.router.core :as router]
|
||||
[status-im.utils.db :as utils.db]
|
||||
[utils.re-frame :as rf]
|
||||
[status-im.utils.random :as random]
|
||||
[status-im.utils.utils :as utils]
|
||||
[status-im2.navigation.events :as navigation]))
|
||||
|
||||
;; (re-frame/reg-fx
|
||||
;; :multiaccount-generate-and-derive-addresses
|
||||
;; (fn []
|
||||
;; (status/multiaccount-generate-and-derive-addresses
|
||||
;; 5
|
||||
;; 12
|
||||
;; [constants/path-whisper
|
||||
;; constants/path-wallet-root
|
||||
;; constants/path-default-wallet]
|
||||
;; #(re-frame/dispatch [:multiaccount-generate-and-derive-addresses-success
|
||||
;; (mapv normalize-multiaccount-data-keys
|
||||
;; (types/json->clj %))]))))
|
||||
|
||||
;; TODO(esep): this seems like it should be an 'ens_' method, but maybe not... not sure
|
||||
;; (defn compressed-public-key
|
||||
;; [chain-id ens-name cb]
|
||||
;; (json-rpc/call {:method "ens_compressedPublicKeyOf"
|
||||
;; :params [chain-id ens-name]
|
||||
;; :on-success cb
|
||||
;; :on-error #(cb "0x")}))
|
||||
|
||||
(re-frame/reg-fx
|
||||
:resolve-public-key
|
||||
(fn [{:keys [chain-id contact-identity cb]}]
|
||||
(let [ens-name (stateofus/ens-name-parse contact-identity)]
|
||||
(println "ens/pubkey:" chain-id ens-name cb)
|
||||
(ens/pubkey chain-id ens-name cb))))
|
||||
|
||||
(re-frame/reg-fx
|
||||
:resolve-compressed-public-key
|
||||
(fn [compressed-public-key]
|
||||
(println "resolve-pubkey:" compressed-public-key)
|
||||
(status/decompress-public-key compressed-public-key #()
|
||||
;; #(re-frame/dispatch [:multiaccount-generate-and-derive-addresses-success
|
||||
;; (mapv normalize-multiaccount-data-keys
|
||||
;; (types/json->clj %))])
|
||||
)))
|
||||
|
||||
(comment
|
||||
(rf/dispatch [:resolve-public-key "0x04351fb713ee1fd8b15cc70918ccb852176f6f4068af9928051d2f78148cbd850fa6f72550142f2c8195082799d9ef90b226cd4183c67e0ad56c657c211cd83ed6"])
|
||||
(rf/dispatch [:resolve-compressed-public-key "zQ3shwiRy5TtMM7B4iZ8MKaGywkaaCgMqqbrnAUYrZJ1sgVWh"])
|
||||
)
|
||||
|
||||
;; (fx/defn set-new-identity
|
||||
;; {:events [:new-chat/set-new-identity]}
|
||||
;; [{:keys [db] :as cofx} id ens]
|
||||
;; (println cofx id ens)
|
||||
;; (status/decompress-public-key
|
||||
;; id #()
|
||||
;; ;; #(re-frame/dispatch [:multiaccount-generate-and-derive-addresses-success
|
||||
;; ;; (mapv normalize-multiaccount-data-keys
|
||||
;; ;; (types/json->clj %))])
|
||||
;; ))
|
||||
|
||||
|
||||
;;NOTE we want to handle only last resolve
|
||||
(def resolve-last-id (atom nil))
|
||||
|
||||
(def x (atom {}))
|
||||
(def db2 (atom nil))
|
||||
;; WEIRD: if user types 'z', it sends z nil nil
|
||||
(rf/defn new-chat-set-new-identity
|
||||
{:events [:new-chat/set-new-identity]}
|
||||
[{db :db} new-identity-raw new-ens-name id]
|
||||
(let [ens-error (and (= new-identity-raw "0x") (not (string/blank? new-ens-name)))]
|
||||
(println "-----------------------")
|
||||
(println "id:" @resolve-last-id)
|
||||
(println new-identity-raw new-ens-name id)
|
||||
;; if we're saying it's an ENS, it should not start with 0x (ENS-ERROR!)
|
||||
(let [ens-error (and (= new-identity-raw "0x")
|
||||
(not (string/blank? new-ens-name)))]
|
||||
;; proceed only if:
|
||||
;; 1) no id (we haven't attempted to resolve it yet) OR
|
||||
;; 2) id is equal resolve-last-id (we just attempted to resolve it)
|
||||
(when (or (not id) (= id @resolve-last-id))
|
||||
(println "ens-error:" ens-error)
|
||||
(if ens-error
|
||||
{:db (assoc-in db [:contacts/new-identity :state] :error)}
|
||||
;; otherwise proceed...
|
||||
(let [new-identity (utils/safe-trim new-identity-raw)
|
||||
is-public-key? (and (string? new-identity)
|
||||
(utils.db/valid-public-key? new-identity))
|
||||
;; this checks against a public key regex ("0x04...{128}")
|
||||
is-public-key? (utils.db/valid-public-key? new-identity)
|
||||
;; if NOT 0x AND looks like an ens
|
||||
is-ens? (and (not is-public-key?)
|
||||
;; is not blank, does not end with '.',
|
||||
;; does not include '..'
|
||||
(ens/valid-eth-name-prefix? new-identity))
|
||||
error (db/validate-pub-key db new-identity)]
|
||||
(reset! resolve-last-id nil)
|
||||
(merge {:db (assoc db
|
||||
:contacts/new-identity
|
||||
{:public-key new-identity
|
||||
:state (cond is-ens?
|
||||
:searching
|
||||
(and (string/blank? new-identity) (not new-ens-name))
|
||||
:empty
|
||||
error
|
||||
:error
|
||||
:else
|
||||
:valid)
|
||||
_ (println "is-ens?" is-ens?)
|
||||
error (db/validate-pub-key db new-identity)
|
||||
_ (println "error:" error)
|
||||
_ (reset! resolve-last-id nil) ; init before
|
||||
a {:public-key new-identity
|
||||
:state (cond is-ens? :searching
|
||||
(and
|
||||
(string/blank? new-identity)
|
||||
(not new-ens-name)) :empty
|
||||
error :error
|
||||
:else :valid)
|
||||
:error error
|
||||
:ens-name (stateofus/ens-name-parse new-ens-name)})}
|
||||
(when is-ens?
|
||||
(reset! resolve-last-id (random/id))
|
||||
{:resolve-public-key
|
||||
{:chain-id (ethereum/chain-id db)
|
||||
:contact-identity new-identity
|
||||
:cb #(re-frame/dispatch [:new-chat/set-new-identity
|
||||
%
|
||||
new-identity
|
||||
@resolve-last-id])}})))))))
|
||||
:ens-name (stateofus/ens-name-parse
|
||||
new-ens-name)}
|
||||
b {:resolve-public-key ; -> ens_publicKeyOf
|
||||
{:chain-id (ethereum/chain-id db)
|
||||
:contact-identity new-identity
|
||||
:cb #(rf/dispatch
|
||||
;; loop: recurse this fn
|
||||
[:new-chat/set-new-identity %
|
||||
;; % is val returned
|
||||
new-identity
|
||||
@resolve-last-id])}}
|
||||
out (merge {:db (assoc db
|
||||
;; just store a map in db,
|
||||
;; no actions taken
|
||||
:contacts/new-identity a)}
|
||||
(when is-ens?
|
||||
;; generate a new id on every call!
|
||||
(reset! resolve-last-id (random/id))
|
||||
;; {:resolve-compressed-public-key new-identity}
|
||||
b))
|
||||
_ (reset! x {:a a :b b})
|
||||
_ (reset! db2 db)
|
||||
;; _ (println out)
|
||||
] out)))))
|
||||
|
||||
(rf/defn clear-new-identity
|
||||
{:events [::clear-new-identity ::new-chat-focus]}
|
||||
@@ -100,3 +178,4 @@
|
||||
:chats (get db :chats)
|
||||
:uri data
|
||||
:cb #(re-frame/dispatch [::qr-code-handled % opts])}})
|
||||
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
(ns status-im.add-new.core-test
|
||||
(:require [cljs.test :refer-macros [deftest testing is run-tests]]
|
||||
[utils.re-frame :as rf]
|
||||
[status-im.add-new.core :as core]))
|
||||
|
||||
;; As a user
|
||||
;; I would like to add a contact by typing their chat key
|
||||
;; I would like to add a contact by typing their ENS
|
||||
;; Description
|
||||
|
||||
;; We should only support compressed keys, and
|
||||
;; https://join.status.im/u/{key/ens-name} links.
|
||||
|
||||
;; If the key is valid, or the ENS name resolves, we should enable the View
|
||||
;; Profile button, which if clicked, will take you to the user's profile.
|
||||
|
||||
;; initial db values
|
||||
(def init-db {:networks/current-network "mainnet_rpc"
|
||||
:networks/networks {"mainnet_rpc"
|
||||
{:id "mainnet_rpc"
|
||||
:config {:NetworkId 1}}}})
|
||||
|
||||
;; we can't compare callback fn objects, so remove it:
|
||||
(defn clean [db] (assoc-in db [:resolve-public-key :cb] nil))
|
||||
|
||||
;; ;; NOTE: :resolve-public-key is not executed by these tests - they are
|
||||
;; ;; executing the fn directly, instead of through re-frame
|
||||
|
||||
;; the first call: a user types "e" for "ENS or chatkey"
|
||||
(deftest first-call ; -> searching
|
||||
(let [inputs {:new-identity-raw "e"
|
||||
:new-ens-name nil
|
||||
:id nil}
|
||||
text (:new-identity-raw inputs)
|
||||
;; the code-under-test will add these to the db
|
||||
add-to-db {:contacts/new-identity {:public-key text
|
||||
:state :searching
|
||||
:error :invalid
|
||||
:ens-name nil}}
|
||||
;; the expected db after code-under-test completes
|
||||
expected {:db (merge init-db add-to-db)
|
||||
:resolve-public-key {:chain-id 1
|
||||
:contact-identity text
|
||||
:cb nil}}
|
||||
;; the actual return value of code-under-test
|
||||
actual (core/new-chat-set-new-identity
|
||||
{:db init-db} text nil nil)]
|
||||
(is (= (clean actual) expected))))
|
||||
|
||||
;; the second call is not user-triggered; it would be a recursion from first-call.
|
||||
(deftest second-call ; -> error
|
||||
(let [inputs {:new-identity-raw "0x"
|
||||
:new-ens-name "e"
|
||||
:id "1671911236203-4ba66785-ed56-523a-83dc-4cd4cdd7e3af"}
|
||||
text (:new-identity-raw inputs)
|
||||
ens (:new-ens-name inputs)
|
||||
id (:id inputs)
|
||||
add-to-db {:contacts/new-identity {:state :error}}
|
||||
expected {:db (merge init-db add-to-db)}
|
||||
_ (reset! core/resolve-last-id id)
|
||||
actual (core/new-chat-set-new-identity
|
||||
{:db init-db} text ens id)]
|
||||
(is (= actual expected))))
|
||||
|
||||
;; the third call is when a user deletes the previously-entered text
|
||||
(deftest third-call ; -> invalid
|
||||
(let [inputs {:new-identity-raw ""
|
||||
:new-ens-name nil
|
||||
:id nil}
|
||||
text (:new-identity-raw inputs)
|
||||
ens (:new-ens-name inputs)
|
||||
id (:id inputs)
|
||||
last-id "1671911236203-4ba66785-ed56-523a-83dc-4cd4cdd7e3af"
|
||||
add-to-db {:contacts/new-identity {:public-key text
|
||||
:state :empty
|
||||
:error :invalid
|
||||
:ens-name nil}}
|
||||
expected {:db (merge init-db add-to-db)}
|
||||
_ (reset! core/resolve-last-id last-id) ; previous id exists
|
||||
actual (core/new-chat-set-new-identity
|
||||
{:db init-db} text ens id)]
|
||||
(is (= actual expected))))
|
||||
|
||||
(deftest fourth-call ; -> valid
|
||||
(let [inputs {:new-identity-raw "0x048a6773339d11ccf5fd81677b7e54daeec544a1287bd92b725047ad6faa9a9b9f8ea86ed5a226d2a994f5f46d0b43321fd8de7b7997a166e67905c8c73cd37cea"
|
||||
:new-ens-name "esep"
|
||||
:id "1671917792586-38d2c414-8a94-501d-83f5-11267655bdb3"}
|
||||
text (:new-identity-raw inputs)
|
||||
ens (:new-ens-name inputs)
|
||||
id (:id inputs)
|
||||
add-to-db {:contacts/new-identity {:public-key text
|
||||
:state :valid
|
||||
:error nil
|
||||
:ens-name "esep.stateofus.eth"}}
|
||||
expected {:db (merge init-db add-to-db)}
|
||||
_ (reset! core/resolve-last-id id) ; same id
|
||||
actual (core/new-chat-set-new-identity
|
||||
{:db init-db} text ens id)]
|
||||
;; (println "actual:" actual)
|
||||
;; (println "expected:" expected)
|
||||
(is (= actual expected))))
|
||||
|
||||
(deftest fifth-call ; -> valid
|
||||
(let [inputs {:new-identity-raw "0x048a6773339d11ccf5fd81677b7e54daeec544a1287bd92b725047ad6faa9a9b9f8ea86ed5a226d2a994f5f46d0b43321fd8de7b7997a166e67905c8c73cd37cea"
|
||||
:new-ens-name "esep"
|
||||
:id "1671917792586-38d2c414-8a94-501d-83f5-11267655bdb3"}
|
||||
text (:new-identity-raw inputs)
|
||||
ens (:new-ens-name inputs)
|
||||
id (:id inputs)
|
||||
add-to-db {:contacts/new-identity {:public-key text
|
||||
:state :valid
|
||||
:error nil
|
||||
:ens-name "esep.stateofus.eth"}}
|
||||
expected {:db (merge init-db add-to-db)}
|
||||
_ (reset! core/resolve-last-id id) ; same id
|
||||
actual (core/new-chat-set-new-identity
|
||||
{:db init-db} text ens id)]
|
||||
;; (println "actual:" actual)
|
||||
;; (println "expected:" expected)
|
||||
(is (= actual expected))))
|
||||
|
||||
(comment
|
||||
(run-tests))
|
||||
@@ -0,0 +1,102 @@
|
||||
(ns status-im.automation
|
||||
(:require [re-frame.core :as rf :refer [dispatch] :rename {dispatch d}]
|
||||
[utils.security.core :as security]))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; recover accounts
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
;; Stimulating Darkseagreen Frenchbulldog
|
||||
;; chat-key
|
||||
;; 0x04259170489c0317ae326447d92b48051e103960e5a8d6470840628856b799559b9f6d5f89d86d78459783d9e832c3f154d6fad8d2a1ed84636927230d9b9392d0
|
||||
(d [:init-root :onboarding])
|
||||
(d [:multiaccounts.recover.ui/recover-multiaccount-button-pressed])
|
||||
(d [:status-im.multiaccounts.recover.core/enter-phrase-pressed])
|
||||
;; add :passphrase to :intro-wizard
|
||||
(d [:multiaccounts.recover/enter-phrase-input-changed
|
||||
(security/mask-data "bridge pride cake piece lucky host perfect require wild light attend click")])
|
||||
;; import multiaccount into :intro-wizard
|
||||
(d [:multiaccounts.recover/enter-phrase-next-pressed])
|
||||
;; change :step in :intro-wizard from :recovery-success to :create-code
|
||||
(d [:multiaccounts.recover/re-encrypt-pressed])
|
||||
;; store multiaccount to :db
|
||||
(d [:multiaccounts.recover/enter-password-next-pressed "testabc"])
|
||||
|
||||
;; Rural Electric Bass
|
||||
;; chat-key
|
||||
;; 0x041adb97f30458f5c5978f5ee82727f4d8f26f94dbfe23a43f59b938f41e3dec070e35dda4fd4315e244d45cb30f45b98ac0a9bcdd366b5102f3ecad6fe193f677
|
||||
(d [:init-root :onboarding])
|
||||
(d [:multiaccounts.recover.ui/recover-multiaccount-button-pressed])
|
||||
(d [:status-im.multiaccounts.recover.core/enter-phrase-pressed])
|
||||
(d [:multiaccounts.recover/enter-phrase-input-changed
|
||||
(security/mask-data "rose hood father immense claw school crisp goat replace gather razor slab")])
|
||||
(d [:multiaccounts.recover/enter-phrase-next-pressed])
|
||||
(d [:multiaccounts.recover/re-encrypt-pressed])
|
||||
(d [:multiaccounts.recover/enter-password-next-pressed "testabc"])
|
||||
|
||||
;; Deep Heavy Marlin
|
||||
;; chat-key
|
||||
;; 0x04351fb713ee1fd8b15cc70918ccb852176f6f4068af9928051d2f78148cbd850fa6f72550142f2c8195082799d9ef90b226cd4183c67e0ad56c657c211cd83ed6
|
||||
(d [:multiaccounts.recover.ui/recover-multiaccount-button-pressed])
|
||||
(d [:status-im.multiaccounts.recover.core/enter-phrase-pressed])
|
||||
(d [:multiaccounts.recover/enter-phrase-input-changed
|
||||
(security/mask-data
|
||||
"town asset resist supply unable slab eyebrow provide huge believe keen weather")])
|
||||
(d [:multiaccounts.recover/enter-phrase-next-pressed])
|
||||
(d [:multiaccounts.recover/re-encrypt-pressed])
|
||||
(d [:multiaccounts.recover/enter-password-next-pressed "testabc"])
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; 1-1 chat (bass -> bulldog)
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; (def chat-id
|
||||
;; "0x04259170489c0317ae326447d92b48051e103960e5a8d6470840628856b799559b9f6d5f89d86d78459783d9e832c3f154d6fad8d2a1ed84636927230d9b9392d0")
|
||||
(def chat-id
|
||||
"0x041adb97f30458f5c5978f5ee82727f4d8f26f94dbfe23a43f59b938f41e3dec070e35dda4fd4315e244d45cb30f45b98ac0a9bcdd366b5102f3ecad6fe193f677")
|
||||
(def chat-id
|
||||
"0x04351fb713ee1fd8b15cc70918ccb852176f6f4068af9928051d2f78148cbd850fa6f72550142f2c8195082799d9ef90b226cd4183c67e0ad56c657c211cd83ed6")
|
||||
(d [:chat.ui/start-chat chat-id])
|
||||
(d [:chat.ui/navigate-to-chat chat-id])
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; log into accounts
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
;; log into `bass` account
|
||||
(d [:multiaccounts.login.ui/multiaccount-selected
|
||||
"0x71473d9e9a22275372a7931af8c7cb2f74c8df74977a4f25fdcc61e7f1455b99"])
|
||||
(d [:set-in [:multiaccounts/login :password] "testabc"])
|
||||
(d [:multiaccounts.login.ui/password-input-submitted])
|
||||
|
||||
;; log into `bulldog` account
|
||||
(d [:multiaccounts.login.ui/multiaccount-selected
|
||||
"0xf994124ce1ce1444f36c17539b47fed056846b44f293ea3e20d566d11a04a2a2"])
|
||||
(d [:set-in [:multiaccounts/login :password] "testabc"])
|
||||
(d [:multiaccounts.login.ui/password-input-submitted])
|
||||
|
||||
;; log into `marlin` account
|
||||
(d [:multiaccounts.login.ui/multiaccount-selected
|
||||
"0x05b0db564fab9d0d38d87dd193926128f3e69723b50474fbbd6b3c6294699ae4"])
|
||||
(d [:set-in [:multiaccounts/login :password] "testabc"])
|
||||
(d [:multiaccounts.login.ui/password-input-submitted])
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; events
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(d [:logout])
|
||||
|
||||
(d [:bottom-sheet/show-sheet :add-new {}]) ;show bottom sheet
|
||||
(d [:bottom-sheet/hide]) ;hide bottom sheet
|
||||
(d [:open-modal :new-chat]) ;open new chat
|
||||
;; (d [:set-view-id :new-chat])
|
||||
;; (d [:screens/on-will-focus :new-chat])
|
||||
;; (d [:status-im.add-new.core/new-chat-focus])
|
||||
(d [:bottom-sheet/show-sheet])
|
||||
|
||||
;; just need these for development =>
|
||||
;; (d [:open-modal :new-chat2])
|
||||
(d [:open-modal :new-chat])
|
||||
(d [:navigate-back])
|
||||
|
||||
(d [:open-modal :new-contact])
|
||||
@@ -164,6 +164,8 @@
|
||||
|
||||
(defn get-current-network
|
||||
[{:networks/keys [current-network networks]}]
|
||||
;; (println networks current-network)
|
||||
;; (println (get networks current-network))
|
||||
(get networks current-network))
|
||||
|
||||
(defn chain-keyword
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
(defn is-valid-eth-name?
|
||||
[ens-name]
|
||||
(and ens-name
|
||||
(and ens-name ; what's this for? does it do anything?
|
||||
(string? ens-name)
|
||||
(string/ends-with? ens-name ".eth")))
|
||||
|
||||
@@ -36,7 +36,9 @@
|
||||
|
||||
(defn pubkey
|
||||
[chain-id ens-name cb]
|
||||
(println "is-valid-eth-name?" (is-valid-eth-name? ens-name))
|
||||
{:pre [(is-valid-eth-name? ens-name)]}
|
||||
(println "pubkey" chain-id ens-name cb)
|
||||
(json-rpc/call {:method "ens_publicKeyOf"
|
||||
:params [chain-id ens-name]
|
||||
:on-success cb
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
(ns status-im.ethereum.json-rpc
|
||||
(:require [clojure.string :as string]
|
||||
[re-frame.core :as re-frame]
|
||||
[status-im.native-module.core :as status]
|
||||
[status-im.utils.types :as types]
|
||||
[status-im.utils.utils :as utils]
|
||||
[taoensso.timbre :as log]))
|
||||
|
||||
;; NOTE(19/12/22 yqrashawn) this namespace has been moved to the
|
||||
;; status-im2.common.json-rpc.events namespace,
|
||||
;; we keep this only for old (status 1.0) code, can be removed with old code later
|
||||
(defn on-error-retry
|
||||
[call-method {:keys [method number-of-retries delay on-error] :as arg}]
|
||||
(if (pos? number-of-retries)
|
||||
(fn [error]
|
||||
(let [updated-delay (if delay
|
||||
(min 2000 (* 2 delay))
|
||||
50)]
|
||||
(log/debug "[on-error-retry]" method
|
||||
"number-of-retries" number-of-retries
|
||||
"delay" delay
|
||||
"error" error)
|
||||
(utils/set-timeout #(call-method (-> arg
|
||||
(update :number-of-retries dec)
|
||||
(assoc :delay updated-delay)))
|
||||
updated-delay)))
|
||||
on-error))
|
||||
|
||||
(defn call
|
||||
[{:keys [method params on-success on-error js-response] :as arg}]
|
||||
(let [params (or params [])
|
||||
on-error (or on-error
|
||||
(on-error-retry call arg)
|
||||
#(log/warn :json-rpc/error method :error % :params params))]
|
||||
(status/call-private-rpc
|
||||
(types/clj->json {:jsonrpc "2.0"
|
||||
:id 1
|
||||
:method method
|
||||
:params params})
|
||||
(fn [response]
|
||||
(if (string/blank? response)
|
||||
(on-error {:message "Blank response"})
|
||||
(let [response-js (types/json->js response)]
|
||||
(if (.-error response-js)
|
||||
(on-error (types/js->clj (.-error response-js)))
|
||||
(on-success (if js-response
|
||||
(.-result response-js)
|
||||
(types/js->clj (.-result response-js)))))))))))
|
||||
|
||||
(re-frame/reg-fx
|
||||
::call
|
||||
(fn [params]
|
||||
(doseq [param params]
|
||||
(call param))))
|
||||
@@ -261,6 +261,27 @@
|
||||
(log/debug "[native-module] hash-message")
|
||||
(.hashMessage ^js (status) message callback))
|
||||
|
||||
(comment
|
||||
(.hashMessage ^js (status) "hi!" #(println "back:" %))
|
||||
(println (status))
|
||||
(.multiAccountImportPrivateKey ^jsa (status)
|
||||
(types/clj->json {:privateKey "0x123abc"})
|
||||
#(println "cb:" %))
|
||||
(.multiformatDeserializePublicKey
|
||||
^js (status) "zQ3shwiRy5TtMM7B4iZ8MKaGywkaaCgMqqbrnAUYrZJ1sgVWh"
|
||||
"f" #(println "cb:" %)))
|
||||
|
||||
(defn decompress-public-key
|
||||
"Decompresses 33-byte compressed format to uncompressed 65-byte format"
|
||||
[public-key callback]
|
||||
(log/debug "[native-module] decompress-public-key")
|
||||
(.multiformatDeserializePublicKey ^js (status) public-key
|
||||
;; (types/clj->json {:n n
|
||||
;; :mnemonicPhraseLength mnemonic-length
|
||||
;; :bip39Passphrase ""
|
||||
;; :paths paths})
|
||||
callback))
|
||||
|
||||
(defn get-connection-string-for-bootstrapping-another-device
|
||||
"Generates connection string form status-go for the purpose of local pairing on the sender end"
|
||||
[config-json callback]
|
||||
|
||||
@@ -70,7 +70,7 @@
|
||||
:AutoUpdate true
|
||||
:PeerExchange true
|
||||
:Port 0
|
||||
:UDPPort 0})
|
||||
:UDPPort 9001})
|
||||
|
||||
(def login-node-config
|
||||
{:WalletConfig (cond-> {:Enabled true}
|
||||
|
||||
@@ -1,41 +1,152 @@
|
||||
(ns status-im.ui.screens.add-new.new-chat.views
|
||||
(:require
|
||||
[clojure.string :as string]
|
||||
[quo.core :as quo]
|
||||
[quo.design-system.colors :as colors]
|
||||
[quo.platform :as platform]
|
||||
[quo.react-native :as rn]
|
||||
[re-frame.core :as re-frame]
|
||||
[reagent.core :as reagent]
|
||||
[status-im.ethereum.ens :as ens]
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
[status-im.multiaccounts.core :as multiaccounts]
|
||||
[status-im.qr-scanner.core :as qr-scanner]
|
||||
[status-im.ui.components.animation :as animation]
|
||||
[status-im.ui.components.chat-icon.screen :as chat-icon]
|
||||
[status-im.ui.components.icons.icons :as icons]
|
||||
[status-im.ui.components.invite.views :as invite]
|
||||
[status-im.ui.components.keyboard-avoid-presentation :as kb-presentation]
|
||||
[status-im.ui.components.list.views :as list]
|
||||
[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-im.utils.gfycat.core :as gfycat]
|
||||
[status-im.utils.identicon :as identicon]
|
||||
[status-im.utils.utils :as utils]
|
||||
[utils.debounce :as debounce])
|
||||
(:require-macros [status-im.utils.views :as views]))
|
||||
;; (:require
|
||||
;; [clojure.string :as string]
|
||||
;; [quo.core :as quo]
|
||||
;; [quo.design-system.colors :as colors]
|
||||
;; [quo.platform :as platform]
|
||||
;; [quo.react-native :as rn]
|
||||
;; [re-frame.core :as re-frame]
|
||||
;; [reagent.core :as reagent]
|
||||
;; [status-im.ethereum.ens :as ens]
|
||||
;; [status-im.i18n.i18n :as i18n]
|
||||
;; [status-im.multiaccounts.core :as multiaccounts]
|
||||
;; [status-im.qr-scanner.core :as qr-scanner]
|
||||
;; [status-im.ui.components.animation :as animation]
|
||||
;; [status-im.ui.components.chat-icon.screen :as chat-icon]
|
||||
;; [status-im.ui.components.icons.icons :as icons]
|
||||
;; [status-im.ui.components.invite.views :as invite]
|
||||
;; [status-im.ui.components.keyboard-avoid-presentation :as kb-presentation]
|
||||
;; [status-im.ui.components.list.views :as list]
|
||||
;; [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-im.utils.gfycat.core :as gfycat]
|
||||
;; [status-im.utils.identicon :as identicon]
|
||||
;; [status-im.utils.utils :as utils]
|
||||
;; [utils.debounce :as debounce])
|
||||
;; (:require-macros [status-im.utils.views :as views])
|
||||
(:require [reagent.core :as reagent]
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
[status-im.qr-scanner.core :as qr-scanner]
|
||||
[status-im.ui.components.icons.icons :as icons]
|
||||
[status-im.ui.components.invite.events :as invite.events]
|
||||
[status-im.ui.components.list.views :as list]
|
||||
[status-im.ui.components.react :as react]
|
||||
[status-im.ui.components.topbar :as topbar]
|
||||
[status-im.utils.db :as utils.db]
|
||||
[status-im.utils.utils :as utils]
|
||||
[utils.debounce :as debounce]
|
||||
[utils.re-frame :as rf]
|
||||
[quo.core :as quo]
|
||||
[quo.react-native :as rn]
|
||||
[quo2.core :as quo2]
|
||||
[quo2.foundations.colors :as colors]))
|
||||
|
||||
(defn- render-row
|
||||
[row]
|
||||
(let [first-name (first (multiaccounts/contact-two-names row false))]
|
||||
[quo/list-item
|
||||
{:title first-name
|
||||
:icon [chat-icon/contact-icon-contacts-tab
|
||||
(multiaccounts/displayed-photo row)]
|
||||
:on-press #(re-frame/dispatch [:chat.ui/start-chat
|
||||
(:public-key row)])}]))
|
||||
;; TODO(@esep): move styles to styles.cljs (https://github.com/status-im/status-mobile/blob/develop/doc/new-guidelines.md#component-styles)
|
||||
|
||||
(defn chat-no-contacts []
|
||||
[rn/view {:top 128
|
||||
:height 302
|
||||
:padding 12
|
||||
:flex-direction :column
|
||||
:justify-content :space-around
|
||||
:align-items :center}
|
||||
[rn/view {:style {:height 120
|
||||
:width 120
|
||||
:background-color colors/neutral-40}}]
|
||||
[quo2/text {:size :paragraph-1
|
||||
:weight :semi-bold
|
||||
:style {:color colors/neutral-100}}
|
||||
(i18n/label :you-have-no-contacts)]
|
||||
[quo2/text {:size :paragraph-2
|
||||
:style {:color colors/neutral-100}}
|
||||
(i18n/label :invite-your-friends)]
|
||||
[quo2/button {:size 32 :on-press
|
||||
#(rf/dispatch [::invite.events/share-link nil])}
|
||||
(i18n/label :invite-friends)]
|
||||
[quo2/button {:type :grey :size 32 :on-press #(%)}
|
||||
(i18n/label :add-a-contact)]])
|
||||
|
||||
(defn contact-item [contact]
|
||||
[rn/touchable-opacity
|
||||
[:<>
|
||||
[rn/view
|
||||
;; [icon-avatar/icon-avatar {:size :medium
|
||||
;; :icon icon
|
||||
;; :color color
|
||||
;; :opacity opacity}]
|
||||
|
||||
[quo2/text (:alias contact)]
|
||||
]]])
|
||||
|
||||
(defn contact-list-key-fn [contact]
|
||||
(:public-key contact))
|
||||
|
||||
(defn render-fn [contact]
|
||||
[contact-item contact])
|
||||
|
||||
(defn chat-contacts-exist [contacts account]
|
||||
;; show contact list
|
||||
[rn/view {:top 128
|
||||
:height 302
|
||||
:padding 12
|
||||
:flex-direction :column
|
||||
:justify-content :space-around
|
||||
:align-items :center}
|
||||
;; user-avatar
|
||||
;; for status: either group-avatar or verified check
|
||||
[list/flat-list
|
||||
{:key-fn contact-list-key-fn
|
||||
;; :getItemLayout get-item-layout
|
||||
;; :on-end-reached #(%)
|
||||
;; :keyboard-should-persist-taps :always
|
||||
:data contacts
|
||||
:render-fn render-fn}]
|
||||
])
|
||||
|
||||
;; (merge (when key-fn {:keyExtractor (memo-wrap-key-fn key-fn)})
|
||||
;; (when render-fn {:renderItem (memo-wrap-render-fn render-fn render-data)})
|
||||
;; (when separator {:ItemSeparatorComponent (memo-separator-fn separator default-separator?)})
|
||||
;; (when empty-component {:ListEmptyComponent (memo-as-element empty-component)})
|
||||
;; (when header {:ListHeaderComponent (memo-as-element header)})
|
||||
;; (when footer {:ListFooterComponent (memo-as-element footer)}))
|
||||
|
||||
(def x (atom {}))
|
||||
(defn new-chat []
|
||||
(let [contacts (rf/sub [:contacts/active])
|
||||
account (rf/sub [:multiaccount])]
|
||||
(swap! x assoc :contacts contacts)
|
||||
(swap! x assoc :account account)
|
||||
[rn/view
|
||||
[rn/view {:height 120
|
||||
:flex-direction :column}
|
||||
[rn/view {:height 20}]
|
||||
[rn/view {:flex-direction :row
|
||||
:align-items :flex-start
|
||||
:justify-content :flex-start
|
||||
:padding-left 20
|
||||
:height 44}
|
||||
[rn/view {:justify-content :center
|
||||
:align-items :center
|
||||
:background-color colors/neutral-10
|
||||
:width 32
|
||||
:height 32
|
||||
:border-radius 10}
|
||||
[rn/touchable-opacity {:on-press #(rf/dispatch [:navigate-back])}
|
||||
[icons/icon :main-icons2/close]]]]
|
||||
[rn/view {:flex 1
|
||||
:flex-direction :row
|
||||
:align-items :center
|
||||
:padding-left 20}
|
||||
[quo2/text {:size :heading-1
|
||||
:weight :semi-bold
|
||||
:style {:color colors/neutral-100}} (i18n/label :new-chat)]]]
|
||||
(if (seq? contacts)
|
||||
(chat-contacts-exist contacts account)
|
||||
(chat-no-contacts))]))
|
||||
|
||||
;; (defn new-contact [] (fn [] [rn/view]))
|
||||
|
||||
(defn- icon-wrapper
|
||||
[color icon]
|
||||
@@ -53,20 +164,18 @@
|
||||
(let [icon (if new-contact? :main-icons/add :main-icons/arrow-right)]
|
||||
(cond
|
||||
(= state :searching)
|
||||
[icon-wrapper colors/gray
|
||||
[react/activity-indicator {:color colors/white-persist}]]
|
||||
[icon-wrapper colors/neutral-40
|
||||
[react/activity-indicator {:color colors/neutral-5}]]
|
||||
|
||||
(and (= state :valid) (not blocked?))
|
||||
[react/touchable-highlight
|
||||
{:on-press #(debounce/dispatch-and-chill [:contact.ui/contact-code-submitted new-contact?
|
||||
entered-nickname]
|
||||
3000)}
|
||||
[icon-wrapper colors/blue
|
||||
[icons/icon icon {:color colors/white-persist}]]]
|
||||
{:on-press #(debounce/dispatch-and-chill [:contact.ui/contact-code-submitted new-contact? entered-nickname] 3000)}
|
||||
[icon-wrapper colors/primary-50
|
||||
[icons/icon icon {:color colors/neutral-5}]]]
|
||||
|
||||
:else
|
||||
[icon-wrapper colors/gray
|
||||
[icons/icon icon {:color colors/white-persist}]])))
|
||||
[icon-wrapper colors/neutral-40
|
||||
[icons/icon icon {:color colors/neutral-5}]])))
|
||||
|
||||
(defn get-validation-label
|
||||
[value]
|
||||
@@ -76,215 +185,7 @@
|
||||
:yourself
|
||||
(i18n/label :t/can-not-add-yourself)))
|
||||
|
||||
(defn search-contacts
|
||||
[filter-text {:keys [name alias nickname]}]
|
||||
(or
|
||||
(string/includes? (string/lower-case (str name)) filter-text)
|
||||
(string/includes? (string/lower-case (str alias)) filter-text)
|
||||
(when nickname
|
||||
(string/includes? (string/lower-case (str nickname)) filter-text))))
|
||||
|
||||
(defn filter-contacts
|
||||
[filter-text contacts]
|
||||
(let [lower-filter-text (string/lower-case filter-text)]
|
||||
(if filter-text
|
||||
(filter (partial search-contacts lower-filter-text) contacts)
|
||||
contacts)))
|
||||
|
||||
(defn is-public-key?
|
||||
[k]
|
||||
(and
|
||||
(string? k)
|
||||
(string/starts-with? k "0x")))
|
||||
|
||||
(defn is-valid-username?
|
||||
[username]
|
||||
(let [is-chat-key? (and (is-public-key? username)
|
||||
(= (count username) 132))
|
||||
is-ens? (ens/valid-eth-name-prefix? username)]
|
||||
(or is-chat-key? is-ens?)))
|
||||
|
||||
(defn translate-anim
|
||||
[translate-y-value translate-y-anim-value]
|
||||
(animation/start
|
||||
(animation/timing translate-y-anim-value
|
||||
{:toValue translate-y-value
|
||||
:duration 200
|
||||
:useNativeDriver true})))
|
||||
(views/defview new-chat
|
||||
[]
|
||||
(views/letsubs [contacts [:contacts/active]
|
||||
{:keys [state ens-name public-key error]} [:contacts/new-identity]
|
||||
search-value (reagent/atom "")
|
||||
account @(re-frame/subscribe [:multiaccount])
|
||||
on-share #(re-frame/dispatch
|
||||
[:show-popover
|
||||
{:view :share-chat-key
|
||||
:address (account :public-key)
|
||||
:ens-name (account :preferred-name)}])
|
||||
my-profile-button-anim-y (animation/create-value 0)
|
||||
keyboard-show-listener (atom nil)
|
||||
keyboard-hide-listener (atom nil)
|
||||
on-keyboard-show (fn []
|
||||
;; 42 is the bottom position so we
|
||||
;; translate it by 32 pts to leave 10 as
|
||||
;; margin
|
||||
(translate-anim 32
|
||||
my-profile-button-anim-y))
|
||||
on-keyboard-hide (fn []
|
||||
(translate-anim 0
|
||||
my-profile-button-anim-y))
|
||||
keyboard-show-event (if platform/android?
|
||||
"keyboardDidShow"
|
||||
"keyboardWillShow")
|
||||
keyboard-hide-event (if platform/android?
|
||||
"keyboardDidHide"
|
||||
"keyboardWillHide")]
|
||||
{:component-did-mount
|
||||
(fn [_]
|
||||
(reset! keyboard-show-listener (.addListener react/keyboard keyboard-show-event on-keyboard-show))
|
||||
(reset! keyboard-hide-listener (.addListener react/keyboard
|
||||
keyboard-hide-event
|
||||
on-keyboard-hide)))
|
||||
:component-will-unmount
|
||||
(fn []
|
||||
(some-> ^js @keyboard-show-listener
|
||||
.remove)
|
||||
(some-> ^js @keyboard-hide-listener
|
||||
.remove))}
|
||||
[kb-presentation/keyboard-avoiding-view {:style {:flex 1}}
|
||||
[react/view {:style {:flex 1}}
|
||||
[topbar/topbar
|
||||
{:title (i18n/label :t/new-chat)
|
||||
:modal? true
|
||||
:right-accessories
|
||||
[{:icon :qr
|
||||
:accessibility-label :scan-contact-code-button
|
||||
:on-press #(re-frame/dispatch [::qr-scanner/scan-code
|
||||
{:title (i18n/label :t/new-chat)
|
||||
:handler :contact/qr-code-scanned}])}]}]
|
||||
[react/view
|
||||
{:flex-direction :row
|
||||
:padding 16}
|
||||
[react/view {:flex 1}
|
||||
[quo/text-input
|
||||
{:on-change-text
|
||||
#(do
|
||||
(reset! search-value %)
|
||||
(re-frame/dispatch [:set-in [:contacts/new-identity :state] :empty])
|
||||
(debounce/debounce-and-dispatch [:new-chat/set-new-identity %] 600))
|
||||
:on-submit-editing
|
||||
#(when (= state :valid)
|
||||
(debounce/dispatch-and-chill [:contact.ui/contact-code-submitted false nil] 3000))
|
||||
:placeholder (i18n/label :t/enter-contact-code)
|
||||
:show-cancel false
|
||||
:accessibility-label :enter-contact-code-input
|
||||
:auto-capitalize :none
|
||||
:return-key-type :go
|
||||
:monospace true
|
||||
:auto-correct false}]]]
|
||||
[react/scroll-view
|
||||
{:style {:flex 1}
|
||||
:keyboard-dismiss-mode :on-drag
|
||||
:keyboard-should-persist-taps :handled}
|
||||
[react/view
|
||||
(when (and
|
||||
(= (count contacts) 0)
|
||||
(= @search-value ""))
|
||||
{:flex 1})
|
||||
(if (and
|
||||
(= (count contacts) 0)
|
||||
(= @search-value ""))
|
||||
[react/view
|
||||
{:flex 1
|
||||
:align-items :center
|
||||
:padding-horizontal 58
|
||||
:padding-top 160}
|
||||
[quo/text
|
||||
{:size :base
|
||||
:align :center
|
||||
:color :secondary}
|
||||
(i18n/label :t/you-dont-have-contacts-invite-friends)]
|
||||
[invite/button]]
|
||||
[list/flat-list
|
||||
{:data (filter-contacts @search-value contacts)
|
||||
:key-fn :address
|
||||
:render-fn render-row}])]
|
||||
(when-not (= @search-value "")
|
||||
[react/view
|
||||
[quo/text
|
||||
{:style {:margin-horizontal 16
|
||||
:margin-vertical 14}
|
||||
:size :base
|
||||
:align :left
|
||||
:color :secondary}
|
||||
(i18n/label :t/non-contacts)]
|
||||
(when (and (= state :searching)
|
||||
(is-valid-username? @search-value))
|
||||
[rn/activity-indicator
|
||||
{:color colors/gray
|
||||
:size (if platform/android? :large :small)}])
|
||||
(if (= state :valid)
|
||||
[quo/list-item
|
||||
(merge
|
||||
{:title (or ens-name (gfycat/generate-gfy public-key))
|
||||
:subtitle (if ens-name
|
||||
(gfycat/generate-gfy public-key)
|
||||
(utils/get-shortened-address public-key))
|
||||
:icon [chat-icon/contact-icon-contacts-tab
|
||||
(identicon/identicon public-key)]
|
||||
:on-press #(do
|
||||
(debounce/dispatch-and-chill [:contact.ui/contact-code-submitted false] 3000)
|
||||
(re-frame/dispatch [:search/home-filter-changed nil]))}
|
||||
(when ens-name {:subtitle-secondary public-key}))]
|
||||
[quo/text
|
||||
{:style {:margin-horizontal 16}
|
||||
:size :base
|
||||
:align :center
|
||||
:color :secondary}
|
||||
(if (is-valid-username? @search-value)
|
||||
(when (= state :error)
|
||||
(get-validation-label error))
|
||||
(i18n/label :t/invalid-username-or-key))])])]
|
||||
(when-not (and
|
||||
(= (count contacts) 0)
|
||||
(= @search-value ""))
|
||||
[react/animated-view
|
||||
{:style {:height 36
|
||||
:width 124
|
||||
:position :absolute
|
||||
:bottom 42
|
||||
:transform [{:translateY my-profile-button-anim-y}]
|
||||
:align-self :center}}
|
||||
[react/touchable-opacity
|
||||
{:style {:padding-horizontal 2
|
||||
:height 36
|
||||
:width 124
|
||||
:background-color colors/blue
|
||||
:border-radius 18
|
||||
:elevation 4
|
||||
:shadow-offset {:width 0 :height 4}
|
||||
:shadow-color "rgba(0, 34, 51, 0.16)"
|
||||
:shadow-radius 4
|
||||
:shadow-opacity 1}
|
||||
:on-press on-share}
|
||||
[react/view
|
||||
{:style {:flex 1
|
||||
:flex-direction :row
|
||||
:align-items :center}}
|
||||
[photos/photo
|
||||
(multiaccounts/displayed-photo account)
|
||||
{:size 32
|
||||
:accessibility-label :current-account-photo}]
|
||||
[quo/text
|
||||
{:size :base
|
||||
:weight :medium
|
||||
:color :inverse
|
||||
:style {:margin-left 6}}
|
||||
(i18n/label :t/my-profile)]]]])]]))
|
||||
|
||||
(defn- nickname-input
|
||||
[entered-nickname]
|
||||
(defn- nickname-input [entered-nickname]
|
||||
[quo/text-input
|
||||
{:on-change-text #(reset! entered-nickname %)
|
||||
:auto-capitalize :none
|
||||
@@ -299,65 +200,69 @@
|
||||
[]
|
||||
(let [entered-nickname (reagent/atom "")]
|
||||
(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 ""))
|
||||
@(re-frame/subscribe [:contacts/contact-blocked?
|
||||
public-key]))]
|
||||
[react/view {:style {:flex 1}}
|
||||
(let [{:keys [state ens-name public-key error]}
|
||||
(rf/sub [:contacts/new-identity])
|
||||
blocked? (and
|
||||
(utils.db/valid-public-key? (or public-key ""))
|
||||
(rf/sub [:contacts/contact-blocked? public-key]))]
|
||||
[rn/view {:style {:flex 1}}
|
||||
[topbar/topbar
|
||||
{:title (i18n/label :t/new-contact)
|
||||
:modal? true
|
||||
:right-accessories
|
||||
[{:icon :qr
|
||||
:accessibility-label :scan-contact-code-button
|
||||
:on-press #(re-frame/dispatch [::qr-scanner/scan-code
|
||||
{:title (i18n/label :t/new-contact)
|
||||
:handler :contact/qr-code-scanned
|
||||
:new-contact? true
|
||||
:nickname @entered-nickname}])}]}]
|
||||
[react/view
|
||||
{:flex-direction :row
|
||||
:padding 16}
|
||||
[react/view
|
||||
{:flex 1
|
||||
:padding-right 16}
|
||||
:on-press
|
||||
#(rf/dispatch [::qr-scanner/scan-code
|
||||
{:title (i18n/label :t/new-contact)
|
||||
:handler :contact/qr-code-scanned
|
||||
:new-contact? true
|
||||
:nickname @entered-nickname}])}]}]
|
||||
[rn/view {:flex-direction :row
|
||||
:padding 16}
|
||||
[rn/view {:flex 1
|
||||
:padding-right 16}
|
||||
[quo/text-input
|
||||
{:on-change-text
|
||||
#(do
|
||||
(re-frame/dispatch [:set-in [:contacts/new-identity :state] :searching])
|
||||
(debounce/debounce-and-dispatch [:new-chat/set-new-identity %] 600))
|
||||
(rf/dispatch [:set-in [:contacts/new-identity :state] :searching])
|
||||
(debounce/debounce-and-dispatch
|
||||
[:new-chat/set-new-identity %] 600))
|
||||
:on-submit-editing
|
||||
#(when (= state :valid)
|
||||
(debounce/dispatch-and-chill [:contact.ui/contact-code-submitted true @entered-nickname]
|
||||
3000))
|
||||
(debounce/dispatch-and-chill
|
||||
[:contact.ui/contact-code-submitted true @entered-nickname] 3000))
|
||||
:placeholder (i18n/label :t/enter-contact-code)
|
||||
:show-cancel false
|
||||
:accessibility-label :enter-contact-code-input
|
||||
:auto-capitalize :none
|
||||
:return-key-type :go}]]
|
||||
[react/view
|
||||
{:justify-content :center
|
||||
:align-items :center}
|
||||
[rn/view {:justify-content :center
|
||||
:align-items :center}
|
||||
[input-icon state true @entered-nickname blocked?]]]
|
||||
[react/view {:min-height 30 :justify-content :flex-end :margin-bottom 16}
|
||||
[quo/text
|
||||
{:style {:margin-horizontal 16}
|
||||
:size :small
|
||||
:align :center
|
||||
:color :secondary}
|
||||
[rn/view {:min-height 30 :justify-content :flex-end :margin-bottom 16}
|
||||
[quo/text {:style {:margin-horizontal 16}
|
||||
:size :small
|
||||
:align :center
|
||||
:color :secondary}
|
||||
(cond (= state :error)
|
||||
(get-validation-label error)
|
||||
(= state :valid)
|
||||
(str (when ens-name (str ens-name " • "))
|
||||
(utils/get-shortened-address public-key))
|
||||
:else "")]]
|
||||
[react/text {:style {:margin-horizontal 16 :color colors/gray}}
|
||||
:else "")]]
|
||||
[react/text {:style {:margin-horizontal 16 :color colors/neutral-40}}
|
||||
(i18n/label :t/nickname-description)]
|
||||
[react/view {:padding 16}
|
||||
[rn/view {:padding 16}
|
||||
[nickname-input entered-nickname]
|
||||
[react/text
|
||||
{:style {:align-self :flex-end
|
||||
:margin-top 16
|
||||
:color colors/gray}}
|
||||
[react/text {:style {:align-self :flex-end :margin-top 16
|
||||
:color colors/neutral-40}}
|
||||
(str (count @entered-nickname) " / 32")]]]))))
|
||||
|
||||
(comment
|
||||
;; open new-chat
|
||||
(rf/dispatch [:open-modal :new-chat])
|
||||
;; close it
|
||||
(rf/dispatch [:navigate-back])
|
||||
(rf/dispatch [:logout])
|
||||
)
|
||||
|
||||
@@ -0,0 +1,256 @@
|
||||
(ns status-im.ui.screens.add-new.new-contact.views
|
||||
(:require [reagent.core :as reagent]
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
[status-im.qr-scanner.core :as qr-scanner]
|
||||
[status-im.ui.components.icons.icons :as icons]
|
||||
[status-im.ui.components.invite.events :as invite.events]
|
||||
[status-im.ui.components.list.views :as list]
|
||||
[status-im.ui.components.react :as react]
|
||||
[status-im.ui.components.topbar :as topbar]
|
||||
[status-im.utils.db :as utils.db]
|
||||
[status-im.utils.utils :as utils]
|
||||
[utils.debounce :as debounce]
|
||||
[utils.re-frame :as rf]
|
||||
[quo.core :as quo]
|
||||
[quo.react-native :as rn]
|
||||
[quo2.core :as quo2]
|
||||
[quo2.foundations.colors :as colors]))
|
||||
|
||||
;; TODO(@esep): move styles to styles.cljs (https://github.com/status-im/status-mobile/blob/develop/doc/new-guidelines.md#component-styles)
|
||||
;; TODO(@esep): i18n (https://github.com/status-im/status-mobile/blob/develop/doc/new-guidelines.md#component-styles)
|
||||
|
||||
(defn get-validation-label [value]
|
||||
(case value
|
||||
:invalid
|
||||
(i18n/label :t/profile-not-found)
|
||||
:yourself
|
||||
(i18n/label :t/can-not-add-yourself)))
|
||||
|
||||
(def x (atom {}))
|
||||
(defn new-contact []
|
||||
(let [new-identity (rf/sub [:contacts/new-identity])
|
||||
{:keys [state ens-name public-key error]} new-identity
|
||||
blocked? (and
|
||||
(utils.db/valid-public-key? (or public-key ""))
|
||||
(rf/sub [:contacts/contact-blocked? public-key]))]
|
||||
(println "state:" state)
|
||||
;; (swap! x assoc :contacts contacts)
|
||||
;; (swap! x assoc :account account)
|
||||
[rn/view
|
||||
[rn/view {:height 20}]
|
||||
[rn/view {:flex-direction :row
|
||||
:align-items :flex-start
|
||||
:justify-content :flex-start
|
||||
:padding-left 20
|
||||
:height 44}
|
||||
[rn/view {:justify-content :center
|
||||
:align-items :center
|
||||
:background-color colors/neutral-10
|
||||
:width 32
|
||||
:height 32
|
||||
:border-radius 10}
|
||||
[rn/touchable-opacity {:on-press #(rf/dispatch [:navigate-back])}
|
||||
[icons/icon :main-icons2/close]]]]
|
||||
[rn/image {:source (js/require "../resources/images/ui/add-contact.png")
|
||||
;; :style styles/sync-devices-header-image
|
||||
}]
|
||||
[rn/view {:height 400
|
||||
:flex-direction :column
|
||||
:padding 16
|
||||
:border-width 1
|
||||
:border-color colors/primary-50
|
||||
}
|
||||
[quo2/text {:size :heading-1
|
||||
:weight :semi-bold
|
||||
:style {:color colors/neutral-100}} "Add a contact"]
|
||||
[quo2/text {:size :paragraph-2
|
||||
:weight :regular
|
||||
:style {:color colors/neutral-100}} "Find your friends with their ENS or Chat key"]
|
||||
[quo2/text {:size :paragraph-2
|
||||
:weight :regular
|
||||
:style {:color colors/neutral-100}} "ENS or Chat key"]
|
||||
[rn/view {;; :flex 1
|
||||
;; :flex-direction :row
|
||||
;; :align-items :flex-start
|
||||
;; :justify-content :flex-start
|
||||
:border-width 1
|
||||
:border-color colors/primary-50
|
||||
;; :padding 16
|
||||
;; :width 280
|
||||
}
|
||||
[quo/text-input
|
||||
{:on-change-text
|
||||
#(do
|
||||
(rf/dispatch [:set-in [:contacts/new-identity :state] :searching])
|
||||
(debounce/debounce-and-dispatch
|
||||
[:new-chat/set-new-identity %] 600))
|
||||
:on-submit-editing #()
|
||||
;; #(when (= state :valid)
|
||||
;; (debounce/dispatch-and-chill
|
||||
;; [:contact.ui/contact-code-submitted true @entered-nickname] 3000))
|
||||
:placeholder "0x123abc"
|
||||
:show-cancel false
|
||||
:accessibility-label :enter-contact-code-input
|
||||
:auto-capitalize :none
|
||||
:height 40
|
||||
:width 283
|
||||
:background-color colors/white
|
||||
:border-width 1
|
||||
:border-color colors/neutral-20
|
||||
:border-radius 12
|
||||
;; :return-key-type :go
|
||||
}]
|
||||
[rn/view {:justify-content :center
|
||||
:align-items :center
|
||||
:width 32
|
||||
:height 32
|
||||
:background-color colors/white
|
||||
:border-width 1
|
||||
:border-color colors/neutral-20
|
||||
:border-radius 12}
|
||||
[rn/touchable-opacity {:on-press #()}
|
||||
[icons/icon :main-icons2/scan2]]]]
|
||||
[quo/text {:style {:margin-horizontal 16}
|
||||
:size :small
|
||||
:align :center
|
||||
:color :secondary}
|
||||
(cond (= state :error)
|
||||
(get-validation-label error)
|
||||
(= state :valid)
|
||||
(str (when ens-name (str ens-name " • "))
|
||||
(utils/get-shortened-address public-key))
|
||||
:else "")]]
|
||||
[rn/view {:padding 16}
|
||||
[quo2/button {:type :primary
|
||||
:size 40
|
||||
:width 335
|
||||
:accessibility-label :new-contact-button
|
||||
:before :i/profile
|
||||
:disabled (if (= state :valid) false true)
|
||||
:on-press
|
||||
(if (= state :valid)
|
||||
#(rf/dispatch [:chat.ui/show-profile public-key])
|
||||
#())}
|
||||
"View profile"]]]))
|
||||
|
||||
(defn- icon-wrapper [color icon]
|
||||
[react/view
|
||||
{:style {:width 50
|
||||
:height 32
|
||||
:border-radius 25
|
||||
:align-items :center
|
||||
:justify-content :center
|
||||
:background-color color}}
|
||||
icon])
|
||||
|
||||
(defn- input-icon
|
||||
[state new-contact? entered-nickname blocked?]
|
||||
(let [icon (if new-contact? :main-icons/add :main-icons/arrow-right)]
|
||||
(cond
|
||||
(= state :searching)
|
||||
[icon-wrapper colors/neutral-40
|
||||
[react/activity-indicator {:color colors/neutral-5}]]
|
||||
|
||||
(and (= state :valid) (not blocked?))
|
||||
[react/touchable-highlight
|
||||
{:on-press #(debounce/dispatch-and-chill [:contact.ui/contact-code-submitted new-contact? entered-nickname] 3000)}
|
||||
[icon-wrapper colors/primary-50
|
||||
[icons/icon icon {:color colors/neutral-5}]]]
|
||||
|
||||
:else
|
||||
[icon-wrapper colors/neutral-40
|
||||
[icons/icon icon {:color colors/neutral-5}]])))
|
||||
|
||||
(defn- nickname-input [entered-nickname]
|
||||
[quo/text-input
|
||||
{:on-change-text #(reset! entered-nickname %)
|
||||
:auto-capitalize :none
|
||||
:max-length 32
|
||||
:auto-focus false
|
||||
:accessibility-label :nickname-input
|
||||
:placeholder (i18n/label :t/add-nickname)
|
||||
:return-key-type :done
|
||||
:auto-correct false}])
|
||||
|
||||
(def y (atom {}))
|
||||
(defn new-contact-old []
|
||||
(let [entered-nickname (reagent/atom "")]
|
||||
(fn []
|
||||
(let [new-identity (rf/sub [:contacts/new-identity])
|
||||
{:keys [state ens-name public-key error]} new-identity
|
||||
blocked? (and
|
||||
(utils.db/valid-public-key? (or public-key ""))
|
||||
(rf/sub [:contacts/contact-blocked? public-key]))]
|
||||
;; (swap! y :new-identity new-identity)
|
||||
[rn/view {:style {:flex 1}}
|
||||
[topbar/topbar
|
||||
{:title (i18n/label :t/new-contact)
|
||||
:modal? true
|
||||
:right-accessories
|
||||
[{:icon :qr
|
||||
:accessibility-label :scan-contact-code-button
|
||||
:on-press
|
||||
#(rf/dispatch [::qr-scanner/scan-code
|
||||
{:title (i18n/label :t/new-contact)
|
||||
:handler :contact/qr-code-scanned
|
||||
:new-contact? true
|
||||
:nickname @entered-nickname}])}]}]
|
||||
[rn/view {:flex-direction :row
|
||||
:padding 16}
|
||||
[rn/view {:flex 1
|
||||
:padding-right 16}
|
||||
[quo/text-input
|
||||
{:on-change-text
|
||||
#(do
|
||||
(rf/dispatch [:set-in [:contacts/new-identity :state] :searching])
|
||||
(debounce/debounce-and-dispatch
|
||||
[:new-chat/set-new-identity %] 600))
|
||||
:on-submit-editing
|
||||
#(when (= state :valid)
|
||||
(debounce/dispatch-and-chill
|
||||
[:contact.ui/contact-code-submitted true @entered-nickname] 3000))
|
||||
:placeholder (i18n/label :t/enter-contact-code)
|
||||
:show-cancel false
|
||||
:accessibility-label :enter-contact-code-input
|
||||
:auto-capitalize :none
|
||||
:return-key-type :go}]]
|
||||
[rn/view {:justify-content :center
|
||||
:border-width 1
|
||||
:border-color colors/danger-50
|
||||
:align-items :center}
|
||||
[input-icon state true @entered-nickname blocked?]]]
|
||||
[rn/view {:min-height 30 :justify-content :flex-end :margin-bottom 16}
|
||||
[quo/text {:style {:margin-horizontal 16}
|
||||
:size :small
|
||||
:align :center
|
||||
:color :secondary}
|
||||
(cond (= state :error)
|
||||
(get-validation-label error)
|
||||
(= state :valid)
|
||||
(str (when ens-name (str ens-name " • "))
|
||||
(utils/get-shortened-address public-key))
|
||||
:else "")]]
|
||||
[react/text {:style {:margin-horizontal 16 :color colors/neutral-40}}
|
||||
(i18n/label :t/nickname-description)]
|
||||
[rn/view {:padding 16}
|
||||
[nickname-input entered-nickname]
|
||||
[react/text {:style {:align-self :flex-end :margin-top 16
|
||||
:color colors/neutral-40}}
|
||||
(str (count @entered-nickname) " / 32")]]]))))
|
||||
|
||||
(comment
|
||||
;; open new-chat
|
||||
(rf/dispatch [:open-modal :new-chat])
|
||||
(rf/dispatch [:open-modal :new-contact])
|
||||
;; close it
|
||||
(rf/dispatch [:navigate-back])
|
||||
(rf/dispatch [:logout])
|
||||
|
||||
;; go to new contact (old one), dispatch below, watch, click plus button
|
||||
;; bass
|
||||
(rf/dispatch [:new-chat/set-new-identity "0x041adb97f30458f5c5978f5ee82727f4d8f26f94dbfe23a43f59b938f41e3dec070e35dda4fd4315e244d45cb30f45b98ac0a9bcdd366b5102f3ecad6fe193f677"])
|
||||
;; marlin
|
||||
(rf/dispatch [:new-chat/set-new-identity "0x04351fb713ee1fd8b15cc70918ccb852176f6f4068af9928051d2f78148cbd850fa6f72550142f2c8195082799d9ef90b226cd4183c67e0ad56c657c211cd83ed6"])
|
||||
(rf/dispatch [:chat.ui/show-profile "0x04351fb713ee1fd8b15cc70918ccb852176f6f4068af9928051d2f78148cbd850fa6f72550142f2c8195082799d9ef90b226cd4183c67e0ad56c657c211cd83ed6"])
|
||||
|
||||
)
|
||||
@@ -95,7 +95,9 @@
|
||||
(views/letsubs [topic [:public-group-topic]
|
||||
error [:public-chat.new/topic-error-message]]
|
||||
[react/scroll-view {:style {:flex 1}}
|
||||
[react/view {:padding-horizontal 16}
|
||||
[react/view {:padding-horizontal 16
|
||||
:border-width 1
|
||||
:border-color colors/blue}
|
||||
[react/view {:align-items :center :padding-vertical 8}
|
||||
[react/image
|
||||
{:source (:new-chat-header resources/ui)
|
||||
|
||||
@@ -357,7 +357,7 @@
|
||||
:margin-horizontal 16
|
||||
:margin-top 15
|
||||
:margin-bottom 8}
|
||||
[quo2.text/text {:size :heading-1 :weight :semi-bold} (i18n/label :t/messages)]
|
||||
[quo2.text/text {:size :heading-1 :weight :semi-bold} "(i18n/label :t/messages)"]
|
||||
[plus-button]]
|
||||
[chats-list]])
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
[status-im.ui.components.icons.icons :as icons]
|
||||
[status-im.ui.screens.about-app.views :as about-app]
|
||||
[status-im.ui.screens.add-new.new-contact.views :as new-contact]
|
||||
[status-im.ui.screens.add-new.new-chat.views :as new-chat]
|
||||
[status-im.ui.screens.add-new.new-public-chat.view :as new-public-chat]
|
||||
[status-im.ui.screens.advanced-settings.views :as advanced-settings]
|
||||
@@ -610,10 +611,8 @@
|
||||
|
||||
;New Contact
|
||||
{:name :new-contact
|
||||
:on-focus [::new-chat.events/new-chat-focus]
|
||||
;;TODO accessories
|
||||
:options {:topBar {:visible false}}
|
||||
:component new-chat/new-contact}
|
||||
:component new-contact/new-contact}
|
||||
|
||||
;[Wallet] Recipient
|
||||
{:name :recipient
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
|
||||
(defn call
|
||||
[{:keys [method params on-success on-error js-response] :as arg}]
|
||||
(println "call:" method params on-success on-error js-response)
|
||||
(let [params (or params [])
|
||||
on-error (or on-error
|
||||
(on-error-retry call arg)
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
"add-an-account": "Add an account",
|
||||
"add-bootnode": "Add bootnode",
|
||||
"add-contact": "Add contact",
|
||||
"add-a-contact": "Add a contact",
|
||||
"add-custom-token": "Add custom token",
|
||||
"add-mailserver": "Add Status node",
|
||||
"add-members": "Add members",
|
||||
@@ -198,6 +199,7 @@
|
||||
"community-share-title": "Share",
|
||||
"invite": "Invite",
|
||||
"invite-contacts": "Invite people from contacts list",
|
||||
"invite-your-friends": "Invite your friends and family to Status",
|
||||
"create-channel": "Create a channel",
|
||||
"import-community": "Import a community",
|
||||
"import-community-title": "Import a community",
|
||||
@@ -962,6 +964,7 @@
|
||||
"no": "No",
|
||||
"no-collectibles": "No collectibles available",
|
||||
"no-contacts": "No contacts yet",
|
||||
"you-have-no-contacts": "You have no contacts",
|
||||
"no-keycard-applet-on-card": "No Keycard applet on card",
|
||||
"no-messages": "No messages",
|
||||
"no-pairing-slots-available": "This card is already paired to 5 devices and cannot pair to this one. Please use one of the paired devices, log in with this card and free up pairing slots on the card",
|
||||
|
||||
Reference in New Issue
Block a user