Compare commits

...
Author SHA1 Message Date
Erik Seppanen eabe4e657f add new-contact 2022-12-28 07:12:31 -05:00
19 changed files with 893 additions and 326 deletions
@@ -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
View File
@@ -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])}})
+123
View File
@@ -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))
+102
View File
@@ -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])
+2
View File
@@ -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
+3 -1
View File
@@ -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
+54
View File
@@ -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))))
+21
View File
@@ -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]
+1 -1
View File
@@ -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)
+1 -1
View File
@@ -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]])
+2 -3
View File
@@ -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)
+3
View File
@@ -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",