Compare commits

...
144 changed files with 10500 additions and 8650 deletions
+1 -2
View File
@@ -1,7 +1,6 @@
{:lint-as {status-im.utils.views/defview clojure.core/defn {:lint-as {status-im.utils.views/defview clojure.core/defn
status-im.utils.views/letsubs clojure.core/let status-im.utils.views/letsubs clojure.core/let
reagent.core/with-let clojure.core/let reagent.core/with-let clojure.core/let
status-im.utils.fx/defn clj-kondo.lint-as/def-catch-all
utils.re-frame/defn clj-kondo.lint-as/def-catch-all utils.re-frame/defn clj-kondo.lint-as/def-catch-all
quo.react/with-deps-check clojure.core/fn quo.react/with-deps-check clojure.core/fn
quo.previews.preview/list-comp clojure.core/for quo.previews.preview/list-comp clojure.core/for
@@ -12,7 +11,7 @@
:linters {:consistent-alias {:level :error :linters {:consistent-alias {:level :error
:aliases {clojure.string string :aliases {clojure.string string
taoensso.timbre log}} taoensso.timbre log}}
:invalid-arity {:skip-args [status-im.utils.fx/defn utils.re-frame/defn]} :invalid-arity {:skip-args [utils.re-frame/defn]}
;; TODO remove number when this is fixed ;; TODO remove number when this is fixed
;; https://github.com/borkdude/clj-kondo/issues/867 ;; https://github.com/borkdude/clj-kondo/issues/867
:unresolved-symbol {:exclude [PersistentPriorityMap.EMPTY :unresolved-symbol {:exclude [PersistentPriorityMap.EMPTY
+27 -25
View File
@@ -1,19 +1,19 @@
(ns status-im.add-new.core (ns status-im.add-new.core
(:require [clojure.string :as string] (:require [clojure.string :as string]
[re-frame.core :as re-frame] [re-frame.core :as re-frame]
[status-im.add-new.db :as db]
[status-im.chat.models :as chat]
[status-im.contact.core :as contact]
[status-im.ethereum.core :as ethereum] [status-im.ethereum.core :as ethereum]
[status-im.ethereum.ens :as ens] [status-im.ethereum.ens :as ens]
[status-im.add-new.db :as db] [status-im.ethereum.stateofus :as stateofus]
[status-im.i18n.i18n :as i18n]
[status-im.router.core :as router]
[status-im.utils.db :as utils.db]
[status-im.utils.random :as random] [status-im.utils.random :as random]
[status-im.utils.utils :as utils] [status-im.utils.utils :as utils]
[status-im.utils.fx :as fx]
[status-im.chat.models :as chat]
[status-im.i18n.i18n :as i18n]
[status-im.contact.core :as contact]
[status-im.router.core :as router]
[status-im2.navigation.events :as navigation] [status-im2.navigation.events :as navigation]
[status-im.ethereum.stateofus :as stateofus] [utils.re-frame :as rf]))
[status-im.utils.db :as utils.db]))
(re-frame/reg-fx (re-frame/reg-fx
:resolve-public-key :resolve-public-key
@@ -24,19 +24,19 @@
;;NOTE we want to handle only last resolve ;;NOTE we want to handle only last resolve
(def resolve-last-id (atom nil)) (def resolve-last-id (atom nil))
(fx/defn new-chat-set-new-identity (rf/defn new-chat-set-new-identity
{:events [:new-chat/set-new-identity]} {:events [:new-chat/set-new-identity]}
[{db :db} new-identity-raw new-ens-name id] [{db :db} new-identity-raw new-ens-name id]
(let [ens-error (and (= new-identity-raw "0x") (not (string/blank? new-ens-name)))] (let [ens-error (and (= new-identity-raw "0x") (not (string/blank? new-ens-name)))]
(when (or (not id) (= id @resolve-last-id)) (when (or (not id) (= id @resolve-last-id))
(if ens-error (if ens-error
{:db (assoc-in db [:contacts/new-identity :state] :error)} {:db (assoc-in db [:contacts/new-identity :state] :error)}
(let [new-identity (utils/safe-trim new-identity-raw) (let [new-identity (utils/safe-trim new-identity-raw)
is-public-key? (and (string? new-identity) is-public-key? (and (string? new-identity)
(utils.db/valid-public-key? new-identity)) (utils.db/valid-public-key? new-identity))
is-ens? (and (not is-public-key?) is-ens? (and (not is-public-key?)
(ens/valid-eth-name-prefix? new-identity)) (ens/valid-eth-name-prefix? new-identity))
error (db/validate-pub-key db new-identity)] error (db/validate-pub-key db new-identity)]
(reset! resolve-last-id nil) (reset! resolve-last-id nil)
(merge {:db (assoc db (merge {:db (assoc db
:contacts/new-identity :contacts/new-identity
@@ -61,25 +61,27 @@
new-identity new-identity
@resolve-last-id])}}))))))) @resolve-last-id])}})))))))
(fx/defn clear-new-identity (rf/defn clear-new-identity
{:events [::clear-new-identity ::new-chat-focus]} {:events [::clear-new-identity ::new-chat-focus]}
[{:keys [db]}] [{:keys [db]}]
{:db (dissoc db :contacts/new-identity)}) {:db (dissoc db :contacts/new-identity)})
(fx/defn qr-code-handled (rf/defn qr-code-handled
{:events [::qr-code-handled]} {:events [::qr-code-handled]}
[{:keys [db] :as cofx} {:keys [type public-key chat-id data ens-name]} {:keys [new-contact? nickname] :as opts}] [{:keys [db] :as cofx} {:keys [type public-key chat-id data ens-name]}
(let [public-key? (and (string? data) {:keys [new-contact? nickname] :as opts}]
(string/starts-with? data "0x")) (let [public-key? (and (string? data)
chat-key (cond (string/starts-with? data "0x"))
(= type :private-chat) chat-id chat-key (cond
(= type :contact) public-key (= type :private-chat) chat-id
(and (= type :undefined) (= type :contact) public-key
public-key?) data) (and (= type :undefined)
public-key?)
data)
validation-result (db/validate-pub-key db chat-key)] validation-result (db/validate-pub-key db chat-key)]
(if-not validation-result (if-not validation-result
(if new-contact? (if new-contact?
(fx/merge cofx (rf/merge cofx
(contact/add-contact chat-key nickname ens-name) (contact/add-contact chat-key nickname ens-name)
(navigation/navigate-to-cofx :contacts-list {})) (navigation/navigate-to-cofx :contacts-list {}))
(chat/start-chat cofx chat-key ens-name)) (chat/start-chat cofx chat-key ens-name))
@@ -91,7 +93,7 @@
(i18n/label :t/can-not-add-yourself)) (i18n/label :t/can-not-add-yourself))
:on-dismiss #(re-frame/dispatch [:pop-to-root-tab :chat-stack])}}))) :on-dismiss #(re-frame/dispatch [:pop-to-root-tab :chat-stack])}})))
(fx/defn qr-code-scanned (rf/defn qr-code-scanned
{:events [:contact/qr-code-scanned]} {:events [:contact/qr-code-scanned]}
[{:keys [db]} data opts] [{:keys [db]} data opts]
{::router/handle-uri {:chain (ethereum/chain-keyword db) {::router/handle-uri {:chain (ethereum/chain-keyword db)
+6 -6
View File
@@ -1,23 +1,23 @@
(ns status-im.backup.core (ns status-im.backup.core
(:require [re-frame.core :as re-frame] (:require [re-frame.core :as re-frame]
[taoensso.timbre :as log]
[status-im.ethereum.json-rpc :as json-rpc] [status-im.ethereum.json-rpc :as json-rpc]
[status-im.utils.fx :as fx])) [taoensso.timbre :as log]
[utils.re-frame :as rf]))
(fx/defn handle-backup-failed (rf/defn handle-backup-failed
{:events [::backup-failed]} {:events [::backup-failed]}
[{:keys [db]}] [{:keys [db]}]
{:db (dissoc db :backup/performing-backup)}) {:db (dissoc db :backup/performing-backup)})
(fx/defn handle-backup-perfomed (rf/defn handle-backup-perfomed
{:events [::backup-performed]} {:events [::backup-performed]}
[{:keys [db]}] [{:keys [db]}]
{:db (dissoc db :backup/performing-backup)}) {:db (dissoc db :backup/performing-backup)})
(fx/defn handle-perform-backup-pressed (rf/defn handle-perform-backup-pressed
{:events [:multiaccounts.ui/perform-backup-pressed]} {:events [:multiaccounts.ui/perform-backup-pressed]}
[{:keys [db]}] [{:keys [db]}]
{:db (assoc db :backup/performing-backup true) {:db (assoc db :backup/performing-backup true)
::json-rpc/call [{:method "wakuext_backupData" ::json-rpc/call [{:method "wakuext_backupData"
:params [] :params []
:on-error #(do :on-error #(do
+52 -41
View File
@@ -4,24 +4,27 @@
[status-im.i18n.i18n :as i18n] [status-im.i18n.i18n :as i18n]
[status-im.multiaccounts.update.core :as multiaccounts.update] [status-im.multiaccounts.update.core :as multiaccounts.update]
[status-im2.navigation.events :as navigation] [status-im2.navigation.events :as navigation]
[status-im.utils.fx :as fx])) [utils.re-frame :as rf]))
(def address-regex #"enode://[a-zA-Z0-9]+:?(.*)\@\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b:(\d{1,5})") (def address-regex #"enode://[a-zA-Z0-9]+:?(.*)\@\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b:(\d{1,5})")
(defn valid-address? [address] (defn valid-address?
[address]
(re-matches address-regex address)) (re-matches address-regex address))
(defn- build [id bootnode-name address chain] (defn- build
[id bootnode-name address chain]
{:address address {:address address
:chain chain :chain chain
:id (string/replace id "-" "") :id (string/replace id "-" "")
:name bootnode-name}) :name bootnode-name})
(fx/defn fetch [cofx id] (rf/defn fetch
[cofx id]
(let [network (get-in cofx [:db :networks/current-network])] (let [network (get-in cofx [:db :networks/current-network])]
(get-in cofx [:db :multiaccount :custom-bootnodes network id]))) (get-in cofx [:db :multiaccount :custom-bootnodes network id])))
(fx/defn set-input (rf/defn set-input
{:events [:bootnodes.ui/input-changed]} {:events [:bootnodes.ui/input-changed]}
[{:keys [db]} input-key value] [{:keys [db]} input-key value]
{:db (update {:db (update
@@ -35,16 +38,17 @@
:name (string/blank? value) :name (string/blank? value)
:url (not (valid-address? value)))})}) :url (not (valid-address? value)))})})
(fx/defn edit (rf/defn edit
{:events [:bootnodes.ui/add-bootnode-pressed]} {:events [:bootnodes.ui/add-bootnode-pressed]}
[{:keys [db] :as cofx} id] [{:keys [db] :as cofx} id]
(let [{:keys [id (let [{:keys [id
address address
name]} (fetch cofx id) name]}
fxs (fx/merge cofx (fetch cofx id)
(set-input :id id) fxs (rf/merge cofx
(set-input :url (str address)) (set-input :id id)
(set-input :name (str name)))] (set-input :url (str address))
(set-input :name (str name)))]
(assoc fxs :dispatch [:navigate-to :edit-bootnode]))) (assoc fxs :dispatch [:navigate-to :edit-bootnode])))
(defn custom-bootnodes-in-use? (defn custom-bootnodes-in-use?
@@ -52,68 +56,75 @@
(let [network (:networks/current-network db)] (let [network (:networks/current-network db)]
(get-in db [:multiaccount :custom-bootnodes-enabled? network]))) (get-in db [:multiaccount :custom-bootnodes-enabled? network])))
(fx/defn delete [{{:keys [multiaccount] :as db} :db :as cofx} id] (rf/defn delete
(let [network (:networks/current-network db) [{{:keys [multiaccount] :as db} :db :as cofx} id]
(let [network (:networks/current-network db)
new-multiaccount (update-in multiaccount [:custom-bootnodes network] dissoc id)] new-multiaccount (update-in multiaccount [:custom-bootnodes network] dissoc id)]
(fx/merge cofx (rf/merge cofx
{:db (assoc db :multiaccount new-multiaccount)} {:db (assoc db :multiaccount new-multiaccount)}
(multiaccounts.update/multiaccount-update (multiaccounts.update/multiaccount-update
:custom-bootnodes (:custom-bootnodes new-multiaccount) :custom-bootnodes
(:custom-bootnodes new-multiaccount)
{:success-event (when (custom-bootnodes-in-use? cofx) {:success-event (when (custom-bootnodes-in-use? cofx)
[:multiaccounts.update.callback/save-settings-success])})))) [:multiaccounts.update.callback/save-settings-success])}))))
(fx/defn upsert (rf/defn upsert
{:events [:bootnodes.ui/save-pressed] {:events [:bootnodes.ui/save-pressed]
:interceptors [(re-frame/inject-cofx :random-id-generator)]} :interceptors [(re-frame/inject-cofx :random-id-generator)]}
[{{:bootnodes/keys [manage] :keys [multiaccount] :as db} :db [{{:bootnodes/keys [manage] :keys [multiaccount] :as db} :db
random-id-generator :random-id-generator :as cofx}] random-id-generator :random-id-generator
:as cofx}]
(let [{:keys [name id url]} manage (let [{:keys [name id url]} manage
network (:networks/current-network db) network (:networks/current-network db)
bootnode (build bootnode (build
(or (:value id) (random-id-generator)) (or (:value id) (random-id-generator))
(:value name) (:value name)
(:value url) (:value url)
network) network)
new-bootnodes (assoc-in new-bootnodes (assoc-in
(:custom-bootnodes multiaccount) (:custom-bootnodes multiaccount)
[network (:id bootnode)] [network (:id bootnode)]
bootnode)] bootnode)]
(fx/merge cofx (rf/merge cofx
{:db (dissoc db :bootnodes/manage) {:db (dissoc db :bootnodes/manage)
:dispatch [:navigate-back]} :dispatch [:navigate-back]}
(multiaccounts.update/multiaccount-update (multiaccounts.update/multiaccount-update
:custom-bootnodes new-bootnodes :custom-bootnodes
new-bootnodes
{:success-event (when (custom-bootnodes-in-use? cofx) {:success-event (when (custom-bootnodes-in-use? cofx)
[:multiaccounts.update.callback/save-settings-success])})))) [:multiaccounts.update.callback/save-settings-success])}))))
(fx/defn toggle-custom-bootnodes (rf/defn toggle-custom-bootnodes
{:events [:bootnodes.ui/custom-bootnodes-switch-toggled]} {:events [:bootnodes.ui/custom-bootnodes-switch-toggled]}
[{:keys [db] :as cofx} value] [{:keys [db] :as cofx} value]
(let [current-network (:networks/current-network db) (let [current-network (:networks/current-network db)
bootnodes-settings (get-in db [:multiaccount :custom-bootnodes-enabled?])] bootnodes-settings (get-in db [:multiaccount :custom-bootnodes-enabled?])]
(multiaccounts.update/multiaccount-update (multiaccounts.update/multiaccount-update
cofx cofx
:custom-bootnodes-enabled? (assoc bootnodes-settings current-network value) :custom-bootnodes-enabled?
(assoc bootnodes-settings current-network value)
{:success-event [:multiaccounts.update.callback/save-settings-success]}))) {:success-event [:multiaccounts.update.callback/save-settings-success]})))
(fx/defn set-bootnodes-from-qr (rf/defn set-bootnodes-from-qr
{:events [:bootnodes.callback/qr-code-scanned]} {:events [:bootnodes.callback/qr-code-scanned]}
[cofx url] [cofx url]
(assoc (set-input cofx :url (string/trim url)) (assoc (set-input cofx :url (string/trim url))
:dispatch [:navigate-back])) :dispatch
[:navigate-back]))
(fx/defn show-delete-bootnode-confirmation (rf/defn show-delete-bootnode-confirmation
{:events [:bootnodes.ui/delete-pressed]} {:events [:bootnodes.ui/delete-pressed]}
[_ bootnode-id] [_ bootnode-id]
{:ui/show-confirmation {:title (i18n/label :t/delete-bootnode-title) {:ui/show-confirmation {:title (i18n/label :t/delete-bootnode-title)
:content (i18n/label :t/delete-bootnode-are-you-sure) :content (i18n/label :t/delete-bootnode-are-you-sure)
:confirm-button-text (i18n/label :t/delete-bootnode) :confirm-button-text (i18n/label :t/delete-bootnode)
:on-accept #(re-frame/dispatch [:bootnodes.ui/delete-confirmed bootnode-id])}}) :on-accept #(re-frame/dispatch [:bootnodes.ui/delete-confirmed
bootnode-id])}})
(fx/defn delete-bootnode (rf/defn delete-bootnode
{:events [:bootnodes.ui/delete-confirmed]} {:events [:bootnodes.ui/delete-confirmed]}
[cofx bootnode-id] [cofx bootnode-id]
(fx/merge cofx (rf/merge cofx
(delete bootnode-id) (delete bootnode-id)
(navigation/navigate-back))) (navigation/navigate-back)))
+9 -9
View File
@@ -1,15 +1,15 @@
(ns status-im.bottom-sheet.core (ns status-im.bottom-sheet.core
(:require [status-im.utils.fx :as fx])) (:require [utils.re-frame :as rf]))
(fx/defn show-bottom-sheet (rf/defn show-bottom-sheet
[{:keys [db]} {:keys [view options]}] [{:keys [db]} {:keys [view options]}]
{:show-bottom-sheet nil {:show-bottom-sheet nil
:db (assoc db :db (assoc db
:bottom-sheet/show? true :bottom-sheet/show? true
:bottom-sheet/view view :bottom-sheet/view view
:bottom-sheet/options options)}) :bottom-sheet/options options)})
(fx/defn show-bottom-sheet-event (rf/defn show-bottom-sheet-event
{:events [:bottom-sheet/show-sheet]} {:events [:bottom-sheet/show-sheet]}
[cofx view options] [cofx view options]
(show-bottom-sheet (show-bottom-sheet
@@ -17,8 +17,8 @@
{:view view {:view view
:options options})) :options options}))
(fx/defn hide-bottom-sheet (rf/defn hide-bottom-sheet
{:events [:bottom-sheet/hide]} {:events [:bottom-sheet/hide]}
[{:keys [db]}] [{:keys [db]}]
{:hide-bottom-sheet nil {:hide-bottom-sheet nil
:db (assoc db :bottom-sheet/show? false)}) :db (assoc db :bottom-sheet/show? false)})
+195 -152
View File
@@ -1,62 +1,65 @@
(ns status-im.browser.core (ns status-im.browser.core
(:require [clojure.string :as string] (:require ["eth-phishing-detect" :as eth-phishing-detect]
[clojure.string :as string]
[re-frame.core :as re-frame] [re-frame.core :as re-frame]
[status-im.bottom-sheet.core :as bottom-sheet]
[status-im.browser.eip3085 :as eip3085]
[status-im.browser.eip3326 :as eip3326]
[status-im.browser.permissions :as browser.permissions] [status-im.browser.permissions :as browser.permissions]
[status-im.browser.webview-ref :as webview-ref]
[status-im.constants :as constants] [status-im.constants :as constants]
[status-im.ethereum.core :as ethereum] [status-im.ethereum.core :as ethereum]
[status-im.ethereum.ens :as ens] [status-im.ethereum.ens :as ens]
[status-im.ethereum.json-rpc :as json-rpc] [status-im.ethereum.json-rpc :as json-rpc]
[status-im.i18n.i18n :as i18n] [status-im.i18n.i18n :as i18n]
[status-im.multiaccounts.update.core :as multiaccounts.update]
[status-im.native-module.core :as status] [status-im.native-module.core :as status]
[status-im.signing.core :as signing]
[status-im.ui.components.list-selection :as list-selection] [status-im.ui.components.list-selection :as list-selection]
[status-im2.navigation.events :as navigation]
[status-im.utils.fx :as fx]
[status-im.utils.http :as http] [status-im.utils.http :as http]
[status-im.utils.platform :as platform] [status-im.utils.platform :as platform]
[status-im.utils.random :as random] [status-im.utils.random :as random]
[status-im.utils.types :as types] [status-im.utils.types :as types]
[status-im.utils.universal-links.utils :as links] [status-im.utils.universal-links.utils :as links]
[status-im2.navigation.events :as navigation]
[taoensso.timbre :as log] [taoensso.timbre :as log]
[status-im.signing.core :as signing]
[status-im.multiaccounts.update.core :as multiaccounts.update]
[status-im.bottom-sheet.core :as bottom-sheet]
[status-im.browser.webview-ref :as webview-ref]
["eth-phishing-detect" :as eth-phishing-detect]
[utils.debounce :as debounce] [utils.debounce :as debounce]
[status-im.browser.eip3085 :as eip3085] [utils.re-frame :as rf]))
[status-im.browser.eip3326 :as eip3326]))
(fx/defn update-browser-option (rf/defn update-browser-option
[{:keys [db]} option-key option-value] [{:keys [db]} option-key option-value]
{:db (assoc-in db [:browser/options option-key] option-value)}) {:db (assoc-in db [:browser/options option-key] option-value)})
(fx/defn update-browser-options (rf/defn update-browser-options
[{:keys [db]} options] [{:keys [db]} options]
{:db (update db :browser/options merge options)}) {:db (update db :browser/options merge options)})
(defn get-current-browser [db] (defn get-current-browser
[db]
(get-in db [:browser/browsers (get-in db [:browser/options :browser-id])])) (get-in db [:browser/browsers (get-in db [:browser/options :browser-id])]))
(defn get-current-url [{:keys [history history-index] (defn get-current-url
:or {history-index 0}}] [{:keys [history history-index]
:or {history-index 0}}]
(when history (when history
(nth history history-index))) (nth history history-index)))
(defn secure? [{:keys [error? dapp?]} {:keys [url]}] (defn secure?
[{:keys [error? dapp?]} {:keys [url]}]
(or dapp? (or dapp?
(and (not error?) (and (not error?)
(when url (when url
(string/starts-with? url "https://"))))) (string/starts-with? url "https://")))))
(fx/defn remove-browser (rf/defn remove-browser
{:events [:browser.ui/remove-browser-pressed]} {:events [:browser.ui/remove-browser-pressed]}
[{:keys [db]} browser-id] [{:keys [db]} browser-id]
{:db (update-in db [:browser/browsers] dissoc browser-id) {:db (update-in db [:browser/browsers] dissoc browser-id)
::json-rpc/call [{:method "wakuext_deleteBrowser" ::json-rpc/call [{:method "wakuext_deleteBrowser"
:params [browser-id] :params [browser-id]
:on-success #()}]}) :on-success #()}]})
(fx/defn clear-all-browsers (rf/defn clear-all-browsers
{:events [:browser.ui/clear-all-browsers-pressed]} {:events [:browser.ui/clear-all-browsers-pressed]}
[{:keys [db]}] [{:keys [db]}]
{:db (dissoc db :browser/browsers) {:db (dissoc db :browser/browsers)
@@ -65,106 +68,116 @@
:params [browser-id] :params [browser-id]
:on-success #()})}) :on-success #()})})
(defn update-dapp-name [{:keys [name] :as browser}] (defn update-dapp-name
[{:keys [name] :as browser}]
(assoc browser :dapp? false :name (or name (i18n/label :t/browser)))) (assoc browser :dapp? false :name (or name (i18n/label :t/browser))))
(defn check-if-phishing-url [{:keys [history history-index] :as browser}] (defn check-if-phishing-url
[{:keys [history history-index] :as browser}]
(let [history-host (http/url-host (try (nth history history-index) (catch js/Error _)))] (let [history-host (http/url-host (try (nth history history-index) (catch js/Error _)))]
(cond-> browser history-host (assoc :unsafe? (eth-phishing-detect history-host))))) (cond-> browser history-host (assoc :unsafe? (eth-phishing-detect history-host)))))
(defn resolve-ens-contenthash-callback [url] (defn resolve-ens-contenthash-callback
[url]
(if (not (string/blank? url)) (if (not (string/blank? url))
(re-frame/dispatch [:browser.callback/resolve-ens-multihash-success url]) (re-frame/dispatch [:browser.callback/resolve-ens-multihash-success url])
(re-frame/dispatch [:browser.callback/resolve-ens-multihash-error]))) (re-frame/dispatch [:browser.callback/resolve-ens-multihash-error])))
(fx/defn resolve-url (rf/defn resolve-url
[{:keys [db]} {:keys [error? resolved-url]}] [{:keys [db]} {:keys [error? resolved-url]}]
(when (not error?) (when (not error?)
(let [current-url (get-current-url (get-current-browser db)) (let [current-url (get-current-url (get-current-browser db))
host (http/url-host current-url)] host (http/url-host current-url)]
(if (and (not resolved-url) (ens/is-valid-eth-name? host)) (if (and (not resolved-url) (ens/is-valid-eth-name? host))
{:db (update db :browser/options assoc :resolving? true) {:db (update db :browser/options assoc :resolving? true)
:browser/resolve-ens-contenthash {:chain-id (ethereum/chain-id db) :browser/resolve-ens-contenthash {:chain-id (ethereum/chain-id db)
:ens-name host :ens-name host
:cb resolve-ens-contenthash-callback}} :cb resolve-ens-contenthash-callback}}
{:db (update db :browser/options assoc :url (or resolved-url current-url) :resolving? false)})))) {:db (update db :browser/options assoc :url (or resolved-url current-url) :resolving? false)}))))
(fx/defn update-browser (rf/defn update-browser
[{:keys [db]} [{:keys [db]}
{:keys [browser-id] :as browser}] {:keys [browser-id] :as browser}]
(let [updated-browser (-> browser (let [updated-browser (-> browser
(update-dapp-name) (update-dapp-name)
(check-if-phishing-url))] (check-if-phishing-url))]
{:db (update-in db {:db (update-in db
[:browser/browsers browser-id] [:browser/browsers browser-id]
merge updated-browser) merge
::json-rpc/call [{:method "wakuext_addBrowser" updated-browser)
:params [(select-keys updated-browser [:browser-id :timestamp :name :dapp? :history :history-index])] ::json-rpc/call [{:method "wakuext_addBrowser"
:params [(select-keys updated-browser
[:browser-id :timestamp :name :dapp? :history
:history-index])]
:on-success #()}]})) :on-success #()}]}))
(fx/defn store-bookmark (rf/defn store-bookmark
{:events [:browser/store-bookmark]} {:events [:browser/store-bookmark]}
[{:keys [db]} [{:keys [db]}
{:keys [url] :as bookmark}] {:keys [url] :as bookmark}]
{:db (assoc-in db [:bookmarks/bookmarks url] bookmark) {:db (assoc-in db [:bookmarks/bookmarks url] bookmark)
::json-rpc/call [{:method "wakuext_addBookmark" ::json-rpc/call [{:method "wakuext_addBookmark"
:params [bookmark] :params [bookmark]
:on-success #()}]}) :on-success #()}]})
(fx/defn update-bookmark (rf/defn update-bookmark
{:events [:browser/update-bookmark]} {:events [:browser/update-bookmark]}
[{:keys [db] :as cofx} [{:keys [db] :as cofx}
{:keys [url] :as bookmark}] {:keys [url] :as bookmark}]
(let [old-bookmark (get-in db [:bookmarks/bookmarks url]) (let [old-bookmark (get-in db [:bookmarks/bookmarks url])
new-bookmark (merge old-bookmark bookmark)] new-bookmark (merge old-bookmark bookmark)]
(fx/merge cofx {:db (assoc-in db [:bookmarks/bookmarks url] new-bookmark) (rf/merge cofx
::json-rpc/call [{:method "wakuext_updateBookmark" {:db (assoc-in db [:bookmarks/bookmarks url] new-bookmark)
:params [url bookmark] ::json-rpc/call [{:method "wakuext_updateBookmark"
:on-success #()}]}))) :params [url bookmark]
:on-success #()}]})))
(fx/defn delete-bookmark (rf/defn delete-bookmark
{:events [:browser/delete-bookmark]} {:events [:browser/delete-bookmark]}
[{:keys [db] :as cofx} [{:keys [db] :as cofx}
url] url]
(let [old-bookmark (get-in db [:bookmarks/bookmarks url]) (let [old-bookmark (get-in db [:bookmarks/bookmarks url])
removed-bookmark (merge old-bookmark {:removed true})] removed-bookmark (merge old-bookmark {:removed true})]
(fx/merge cofx {:db (update db :bookmarks/bookmarks dissoc url) (rf/merge cofx
::json-rpc/call [{:method "wakuext_removeBookmark" {:db (update db :bookmarks/bookmarks dissoc url)
:params [url] ::json-rpc/call [{:method "wakuext_removeBookmark"
:on-success #()}]}))) :params [url]
:on-success #()}]})))
(defn can-go-back? [{:keys [history-index]}] (defn can-go-back?
[{:keys [history-index]}]
(pos? history-index)) (pos? history-index))
(fx/defn navigate-to-previous-page (rf/defn navigate-to-previous-page
{:events [:browser.ui/previous-page-button-pressed]} {:events [:browser.ui/previous-page-button-pressed]}
[cofx] [cofx]
(let [{:keys [history-index] :as browser} (get-current-browser (:db cofx))] (let [{:keys [history-index] :as browser} (get-current-browser (:db cofx))]
(when (can-go-back? browser) (when (can-go-back? browser)
(fx/merge cofx (rf/merge cofx
(update-browser (assoc browser :history-index (dec history-index))) (update-browser (assoc browser :history-index (dec history-index)))
(resolve-url nil))))) (resolve-url nil)))))
(fx/defn ignore-unsafe (rf/defn ignore-unsafe
{:events [:browser/ignore-unsafe]} {:events [:browser/ignore-unsafe]}
[cofx] [cofx]
(let [browser (get-current-browser (:db cofx)) (let [browser (get-current-browser (:db cofx))
host (http/url-host (get-current-url browser))] host (http/url-host (get-current-url browser))]
(update-browser cofx (assoc browser :ignore-unsafe host)))) (update-browser cofx (assoc browser :ignore-unsafe host))))
(defn can-go-forward? [{:keys [history-index history]}] (defn can-go-forward?
[{:keys [history-index history]}]
(< history-index (dec (count history)))) (< history-index (dec (count history))))
(fx/defn navigate-to-next-page (rf/defn navigate-to-next-page
{:events [:browser.ui/next-page-button-pressed]} {:events [:browser.ui/next-page-button-pressed]}
[cofx] [cofx]
(let [{:keys [history-index] :as browser} (get-current-browser (:db cofx))] (let [{:keys [history-index] :as browser} (get-current-browser (:db cofx))]
(when (can-go-forward? browser) (when (can-go-forward? browser)
(fx/merge cofx (rf/merge cofx
(update-browser (assoc browser :history-index (inc history-index))) (update-browser (assoc browser :history-index (inc history-index)))
(resolve-url nil))))) (resolve-url nil)))))
(fx/defn update-browser-history (rf/defn update-browser-history
[cofx browser url] [cofx browser url]
(let [history-index (:history-index browser) (let [history-index (:history-index browser)
history (:history browser)] history (:history browser)]
@@ -173,73 +186,81 @@
new-index (dec (count new-history))] new-index (dec (count new-history))]
(update-browser cofx (update-browser cofx
(assoc browser (assoc browser
:history new-history :history new-history
:history-index new-index)))))) :history-index new-index))))))
(fx/defn resolve-ens-multihash-success (rf/defn resolve-ens-multihash-success
{:events [:browser.callback/resolve-ens-multihash-success]} {:events [:browser.callback/resolve-ens-multihash-success]}
[{:keys [db] :as cofx} url] [{:keys [db] :as cofx} url]
(let [current-url (get-current-url (get-current-browser db)) (let [current-url (get-current-url (get-current-browser db))
host (http/url-host current-url) host (http/url-host current-url)
path (subs current-url (+ (.indexOf ^js current-url host) (count host))) path (subs current-url (+ (.indexOf ^js current-url host) (count host)))
gateway url] gateway url]
(fx/merge cofx (rf/merge cofx
{:db (-> (update db :browser/options {:db (-> (update db
:browser/options
assoc assoc
:url (str gateway path) :url (str gateway path)
:resolving? false) :resolving? false)
(assoc-in [:browser/options :resolved-ens host] gateway))}))) (assoc-in [:browser/options :resolved-ens host] gateway))})))
(fx/defn resolve-ens-multihash-error (rf/defn resolve-ens-multihash-error
{:events [:browser.callback/resolve-ens-multihash-error]} {:events [:browser.callback/resolve-ens-multihash-error]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(update-browser-options cofx {:url (get-current-url (get-current-browser db)) (update-browser-options cofx
:resolving? false {:url (get-current-url (get-current-browser db))
:error? true})) :resolving? false
:error? true}))
(fx/defn handle-browser-error (rf/defn handle-browser-error
{:events [:browser/error-occured]} {:events [:browser/error-occured]}
[cofx] [cofx]
(fx/merge cofx (rf/merge cofx
(update-browser-option :error? true) (update-browser-option :error? true)
(update-browser-option :loading? false))) (update-browser-option :loading? false)))
(fx/defn handle-pdf (rf/defn handle-pdf
[_ url] [_ url]
(when (and platform/android? (string/ends-with? url ".pdf")) (when (and platform/android? (string/ends-with? url ".pdf"))
{:browser/show-web-browser-selection url})) {:browser/show-web-browser-selection url}))
(fx/defn handle-message-link (rf/defn handle-message-link
{:events [:browser.ui/message-link-pressed]} {:events [:browser.ui/message-link-pressed]}
[_ link] [_ link]
(if (links/universal-link? link) (if (links/universal-link? link)
{:dispatch [:universal-links/handle-url link]} {:dispatch [:universal-links/handle-url link]}
{:browser/show-browser-selection link})) {:browser/show-browser-selection link}))
(fx/defn update-browser-on-nav-change (rf/defn update-browser-on-nav-change
[cofx url error?] [cofx url error?]
(let [browser (get-current-browser (:db cofx)) (let [browser (get-current-browser (:db cofx))
options (get-in cofx [:db :browser/options]) options (get-in cofx [:db :browser/options])
current-url (:url options)] current-url (:url options)]
(when (and browser (not (string/blank? url)) (not= "about:blank" url) (not= current-url url) (not= (str current-url "/") url)) (when (and browser
(not (string/blank? url))
(not= "about:blank" url)
(not= current-url url)
(not= (str current-url "/") url))
(let [resolved-ens (first (filter (fn [v] (let [resolved-ens (first (filter (fn [v]
(not= (.indexOf ^js url (second v)) -1)) (not= (.indexOf ^js url (second v)) -1))
(:resolved-ens options))) (:resolved-ens options)))
resolved-url (if resolved-ens resolved-url (if resolved-ens
(http/normalize-url (string/replace url (second resolved-ens) (first resolved-ens))) (http/normalize-url (string/replace url
(second resolved-ens)
(first resolved-ens)))
url)] url)]
(fx/merge cofx (rf/merge cofx
(update-browser-history browser resolved-url) (update-browser-history browser resolved-url)
(handle-pdf url) (handle-pdf url)
(resolve-url {:error? error? :resolved-url (when resolved-ens url)})))))) (resolve-url {:error? error? :resolved-url (when resolved-ens url)}))))))
(fx/defn update-browser-name (rf/defn update-browser-name
[cofx title] [cofx title]
(let [browser (get-current-browser (:db cofx))] (let [browser (get-current-browser (:db cofx))]
(when (and browser (not (:dapp? browser)) title (not (string/blank? title))) (when (and browser (not (:dapp? browser)) title (not (string/blank? title)))
(update-browser cofx (assoc browser :name title))))) (update-browser cofx (assoc browser :name title)))))
(fx/defn navigation-state-changed (rf/defn navigation-state-changed
{:events [:browser/navigation-state-changed]} {:events [:browser/navigation-state-changed]}
[cofx event error?] [cofx event error?]
(let [{:strs [url loading title]} (js->clj event) (let [{:strs [url loading title]} (js->clj event)
@@ -247,59 +268,61 @@
(if (links/universal-link? url) (if (links/universal-link? url)
(when-not (and deep-link? platform/ios?) ;; ios webview handles this (when-not (and deep-link? platform/ios?) ;; ios webview handles this
{:dispatch [:universal-links/handle-url url]}) {:dispatch [:universal-links/handle-url url]})
(fx/merge cofx (rf/merge cofx
(update-browser-option :loading? loading) (update-browser-option :loading? loading)
(update-browser-name title) (update-browser-name title)
(update-browser-on-nav-change url error?))))) (update-browser-on-nav-change url error?)))))
(fx/defn open-url-in-current-browser (rf/defn open-url-in-current-browser
"Opens a url in the current browser, which mean no new entry is added to the home page "Opens a url in the current browser, which mean no new entry is added to the home page
and history of the current browser is updated so that the user can navigate back to the and history of the current browser is updated so that the user can navigate back to the
origin url" origin url"
{:events [:browser.ui/url-submitted]} {:events [:browser.ui/url-submitted]}
[cofx url] [cofx url]
(let [browser (get-current-browser (:db cofx)) (let [browser (get-current-browser (:db cofx))
normalized-url (http/normalize-and-decode-url url)] normalized-url (http/normalize-and-decode-url url)]
(if (links/universal-link? normalized-url) (if (links/universal-link? normalized-url)
{:dispatch [:universal-links/handle-url normalized-url]} {:dispatch [:universal-links/handle-url normalized-url]}
(fx/merge cofx (rf/merge cofx
(update-browser-option :url-editing? false) (update-browser-option :url-editing? false)
(update-browser-history browser normalized-url) (update-browser-history browser normalized-url)
(resolve-url nil))))) (resolve-url nil)))))
(fx/defn open-url (rf/defn open-url
"Opens a url in the browser. If a host can be extracted from the url and "Opens a url in the browser. If a host can be extracted from the url and
there is already a browser for this host, this browser is reused there is already a browser for this host, this browser is reused
If the browser is reused, the history is flushed" If the browser is reused, the history is flushed"
{:events [:browser.ui/open-url]} {:events [:browser.ui/open-url]}
[{:keys [db] :as cofx} url] [{:keys [db] :as cofx} url]
(let [normalized-url (http/normalize-and-decode-url url) (let [normalized-url (http/normalize-and-decode-url url)
browser {:browser-id (random/id) browser {:browser-id (random/id)
:history-index 0 :history-index 0
:history [normalized-url]}] :history [normalized-url]}]
(if (links/universal-link? normalized-url) (if (links/universal-link? normalized-url)
{:dispatch [:universal-links/handle-url normalized-url]} {:dispatch [:universal-links/handle-url normalized-url]}
(fx/merge cofx (rf/merge cofx
{:db (assoc db :browser/options {:db (assoc db
:browser/options
{:browser-id (:browser-id browser)})} {:browser-id (:browser-id browser)})}
(navigation/change-tab :browser) (navigation/change-tab :browser)
(navigation/set-stack-root :browser-stack :browser) (navigation/set-stack-root :browser-stack :browser)
(update-browser browser) (update-browser browser)
(resolve-url nil))))) (resolve-url nil)))))
(fx/defn open-existing-browser (rf/defn open-existing-browser
"Opens an existing browser with it's history" "Opens an existing browser with it's history"
{:events [:browser.ui/browser-item-selected]} {:events [:browser.ui/browser-item-selected]}
[{:keys [db] :as cofx} browser-id] [{:keys [db] :as cofx} browser-id]
(let [browser (get-in db [:browser/browsers browser-id])] (let [browser (get-in db [:browser/browsers browser-id])]
(fx/merge cofx (rf/merge cofx
{:db (assoc db :browser/options {:db (assoc db
:browser/options
{:browser-id browser-id})} {:browser-id browser-id})}
(update-browser browser) (update-browser browser)
(navigation/set-stack-root :browser-stack :browser) (navigation/set-stack-root :browser-stack :browser)
(resolve-url nil)))) (resolve-url nil))))
(fx/defn web3-error-callback (rf/defn web3-error-callback
{:events [:browser.dapp/transaction-on-error]} {:events [:browser.dapp/transaction-on-error]}
[_ message-id message] [_ message-id message]
{:browser/send-to-bridge {:browser/send-to-bridge
@@ -307,7 +330,7 @@
:messageId message-id :messageId message-id
:error message}}) :error message}})
(fx/defn dapp-complete-transaction (rf/defn dapp-complete-transaction
{:events [:browser.dapp/transaction-on-result]} {:events [:browser.dapp/transaction-on-result]}
[_ message-id id result] [_ message-id id result]
;;TODO check and test id ;;TODO check and test id
@@ -336,21 +359,24 @@
(ethereum/address? second-param) (ethereum/address? second-param)
[second-param (if typed? first-param (normalize-message first-param))])))) [second-param (if typed? first-param (normalize-message first-param))]))))
(fx/defn send-to-bridge (rf/defn send-to-bridge
{:events [:browser.callback/call-rpc]} {:events [:browser.callback/call-rpc]}
[_ message] [_ message]
{:browser/send-to-bridge message}) {:browser/send-to-bridge message})
(defn web3-sign-message? [method] (defn web3-sign-message?
[method]
(#{constants/web3-sign-typed-data constants/web3-sign-typed-data-v3 constants/web3-sign-typed-data-v4 (#{constants/web3-sign-typed-data constants/web3-sign-typed-data-v3 constants/web3-sign-typed-data-v4
constants/web3-personal-sign constants/web3-personal-sign
constants/web3-eth-sign constants/web3-keycard-sign-typed-data} method)) constants/web3-eth-sign constants/web3-keycard-sign-typed-data}
method))
(fx/defn web3-send-async (rf/defn web3-send-async
[cofx dapp-name {:keys [method params id] :as payload} message-id] [cofx dapp-name {:keys [method params id] :as payload} message-id]
(let [message? (web3-sign-message? method) (let [message? (web3-sign-message? method)
dapps-address (get-in cofx [:db :multiaccount :dapps-address]) dapps-address (get-in cofx [:db :multiaccount :dapps-address])
typed? (and (not= constants/web3-personal-sign method) (not= constants/web3-eth-sign method))] typed? (and (not= constants/web3-personal-sign method)
(not= constants/web3-eth-sign method))]
(if (or message? (= constants/web3-send-transaction method)) (if (or message? (= constants/web3-send-transaction method))
(let [[address data] (cond (and (= method constants/web3-keycard-sign-typed-data) (let [[address data] (cond (and (= method constants/web3-keycard-sign-typed-data)
(not (vector? params))) (not (vector? params)))
@@ -359,34 +385,38 @@
message? (normalize-sign-message-params params typed?) message? (normalize-sign-message-params params typed?)
:else [nil nil])] :else [nil nil])]
(when (or (not message?) (and address data)) (when (or (not message?) (and address data))
(signing/sign cofx (merge (signing/sign cofx
(if message? (merge
{:message {:address address (if message?
:data data {:message {:address address
:v4 (= constants/web3-sign-typed-data-v4 method) :data data
:typed? typed? :v4 (= constants/web3-sign-typed-data-v4 method)
:pinless? (= method constants/web3-keycard-sign-typed-data) :typed? typed?
:from dapps-address}} :pinless? (= method constants/web3-keycard-sign-typed-data)
{:tx-obj (-> params :from dapps-address}}
first {:tx-obj (-> params
(update :from #(or % dapps-address)) first
(dissoc :gasPrice))}) (update :from #(or % dapps-address))
{:on-result [:browser.dapp/transaction-on-result message-id id] (dissoc :gasPrice))})
:on-error [:browser.dapp/transaction-on-error message-id]})))) {:on-result [:browser.dapp/transaction-on-result message-id id]
:on-error [:browser.dapp/transaction-on-error message-id]}))))
(cond (cond
(#{"eth_accounts" "eth_coinbase"} method) (#{"eth_accounts" "eth_coinbase"} method)
(send-to-bridge cofx {:type constants/web3-send-async-callback (send-to-bridge
:messageId message-id cofx
:result {:jsonrpc "2.0" {:type constants/web3-send-async-callback
:id (int id) :messageId message-id
:result (if (= method "eth_coinbase") dapps-address [dapps-address])}}) :result {:jsonrpc "2.0"
:id (int id)
:result (if (= method "eth_coinbase") dapps-address [dapps-address])}})
(= method "personal_ecRecover") (= method "personal_ecRecover")
{:signing.fx/recover-message {:params {:message (first params) {:signing.fx/recover-message {:params {:message (first params)
:signature (second params)} :signature (second params)}
:on-completed #(re-frame/dispatch [:browser.callback/call-rpc :on-completed #(re-frame/dispatch
{:type constants/web3-send-async-callback [:browser.callback/call-rpc
:messageId message-id {:type constants/web3-send-async-callback
:result (types/json->clj %)}])}} :messageId message-id
:result (types/json->clj %)}])}}
(= method "wallet_switchEthereumChain") (= method "wallet_switchEthereumChain")
(eip3326/handle-switch-ethereum-chain cofx dapp-name id message-id (first params)) (eip3326/handle-switch-ethereum-chain cofx dapp-name id message-id (first params))
@@ -401,7 +431,8 @@
:error %1 :error %1
:result %2}])]})))) :result %2}])]}))))
(fx/defn handle-no-permissions [cofx {:keys [method id]} message-id] (rf/defn handle-no-permissions
[cofx {:keys [method id]} message-id]
(if (= method "eth_accounts") (if (= method "eth_accounts")
;; eth_accounts returns empty array for compatibility with meta-mask ;; eth_accounts returns empty array for compatibility with meta-mask
(send-to-bridge cofx (send-to-bridge cofx
@@ -420,41 +451,48 @@
"keycard_signTypedData" "keycard_signTypedData"
"eth_signTypedData" "personal_sign" "personal_ecRecover"}) "eth_signTypedData" "personal_sign" "personal_ecRecover"})
(defn has-permissions? [{:dapps/keys [permissions]} dapp-name method] (defn has-permissions?
[{:dapps/keys [permissions]} dapp-name method]
(boolean (boolean
(and (permissioned-method method) (and (permissioned-method method)
(not (some #{constants/dapp-permission-web3} (get-in permissions [dapp-name :permissions])))))) (not (some #{constants/dapp-permission-web3} (get-in permissions [dapp-name :permissions]))))))
(fx/defn web3-send-async-read-only (rf/defn web3-send-async-read-only
[{:keys [db] :as cofx} dapp-name {:keys [method] :as payload} message-id] [{:keys [db] :as cofx} dapp-name {:keys [method] :as payload} message-id]
(if (has-permissions? db dapp-name method) (if (has-permissions? db dapp-name method)
(handle-no-permissions cofx payload message-id) (handle-no-permissions cofx payload message-id)
(web3-send-async cofx dapp-name payload message-id))) (web3-send-async cofx dapp-name payload message-id)))
(fx/defn handle-scanned-qr-code (rf/defn handle-scanned-qr-code
{:events [:browser.bridge.callback/qr-code-scanned]} {:events [:browser.bridge.callback/qr-code-scanned]}
[cofx data {{:keys [dapp-name permission message-id]} :data}] [cofx data {{:keys [dapp-name permission message-id]} :data}]
(fx/merge (assoc-in cofx [:db :browser/options :yielding-control?] false) (rf/merge (assoc-in cofx [:db :browser/options :yielding-control?] false)
(browser.permissions/send-response-to-bridge permission message-id true data) (browser.permissions/send-response-to-bridge permission message-id true data)
(browser.permissions/process-next-permission dapp-name) (browser.permissions/process-next-permission dapp-name)
(navigation/navigate-back))) (navigation/navigate-back)))
(fx/defn handle-canceled-qr-code (rf/defn handle-canceled-qr-code
{:events [:browser.bridge.callback/qr-code-canceled]} {:events [:browser.bridge.callback/qr-code-canceled]}
[cofx {{:keys [dapp-name permission message-id]} :data}] [cofx {{:keys [dapp-name permission message-id]} :data}]
(fx/merge (assoc-in cofx [:db :browser/options :yielding-control?] false) (rf/merge (assoc-in cofx [:db :browser/options :yielding-control?] false)
(browser.permissions/send-response-to-bridge permission message-id true nil) (browser.permissions/send-response-to-bridge permission message-id true nil)
(browser.permissions/process-next-permission dapp-name))) (browser.permissions/process-next-permission dapp-name)))
(fx/defn process-bridge-message (rf/defn process-bridge-message
{:events [:browser/bridge-message-received]} {:events [:browser/bridge-message-received]}
[{:keys [db] :as cofx} message] [{:keys [db] :as cofx} message]
(let [browser (get-current-browser db) (let [browser (get-current-browser
url-original (get-current-url browser) db)
data (types/json->clj message) url-original (get-current-url
browser)
data (types/json->clj
message)
{{:keys [url]} :navState :keys [type permission payload messageId params]} data {{:keys [url]} :navState :keys [type permission payload messageId params]} data
{:keys [dapp? name]} browser {:keys [dapp? name]} browser
dapp-name (if dapp? name (http/url-host url-original))] dapp-name (if dapp?
name
(http/url-host
url-original))]
(cond (cond
(and (= type constants/history-state-changed) (and (= type constants/history-state-changed)
(not= "about:blank" url)) (not= "about:blank" url))
@@ -474,10 +512,13 @@
(re-frame/reg-fx (re-frame/reg-fx
:browser/send-to-bridge :browser/send-to-bridge
(fn [message] (fn [message]
(let [^js webview @webview-ref/webview-ref (let
msg (str "(function() { var __send = function() { if (ReactNativeWebView.onMessage) { ReactNativeWebView.onMessage('" [^js webview @webview-ref/webview-ref
(types/clj->json message) msg
"');} else {setTimeout(__send, 0)}}; __send();})();")] (str
"(function() { var __send = function() { if (ReactNativeWebView.onMessage) { ReactNativeWebView.onMessage('"
(types/clj->json message)
"');} else {setTimeout(__send, 0)}}; __send();})();")]
(when (and message webview) (when (and message webview)
(.injectJavaScript webview msg))))) (.injectJavaScript webview msg)))))
@@ -508,59 +549,61 @@
(fn [] (fn []
(status/clear-web-data))) (status/clear-web-data)))
(defn share-link [url] (defn share-link
[url]
(let [link (links/generate-link :browse :external url) (let [link (links/generate-link :browse :external url)
message (i18n/label :t/share-dapp-text {:link link})] message (i18n/label :t/share-dapp-text {:link link})]
(list-selection/open-share {:message message}))) (list-selection/open-share {:message message})))
(fx/defn dapps-account-selected (rf/defn dapps-account-selected
{:events [:dapps-account-selected]} {:events [:dapps-account-selected]}
[{:keys [db] :as cofx} address] [{:keys [db] :as cofx} address]
(fx/merge cofx (rf/merge cofx
{:browser/clear-web-data nil} {:browser/clear-web-data nil}
(bottom-sheet/hide-bottom-sheet) (bottom-sheet/hide-bottom-sheet)
(browser.permissions/clear-dapps-permissions) (browser.permissions/clear-dapps-permissions)
(multiaccounts.update/multiaccount-update :dapps-address address {}) (multiaccounts.update/multiaccount-update :dapps-address address {})
#(when (= (:view-id db) :browser) #(when (= (:view-id db) :browser)
(merge (navigation/navigate-back %) (merge (navigation/navigate-back %)
{:dispatch [:browser.ui/browser-item-selected (get-in db [:browser/options :browser-id])]})))) {:dispatch [:browser.ui/browser-item-selected
(get-in db [:browser/options :browser-id])]}))))
(fx/defn open-empty-tab (rf/defn open-empty-tab
{:events [:browser.ui/open-empty-tab]} {:events [:browser.ui/open-empty-tab]}
[cofx] [cofx]
(debounce/clear :browser/navigation-state-changed) (debounce/clear :browser/navigation-state-changed)
(navigation/set-stack-root cofx :browser-stack :empty-tab)) (navigation/set-stack-root cofx :browser-stack :empty-tab))
(fx/defn url-input-pressed (rf/defn url-input-pressed
{:events [:browser.ui/url-input-pressed]} {:events [:browser.ui/url-input-pressed]}
[cofx _] [cofx _]
(update-browser-option cofx :url-editing? true)) (update-browser-option cofx :url-editing? true))
(fx/defn url-input-blured (rf/defn url-input-blured
{:events [:browser.ui/url-input-blured]} {:events [:browser.ui/url-input-blured]}
[cofx] [cofx]
(update-browser-option cofx :url-editing? false)) (update-browser-option cofx :url-editing? false))
(fx/defn lock-pressed (rf/defn lock-pressed
{:events [:browser.ui/lock-pressed]} {:events [:browser.ui/lock-pressed]}
[cofx secure?] [cofx secure?]
(update-browser-option cofx :show-tooltip (if secure? :secure :not-secure))) (update-browser-option cofx :show-tooltip (if secure? :secure :not-secure)))
(fx/defn close-tooltip-pressed (rf/defn close-tooltip-pressed
{:events [:browser.ui/close-tooltip-pressed]} {:events [:browser.ui/close-tooltip-pressed]}
[cofx] [cofx]
(update-browser-option cofx :show-tooltip nil)) (update-browser-option cofx :show-tooltip nil))
(fx/defn loading-started (rf/defn loading-started
{:events [:browser/loading-started]} {:events [:browser/loading-started]}
[cofx] [cofx]
(update-browser-options cofx {:error? false :loading? true})) (update-browser-options cofx {:error? false :loading? true}))
(fx/defn handle-bookmarks (rf/defn handle-bookmarks
[{:keys [db]} bookmarks] [{:keys [db]} bookmarks]
(let [changed-bookmarks (reduce (fn [acc {:keys [url] :as bookmark}] (let [changed-bookmarks (reduce (fn [acc {:keys [url] :as bookmark}]
(assoc acc url bookmark)) (assoc acc url bookmark))
{} {}
bookmarks) bookmarks)
stored-bookmarks (get-in db [:bookmarks/bookmarks])] stored-bookmarks (get-in db [:bookmarks/bookmarks])]
{:db (assoc-in db [:bookmarks/bookmarks] (merge stored-bookmarks changed-bookmarks))})) {:db (assoc-in db [:bookmarks/bookmarks] (merge stored-bookmarks changed-bookmarks))}))
+51 -44
View File
@@ -1,69 +1,76 @@
;reference https://eips.ethereum.org/EIPS/eip-3085 EIP-3085: Wallet Add Ethereum Chain RPC Method (`wallet_addEthereumChain`) ;reference https://eips.ethereum.org/EIPS/eip-3085 EIP-3085: Wallet Add Ethereum Chain RPC Method
;(`wallet_addEthereumChain`)
(ns status-im.browser.eip3085 (ns status-im.browser.eip3085
(:require [status-im.constants :as constants] (:require [clojure.string :as string]
[clojure.string :as string]
[re-frame.core :as re-frame] [re-frame.core :as re-frame]
[status-im.utils.fx :as fx] [status-im.constants :as constants]
[status-im.network.core :as network]
[taoensso.timbre :as log]
[status-im.utils.random :as random]
[status-im.ethereum.json-rpc :as json-rpc] [status-im.ethereum.json-rpc :as json-rpc]
[status-im.ui.screens.browser.eip3085.sheet :as sheet])) [status-im.network.core :as network]
[status-im.ui.screens.browser.eip3085.sheet :as sheet]
[status-im.utils.random :as random]
[taoensso.timbre :as log]
[utils.re-frame :as rf]))
(fx/defn send-success-call-to-bridge (rf/defn send-success-call-to-bridge
{:events [:eip3085/send-success-call-to-bridge]} {:events [:eip3085/send-success-call-to-bridge]}
[_ id messageId] [_ id messageId]
{:browser/send-to-bridge {:type constants/web3-send-async-callback {:browser/send-to-bridge {:type constants/web3-send-async-callback
:messageId messageId :messageId messageId
:result {:jsonrpc "2.0" :result {:jsonrpc "2.0"
:id (int id) :id (int id)
:result nil}}}) :result nil}}})
(fx/defn allow-permission (rf/defn allow-permission
{:events [:eip3085.ui/dapp-permission-allowed]} {:events [:eip3085.ui/dapp-permission-allowed]}
[{:keys [db] :as cofx} message-id {:keys [new-networks id]}] [{:keys [db] :as cofx} message-id {:keys [new-networks id]}]
{:db (assoc db :networks/networks new-networks) {:db (assoc db :networks/networks new-networks)
::json-rpc/call [{:method "settings_saveSetting" ::json-rpc/call [{:method "settings_saveSetting"
:params [:networks/networks (vals new-networks)] :params [:networks/networks (vals new-networks)]
:on-success #(re-frame/dispatch [:eip3085/send-success-call-to-bridge cofx id message-id]) :on-success #(re-frame/dispatch [:eip3085/send-success-call-to-bridge cofx id
message-id])
:on-error #(log/error "failed to perform settings_saveSetting" %)}] :on-error #(log/error "failed to perform settings_saveSetting" %)}]
:dispatch [:bottom-sheet/hide]}) :dispatch [:bottom-sheet/hide]})
(fx/defn deny-permission (rf/defn deny-permission
{:events [:eip3085.ui/dapp-permission-denied]} {:events [:eip3085.ui/dapp-permission-denied]}
[_ message-id _] [_ message-id _]
{:browser/send-to-bridge {:type constants/web3-send-async-callback {:browser/send-to-bridge {:type constants/web3-send-async-callback
:messageId message-id :messageId message-id
:error {:code 4001 :error {:code 4001
:message "User rejected the request."}} :message "User rejected the request."}}
:dispatch [:bottom-sheet/hide]}) :dispatch [:bottom-sheet/hide]})
(fx/defn handle-add-ethereum-chain (rf/defn handle-add-ethereum-chain
{:events [:eip3085/handle-add-ethereum-chain]} {:events [:eip3085/handle-add-ethereum-chain]}
[{{:networks/keys [networks] :as db} :db :as cofx} [{{:networks/keys [networks] :as db} :db :as cofx}
dapp-name id message-id {:keys [chainId blockExplorerUrls chainName iconUrls nativeCurrency rpcUrls] :as params}] dapp-name id message-id
(let [manage {:name {:value chainName} {:keys [chainId blockExplorerUrls chainName iconUrls nativeCurrency rpcUrls] :as params}]
:symbol {:value (:symbol nativeCurrency)} (let [manage {:name {:value chainName}
:url {:value (first rpcUrls)} :symbol {:value (:symbol nativeCurrency)}
:url {:value (first rpcUrls)}
:network-id {:value chainId} :network-id {:value chainId}
:chain {:value :custom}}] :chain {:value :custom}}]
(if (network/valid-manage? manage) (if (network/valid-manage? manage)
(let [{:keys [name url chain network-id symbol]} manage (let [{:keys [name url chain network-id symbol]} manage
random-id (string/replace (random/id) "-" "") random-id (string/replace (random/id) "-" "")
network (network/new-network random-id network (network/new-network random-id
(:value name) (:value name)
(:value symbol) (:value symbol)
(:value url) (:value url)
(:value chain) (:value chain)
(:value network-id)) (:value network-id))
new-networks (assoc networks random-id network) new-networks (assoc networks random-id network)
params (assoc params :new-networks new-networks :id id :new-network network)] params (assoc params
:new-networks new-networks
:id id
:new-network network)]
(if (network/chain-id-available? networks network) (if (network/chain-id-available? networks network)
{:dispatch [:bottom-sheet/show-sheet {:content (fn [] {:dispatch [:bottom-sheet/show-sheet
[sheet/permissions-panel dapp-name message-id params])}]} {:content (fn []
[sheet/permissions-panel dapp-name message-id params])}]}
(send-success-call-to-bridge cofx id message-id))) (send-success-call-to-bridge cofx id message-id)))
{:browser/send-to-bridge {:type constants/web3-send-async-callback {:browser/send-to-bridge {:type constants/web3-send-async-callback
:messageId message-id :messageId message-id
:error {:code -32602 :error {:code -32602
:message "invalid network parameters"}}}))) :message "invalid network parameters"}}})))
+37 -26
View File
@@ -1,43 +1,54 @@
;reference https://eips.ethereum.org/EIPS/eip-3326 EIP-3326: Wallet Switch Ethereum Chain RPC Method (`wallet_switchEthereumChain`) ;reference https://eips.ethereum.org/EIPS/eip-3326 EIP-3326: Wallet Switch Ethereum Chain RPC Method
;(`wallet_switchEthereumChain`)
(ns status-im.browser.eip3326 (ns status-im.browser.eip3326
(:require [status-im.constants :as constants] (:require [status-im.constants :as constants]
[status-im.utils.fx :as fx]
[status-im.ethereum.core :as ethereum] [status-im.ethereum.core :as ethereum]
[status-im.ui.screens.browser.eip3326.sheet :as sheet])) [status-im.ui.screens.browser.eip3326.sheet :as sheet]
[utils.re-frame :as rf]))
(fx/defn deny-permission (rf/defn deny-permission
{:events [:eip3326.ui/dapp-permission-denied]} {:events [:eip3326.ui/dapp-permission-denied]}
[{:keys [db] :as cofx} message-id _] [{:keys [db] :as cofx} message-id _]
{:browser/send-to-bridge {:type constants/web3-send-async-callback {:browser/send-to-bridge {:type constants/web3-send-async-callback
:messageId message-id :messageId message-id
:error {:code 4001 :error {:code 4001
:message "User rejected the request."}} :message "User rejected the request."}}
:dispatch [:bottom-sheet/hide]}) :dispatch [:bottom-sheet/hide]})
(fx/defn handle-switch-ethereum-chain (rf/defn handle-switch-ethereum-chain
{:events [:eip3326/handle-switch-ethereum-chain]} {:events [:eip3326/handle-switch-ethereum-chain]}
[{:keys [db] :as cofx} dapp-name id message-id {:keys [chainId] :as params}] [{:keys [db] :as cofx} dapp-name id message-id {:keys [chainId] :as params}]
(let [target-chain-id (js/parseInt chainId 16) (let [target-chain-id (js/parseInt chainId 16)
networks (vals (get-in db [:networks/networks])) networks (vals (get-in db [:networks/networks]))
exist-chain-ids (set (map ethereum/network->chain-id networks)) exist-chain-ids (set (map ethereum/network->chain-id networks))
current-chain-id (ethereum/chain-id db)] current-chain-id (ethereum/chain-id db)]
(if (exist-chain-ids target-chain-id) (if (exist-chain-ids target-chain-id)
(if (= current-chain-id target-chain-id) (if (= current-chain-id target-chain-id)
{:browser/send-to-bridge {:type constants/web3-send-async-callback {:browser/send-to-bridge {:type constants/web3-send-async-callback
:messageId message-id :messageId message-id
:result {:jsonrpc "2.0" :result {:jsonrpc "2.0"
:id (int id) :id (int id)
:result nil}}} :result nil}}}
(let [target-network (first (filter #(= (ethereum/network->chain-id %1) target-chain-id) networks)) (let [target-network (first (filter #(= (ethereum/network->chain-id %1) target-chain-id)
networks))
target-network-id (:id target-network) target-network-id (:id target-network)
current-network (ethereum/current-network db) current-network (ethereum/current-network db)
network-from (:name current-network) network-from (:name current-network)
network-to (:name target-network) network-to (:name target-network)
params (assoc params :target-network-id target-network-id :network-from network-from :network-to network-to)] params (assoc params
{:dispatch [:bottom-sheet/show-sheet {:content (fn [] :target-network-id target-network-id
[sheet/permissions-panel dapp-name message-id params])}]})) :network-from network-from
{:browser/send-to-bridge {:type constants/web3-send-async-callback :network-to network-to)]
{:dispatch [:bottom-sheet/show-sheet
{:content (fn []
[sheet/permissions-panel dapp-name message-id params])}]}))
{:browser/send-to-bridge {:type constants/web3-send-async-callback
:messageId message-id :messageId message-id
:error {:code 4902 :error
:message (str "Unrecognized chain ID: " target-chain-id ". Try adding the chain using wallet_addEthereumChain first.")}}}))) {:code 4902
:message
(str
"Unrecognized chain ID: "
target-chain-id
". Try adding the chain using wallet_addEthereumChain first.")}}})))
+57 -51
View File
@@ -2,51 +2,53 @@
(:require [status-im.constants :as constants] (:require [status-im.constants :as constants]
[status-im.ethereum.json-rpc :as json-rpc] [status-im.ethereum.json-rpc :as json-rpc]
[status-im.i18n.i18n :as i18n] [status-im.i18n.i18n :as i18n]
[status-im2.navigation.events :as navigation]
[status-im.qr-scanner.core :as qr-scanner] [status-im.qr-scanner.core :as qr-scanner]
[status-im.utils.fx :as fx])) [status-im2.navigation.events :as navigation]
[utils.re-frame :as rf]))
(declare process-next-permission) (declare process-next-permission)
(declare send-response-to-bridge) (declare send-response-to-bridge)
(def supported-permissions (def supported-permissions
{constants/dapp-permission-qr-code {:yield-control? true {constants/dapp-permission-qr-code {:yield-control? true
:allowed? true} :allowed? true}
constants/dapp-permission-contact-code {:type :profile constants/dapp-permission-contact-code {:type :profile
:title (i18n/label :t/wants-to-access-profile) :title (i18n/label :t/wants-to-access-profile)
:description (i18n/label :t/your-contact-code) :description (i18n/label :t/your-contact-code)
:icon :main-icons/profile} :icon :main-icons/profile}
constants/dapp-permission-web3 {:type :wallet constants/dapp-permission-web3 {:type :wallet
:title (i18n/label :t/dapp-would-like-to-connect-wallet) :title (i18n/label :t/dapp-would-like-to-connect-wallet)
:description (i18n/label :t/allowing-authorizes-this-dapp) :description (i18n/label :t/allowing-authorizes-this-dapp)
:icon :main-icons/wallet}}) :icon :main-icons/wallet}})
(fx/defn permission-yield-control (rf/defn permission-yield-control
[{:keys [db] :as cofx} dapp-name permission message-id params] [{:keys [db] :as cofx} dapp-name permission message-id params]
(cond (cond
(= permission constants/dapp-permission-qr-code) (= permission constants/dapp-permission-qr-code)
(fx/merge (assoc-in cofx [:db :browser/options :yielding-control?] true) (rf/merge (assoc-in cofx [:db :browser/options :yielding-control?] true)
(qr-scanner/scan-qr-code {:handler :browser.bridge.callback/qr-code-scanned (qr-scanner/scan-qr-code {:handler :browser.bridge.callback/qr-code-scanned
:cancel-handler :browser.bridge.callback/qr-code-canceled :cancel-handler :browser.bridge.callback/qr-code-canceled
:data {:dapp-name dapp-name :data {:dapp-name dapp-name
:permission permission :permission permission
:message-id message-id}})))) :message-id message-id}}))))
(fx/defn permission-show-permission (rf/defn permission-show-permission
[{:keys [db] :as cofx} dapp-name permission message-id yield-control?] [{:keys [db] :as cofx} dapp-name permission message-id yield-control?]
{:db (assoc-in db [:browser/options :show-permission] {:db (assoc-in db
{:requested-permission permission [:browser/options :show-permission]
:message-id message-id {:requested-permission permission
:dapp-name dapp-name :message-id message-id
:yield-control? yield-control?})}) :dapp-name dapp-name
:yield-control? yield-control?})})
(defn get-permission-data [cofx allowed-permission] (defn get-permission-data
[cofx allowed-permission]
(let [multiaccount (get-in cofx [:db :multiaccount])] (let [multiaccount (get-in cofx [:db :multiaccount])]
(get {constants/dapp-permission-contact-code (:public-key multiaccount) (get {constants/dapp-permission-contact-code (:public-key multiaccount)
constants/dapp-permission-web3 [(:dapps-address multiaccount)]} constants/dapp-permission-web3 [(:dapps-address multiaccount)]}
allowed-permission))) allowed-permission)))
(fx/defn send-response-to-bridge (rf/defn send-response-to-bridge
"Send response to the bridge. If the permission is allowed, send data associated "Send response to the bridge. If the permission is allowed, send data associated
with the permission" with the permission"
[{:keys [db] :as cofx} permission message-id allowed? data] [{:keys [db] :as cofx} permission message-id allowed? data]
@@ -57,7 +59,7 @@
allowed? allowed?
(assoc :data data))}) (assoc :data data))})
(fx/defn update-dapp-permissions (rf/defn update-dapp-permissions
[{:keys [db]} dapp-name permission allowed?] [{:keys [db]} dapp-name permission allowed?]
(let [dapp-permissions-set (set (get-in db [:dapps/permissions dapp-name :permissions])) (let [dapp-permissions-set (set (get-in db [:dapps/permissions dapp-name :permissions]))
allowed-permissions-set (if allowed? allowed-permissions-set (if allowed?
@@ -65,28 +67,28 @@
(disj dapp-permissions-set permission)) (disj dapp-permissions-set permission))
allowed-permissions {:dapp dapp-name allowed-permissions {:dapp dapp-name
:permissions (vec allowed-permissions-set)}] :permissions (vec allowed-permissions-set)}]
{:db (assoc-in db [:dapps/permissions dapp-name] allowed-permissions) {:db (assoc-in db [:dapps/permissions dapp-name] allowed-permissions)
::json-rpc/call [{:method "permissions_addDappPermissions" ::json-rpc/call [{:method "permissions_addDappPermissions"
:params [allowed-permissions] :params [allowed-permissions]
:on-success #()}]})) :on-success #()}]}))
(fx/defn revoke-permissions (rf/defn revoke-permissions
{:events [:browser/revoke-dapp-permissions]} {:events [:browser/revoke-dapp-permissions]}
[{:keys [db] :as cofx} dapp] [{:keys [db] :as cofx} dapp]
(fx/merge cofx (rf/merge cofx
{:db (update-in db [:dapps/permissions] dissoc dapp) {:db (update-in db [:dapps/permissions] dissoc dapp)
::json-rpc/call [{:method "permissions_deleteDappPermissions" ::json-rpc/call [{:method "permissions_deleteDappPermissions"
:params [dapp] :params [dapp]
:on-success #()}]})) :on-success #()}]}))
(fx/defn revoke-dapp-permissions (rf/defn revoke-dapp-permissions
{:events [:dapps/revoke-access]} {:events [:dapps/revoke-access]}
[cofx dapp] [cofx dapp]
(fx/merge cofx (rf/merge cofx
(revoke-permissions dapp) (revoke-permissions dapp)
(navigation/navigate-back))) (navigation/navigate-back)))
(fx/defn clear-dapps-permissions (rf/defn clear-dapps-permissions
[{:keys [db]}] [{:keys [db]}]
(let [dapp-permissions (keys (:dapps/permissions db))] (let [dapp-permissions (keys (:dapps/permissions db))]
{:db (dissoc db :dapps/permissions) {:db (dissoc db :dapps/permissions)
@@ -95,7 +97,7 @@
:params [dapp] :params [dapp]
:on-success #()})})) :on-success #()})}))
(fx/defn process-next-permission (rf/defn process-next-permission
"Process next permission by removing it from pending permissions and prompting user "Process next permission by removing it from pending permissions and prompting user
if there is no pending permissions left, save all granted permissions if there is no pending permissions left, save all granted permissions
and return the result to the bridge" and return the result to the bridge"
@@ -105,46 +107,47 @@
{:db db} {:db db}
(let [pending-permissions (get-in db [:browser/options :pending-permissions]) (let [pending-permissions (get-in db [:browser/options :pending-permissions])
next-permission (last pending-permissions) next-permission (last pending-permissions)
new-cofx (update-in cofx [:db :browser/options :pending-permissions] butlast)] new-cofx (update-in cofx [:db :browser/options :pending-permissions] butlast)]
(when-let [{:keys [yield-control? permission message-id allowed? params]} next-permission] (when-let [{:keys [yield-control? permission message-id allowed? params]} next-permission]
(if (and yield-control? allowed?) (if (and yield-control? allowed?)
(permission-yield-control new-cofx dapp-name permission message-id params) (permission-yield-control new-cofx dapp-name permission message-id params)
(permission-show-permission new-cofx dapp-name permission message-id yield-control?))))))) (permission-show-permission new-cofx dapp-name permission message-id yield-control?)))))))
(fx/defn send-response-and-process-next-permission (rf/defn send-response-and-process-next-permission
[{:keys [db] :as cofx} dapp-name requested-permission message-id] [{:keys [db] :as cofx} dapp-name requested-permission message-id]
(fx/merge cofx (rf/merge cofx
(send-response-to-bridge requested-permission (send-response-to-bridge requested-permission
message-id message-id
true true
(get-permission-data cofx requested-permission)) (get-permission-data cofx requested-permission))
(process-next-permission dapp-name))) (process-next-permission dapp-name)))
(fx/defn allow-permission (rf/defn allow-permission
"Add permission to set of allowed permission and process next permission" "Add permission to set of allowed permission and process next permission"
{:events [:browser.permissions.ui/dapp-permission-allowed]} {:events [:browser.permissions.ui/dapp-permission-allowed]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(let [{:keys [requested-permission message-id dapp-name yield-control? params]} (let [{:keys [requested-permission message-id dapp-name yield-control? params]}
(get-in db [:browser/options :show-permission])] (get-in db [:browser/options :show-permission])]
(fx/merge (assoc-in cofx [:db :browser/options :show-permission] nil) (rf/merge (assoc-in cofx [:db :browser/options :show-permission] nil)
(update-dapp-permissions dapp-name requested-permission true) (update-dapp-permissions dapp-name requested-permission true)
(if yield-control? (if yield-control?
(permission-yield-control dapp-name requested-permission message-id params) (permission-yield-control dapp-name requested-permission message-id params)
(send-response-and-process-next-permission dapp-name requested-permission message-id))))) (send-response-and-process-next-permission dapp-name requested-permission message-id)))))
(fx/defn deny-permission (rf/defn deny-permission
"Add permission to set of allowed permission and process next permission" "Add permission to set of allowed permission and process next permission"
{:events [:browser.permissions.ui/dapp-permission-denied]} {:events [:browser.permissions.ui/dapp-permission-denied]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(let [{:keys [requested-permission message-id dapp-name]} (get-in db [:browser/options :show-permission])] (let [{:keys [requested-permission message-id dapp-name]} (get-in db
(fx/merge (assoc-in cofx [:db :browser/options :show-permission] nil) [:browser/options :show-permission])]
(rf/merge (assoc-in cofx [:db :browser/options :show-permission] nil)
(send-response-to-bridge requested-permission (send-response-to-bridge requested-permission
message-id message-id
false false
(get-permission-data cofx requested-permission)) (get-permission-data cofx requested-permission))
(process-next-permission dapp-name)))) (process-next-permission dapp-name))))
(fx/defn process-permission (rf/defn process-permission
"Process the permission requested by a dapp "Process the permission requested by a dapp
If supported permission is already granted, return the result immediatly to the bridge If supported permission is already granted, return the result immediatly to the bridge
Otherwise process the first permission which will prompt user" Otherwise process the first permission which will prompt user"
@@ -156,15 +159,18 @@
(not supported-permission) (not supported-permission)
(send-response-to-bridge cofx permission message-id false nil) (send-response-to-bridge cofx permission message-id false nil)
(and (or permission-allowed? (:allowed? supported-permission)) (not (:yield-control? supported-permission))) (and (or permission-allowed? (:allowed? supported-permission))
(not (:yield-control? supported-permission)))
(send-response-to-bridge cofx permission message-id true (get-permission-data cofx permission)) (send-response-to-bridge cofx permission message-id true (get-permission-data cofx permission))
:else :else
(process-next-permission (update-in cofx [:db :browser/options :pending-permissions] (process-next-permission (update-in cofx
conj {:permission permission [:db :browser/options :pending-permissions]
:allowed? (or permission-allowed? conj
(:allowed? supported-permission)) {:permission permission
:yield-control? (:yield-control? supported-permission) :allowed? (or permission-allowed?
:params params (:allowed? supported-permission))
:message-id message-id}) :yield-control? (:yield-control? supported-permission)
:params params
:message-id message-id})
dapp-name)))) dapp-name))))
+164 -150
View File
@@ -1,31 +1,33 @@
(ns status-im.chat.models (ns status-im.chat.models
(:require [re-frame.core :as re-frame] (:require [clojure.set :as set]
[taoensso.timbre :as log] [quo.design-system.colors :as colors]
[status-im.multiaccounts.model :as multiaccounts.model] [re-frame.core :as re-frame]
[status-im.add-new.db :as new-public-chat.db]
[status-im.chat.models.loading :as loading]
[status-im.chat.models.message-list :as message-list] [status-im.chat.models.message-list :as message-list]
[status-im2.contexts.chat.messages.message.delete-message.events :as delete-message] [status-im.constants :as constants]
[status-im2.contexts.chat.messages.message.delete-message-for-me.events :as delete-for-me]
[status-im.data-store.chats :as chats-store] [status-im.data-store.chats :as chats-store]
[status-im.mailserver.core :as mailserver]
[status-im.data-store.contacts :as contacts-store] [status-im.data-store.contacts :as contacts-store]
[status-im.ethereum.json-rpc :as json-rpc] [status-im.ethereum.json-rpc :as json-rpc]
[status-im.i18n.i18n :as i18n] [status-im.i18n.i18n :as i18n]
[quo.design-system.colors :as colors] [status-im.mailserver.core :as mailserver]
[status-im.constants :as constants] [status-im.multiaccounts.model :as multiaccounts.model]
[status-im.utils.clocks :as utils.clocks]
[status-im.utils.fx :as fx]
[status-im.utils.utils :as utils]
[status-im.utils.types :as types]
[status-im.add-new.db :as new-public-chat.db]
[status-im.chat.models.loading :as loading]
[status-im.ui.screens.chat.state :as chat.state] [status-im.ui.screens.chat.state :as chat.state]
[clojure.set :as set] [status-im.utils.clocks :as utils.clocks]
[status-im2.navigation.events :as navigation])) [status-im.utils.types :as types]
[status-im.utils.utils :as utils]
[status-im2.contexts.chat.messages.message.delete-message-for-me.events :as delete-for-me]
[status-im2.contexts.chat.messages.message.delete-message.events :as delete-message]
[status-im2.navigation.events :as navigation]
[taoensso.timbre :as log]
[utils.re-frame :as rf]))
(defn chats [] (defn chats
[]
(:chats (types/json->clj (js/require "./chats.js")))) (:chats (types/json->clj (js/require "./chats.js"))))
(defn- get-chat [cofx chat-id] (defn- get-chat
[cofx chat-id]
(get-in cofx [:db :chats chat-id])) (get-in cofx [:db :chats chat-id]))
(defn multi-user-chat? (defn multi-user-chat?
@@ -49,7 +51,8 @@
([cofx chat-id] ([cofx chat-id]
(community-chat? (get-chat cofx chat-id)))) (community-chat? (get-chat cofx chat-id))))
(defn active-chat? [cofx chat-id] (defn active-chat?
[cofx chat-id]
(let [chat (get-chat cofx chat-id)] (let [chat (get-chat cofx chat-id)]
(:active chat))) (:active chat)))
@@ -85,52 +88,56 @@
(defn- create-new-chat (defn- create-new-chat
[chat-id {:keys [db now]}] [chat-id {:keys [db now]}]
(let [name (get-in db [:contacts/contacts chat-id :name])] (let [name (get-in db [:contacts/contacts chat-id :name])]
{:chat-id chat-id {:chat-id chat-id
:name (or name "") :name (or name "")
:color (rand-nth colors/chat-colors) :color (rand-nth colors/chat-colors)
:chat-type constants/one-to-one-chat-type :chat-type constants/one-to-one-chat-type
:group-chat false :group-chat false
:timestamp now :timestamp now
:contacts #{chat-id} :contacts #{chat-id}
:last-clock-value 0})) :last-clock-value 0}))
(defn map-chats [{:keys [db] :as cofx}] (defn map-chats
[{:keys [db] :as cofx}]
(fn [val] (fn [val]
(assoc (assoc
(merge (merge
(or (get (:chats db) (:chat-id val)) (or (get (:chats db) (:chat-id val))
(create-new-chat (:chat-id val) cofx)) (create-new-chat (:chat-id val) cofx))
val) val)
:invitation-admin (:invitation-admin val)))) :invitation-admin
(:invitation-admin val))))
(defn filter-chats [db] (defn filter-chats
[db]
(fn [val] (fn [val]
(and (not (get-in db [:chats (:chat-id val)])) (:public? val)))) (and (not (get-in db [:chats (:chat-id val)])) (:public? val))))
(fx/defn leave-removed-chat (rf/defn leave-removed-chat
[{{:keys [view-id current-chat-id chats]} :db [{{:keys [view-id current-chat-id chats]} :db
:as cofx}] :as cofx}]
(when (and (= view-id :chat) (when (and (= view-id :chat)
(not (contains? chats current-chat-id))) (not (contains? chats current-chat-id)))
(navigation/navigate-back cofx))) (navigation/navigate-back cofx)))
(fx/defn ensure-chats (rf/defn ensure-chats
"Add chats to db and update" "Add chats to db and update"
[{:keys [db] :as cofx} chats] [{:keys [db] :as cofx} chats]
(let [{:keys [all-chats chats-home-list removed-chats]} (let [{:keys [all-chats chats-home-list removed-chats]}
(reduce (fn [acc {:keys [chat-id profile-public-key timeline? community-id active muted] :as chat}] (reduce
(if (not (or active muted)) (fn [acc {:keys [chat-id profile-public-key timeline? community-id active muted] :as chat}]
(update acc :removed-chats conj chat-id) (if (not (or active muted))
(cond-> acc (update acc :removed-chats conj chat-id)
(and (not profile-public-key) (not timeline?) (not community-id) active) (cond-> acc
(update :chats-home-list conj chat-id) (and (not profile-public-key) (not timeline?) (not community-id) active)
:always (update :chats-home-list conj chat-id)
(assoc-in [:all-chats chat-id] chat)))) :always
{:all-chats {} (assoc-in [:all-chats chat-id] chat))))
:chats-home-list #{} {:all-chats {}
:removed-chats #{}} :chats-home-list #{}
(map (map-chats cofx) chats))] :removed-chats #{}}
(fx/merge (map (map-chats cofx) chats))]
(rf/merge
cofx cofx
(merge {:db (-> db (merge {:db (-> db
(update :chats merge all-chats) (update :chats merge all-chats)
@@ -143,62 +150,66 @@
(get-in db [:multiaccount :remote-push-notifications-enabled?])]})) (get-in db [:multiaccount :remote-push-notifications-enabled?])]}))
leave-removed-chat))) leave-removed-chat)))
(fx/defn clear-history (rf/defn clear-history
"Clears history of the particular chat" "Clears history of the particular chat"
[{:keys [db] :as cofx} chat-id remove-chat?] [{:keys [db] :as cofx} chat-id remove-chat?]
(let [{:keys [last-message public? (let [{:keys [last-message public?
deleted-at-clock-value]} (get-in db [:chats chat-id]) deleted-at-clock-value]}
(get-in db [:chats chat-id])
last-message-clock-value (if (and public? remove-chat?) last-message-clock-value (if (and public? remove-chat?)
0 0
(or (:clock-value last-message) (or (:clock-value last-message)
deleted-at-clock-value deleted-at-clock-value
(utils.clocks/send 0)))] (utils.clocks/send 0)))]
{:db (-> db {:db (-> db
(assoc-in [:messages chat-id] {}) (assoc-in [:messages chat-id] {})
(update-in [:message-lists] dissoc chat-id) (update-in [:message-lists] dissoc chat-id)
(update :chats (fn [chats] (update :chats
(if (contains? chats chat-id) (fn [chats]
(update chats chat-id merge (if (contains? chats chat-id)
{:last-message nil (update chats
:unviewed-messages-count 0 chat-id
:unviewed-mentions-count 0 merge
:deleted-at-clock-value last-message-clock-value}) {:last-message nil
chats))))})) :unviewed-messages-count 0
:unviewed-mentions-count 0
:deleted-at-clock-value last-message-clock-value})
chats))))}))
(fx/defn clear-history-handler (rf/defn clear-history-handler
"Clears history of the particular chat" "Clears history of the particular chat"
{:events [:chat.ui/clear-history]} {:events [:chat.ui/clear-history]}
[{:keys [db] :as cofx} chat-id remove-chat?] [{:keys [db] :as cofx} chat-id remove-chat?]
(fx/merge cofx (rf/merge cofx
{:db db {:db db
::json-rpc/call [{:method "wakuext_clearHistory" ::json-rpc/call [{:method "wakuext_clearHistory"
:params [{:id chat-id}] :params [{:id chat-id}]
:on-success #(re-frame/dispatch [::history-cleared chat-id %]) :on-success #(re-frame/dispatch [::history-cleared chat-id %])
:on-error #(log/error "failed to clear history " chat-id %)}]} :on-error #(log/error "failed to clear history " chat-id %)}]}
(clear-history chat-id remove-chat?))) (clear-history chat-id remove-chat?)))
(fx/defn chat-deactivated (rf/defn chat-deactivated
{:events [::chat-deactivated]} {:events [::chat-deactivated]}
[_ chat-id] [_ chat-id]
(log/debug "chat deactivated" chat-id)) (log/debug "chat deactivated" chat-id))
(fx/defn deactivate-chat (rf/defn deactivate-chat
"Deactivate chat in db, no side effects" "Deactivate chat in db, no side effects"
[{:keys [db now] :as cofx} chat-id] [{:keys [db now] :as cofx} chat-id]
(fx/merge (rf/merge
cofx cofx
{:db (-> (if (get-in db [:chats chat-id :muted]) {:db (-> (if (get-in db [:chats chat-id :muted])
(assoc-in db [:chats chat-id :active] false) (assoc-in db [:chats chat-id :active] false)
(update db :chats dissoc chat-id)) (update db :chats dissoc chat-id))
(update :chats-home-list disj chat-id) (update :chats-home-list disj chat-id)
(assoc :current-chat-id nil)) (assoc :current-chat-id nil))
::json-rpc/call [{:method "wakuext_deactivateChat" ::json-rpc/call [{:method "wakuext_deactivateChat"
:params [{:id chat-id}] :params [{:id chat-id}]
:on-success #(re-frame/dispatch [::chat-deactivated chat-id]) :on-success #(re-frame/dispatch [::chat-deactivated chat-id])
:on-error #(log/error "failed to create public chat" chat-id %)}]} :on-error #(log/error "failed to create public chat" chat-id %)}]}
(clear-history chat-id true))) (clear-history chat-id true)))
(fx/defn offload-messages (rf/defn offload-messages
{:events [:offload-messages]} {:events [:offload-messages]}
[{:keys [db]} chat-id] [{:keys [db]} chat-id]
(merge {:db (-> db (merge {:db (-> db
@@ -208,52 +219,52 @@
(when (and (= chat-id constants/timeline-chat-id) (= (:view-id db) :status)) (when (and (= chat-id constants/timeline-chat-id) (= (:view-id db) :status))
{:dispatch [:init-timeline-chat]}))) {:dispatch [:init-timeline-chat]})))
(fx/defn close-chat (rf/defn close-chat
{:events [:close-chat]} {:events [:close-chat]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(when-let [chat-id (:current-chat-id db)] (when-let [chat-id (:current-chat-id db)]
(chat.state/reset-visible-item) (chat.state/reset-visible-item)
(fx/merge cofx (rf/merge cofx
{:db (dissoc db :current-chat-id)} {:db (dissoc db :current-chat-id)}
(delete-for-me/sync-all) (delete-for-me/sync-all)
(delete-message/send-all) (delete-message/send-all)
(offload-messages chat-id)))) (offload-messages chat-id))))
(fx/defn force-close-chat (rf/defn force-close-chat
[{:keys [db] :as cofx} chat-id] [{:keys [db] :as cofx} chat-id]
(do (do
(chat.state/reset-visible-item) (chat.state/reset-visible-item)
(fx/merge cofx (rf/merge cofx
{:db (dissoc db :current-chat-id)} {:db (dissoc db :current-chat-id)}
(offload-messages chat-id)))) (offload-messages chat-id))))
(fx/defn remove-chat (rf/defn remove-chat
"Removes chat completely from app, producing all necessary effects for that" "Removes chat completely from app, producing all necessary effects for that"
{:events [:chat.ui/remove-chat]} {:events [:chat.ui/remove-chat]}
[{:keys [db now] :as cofx} chat-id] [{:keys [db now] :as cofx} chat-id]
(fx/merge cofx (rf/merge cofx
{:clear-message-notifications {:clear-message-notifications
[[chat-id] (get-in db [:multiaccount :remote-push-notifications-enabled?])]} [[chat-id] (get-in db [:multiaccount :remote-push-notifications-enabled?])]}
(deactivate-chat chat-id) (deactivate-chat chat-id)
(offload-messages chat-id))) (offload-messages chat-id)))
(fx/defn show-more-chats (rf/defn show-more-chats
{:events [:chat.ui/show-more-chats]} {:events [:chat.ui/show-more-chats]}
[{:keys [db]}] [{:keys [db]}]
(when (< (:home-items-show-number db) (count (:chats db))) (when (< (:home-items-show-number db) (count (:chats db)))
{:db (update db :home-items-show-number + 40)})) {:db (update db :home-items-show-number + 40)}))
(fx/defn preload-chat-data (rf/defn preload-chat-data
"Takes chat-id and coeffects map, returns effects necessary when navigating to chat" "Takes chat-id and coeffects map, returns effects necessary when navigating to chat"
{:events [:chat.ui/preload-chat-data]} {:events [:chat.ui/preload-chat-data]}
[cofx chat-id] [cofx chat-id]
(loading/load-messages cofx chat-id)) (loading/load-messages cofx chat-id))
(fx/defn navigate-to-chat (rf/defn navigate-to-chat
"Takes coeffects map and chat-id, returns effects necessary for navigation and preloading data" "Takes coeffects map and chat-id, returns effects necessary for navigation and preloading data"
{:events [:chat.ui/navigate-to-chat]} {:events [:chat.ui/navigate-to-chat]}
[{db :db :as cofx} chat-id] [{db :db :as cofx} chat-id]
(fx/merge cofx (rf/merge cofx
{:dispatch [:navigate-to :chat]} {:dispatch [:navigate-to :chat]}
(navigation/change-tab :chat) (navigation/change-tab :chat)
(when-not (= (:view-id db) :community) (when-not (= (:view-id db) :community)
@@ -266,11 +277,11 @@
#(when (group-chat? cofx chat-id) #(when (group-chat? cofx chat-id)
(loading/load-chat % chat-id)))) (loading/load-chat % chat-id))))
(fx/defn navigate-to-chat-nav2 (rf/defn navigate-to-chat-nav2
"Takes coeffects map and chat-id, returns effects necessary for navigation and preloading data" "Takes coeffects map and chat-id, returns effects necessary for navigation and preloading data"
{:events [:chat.ui/navigate-to-chat-nav2]} {:events [:chat.ui/navigate-to-chat-nav2]}
[{db :db :as cofx} chat-id from-shell?] [{db :db :as cofx} chat-id from-shell?]
(fx/merge cofx (rf/merge cofx
{:dispatch [:navigate-to-nav2 :chat chat-id from-shell?]} {:dispatch [:navigate-to-nav2 :chat chat-id from-shell?]}
(when-not (= (:view-id db) :community) (when-not (= (:view-id db) :community)
(navigation/pop-to-root-tab :shell-stack)) (navigation/pop-to-root-tab :shell-stack))
@@ -282,65 +293,68 @@
#(when (group-chat? cofx chat-id) #(when (group-chat? cofx chat-id)
(loading/load-chat % chat-id)))) (loading/load-chat % chat-id))))
(fx/defn handle-clear-history-response (rf/defn handle-clear-history-response
{:events [::history-cleared]} {:events [::history-cleared]}
[{:keys [db]} chat-id response] [{:keys [db]} chat-id response]
(let [chat (chats-store/<-rpc (first (:chats response)))] (let [chat (chats-store/<-rpc (first (:chats response)))]
{:db (assoc-in db [:chats chat-id] chat)})) {:db (assoc-in db [:chats chat-id] chat)}))
(fx/defn handle-one-to-one-chat-created (rf/defn handle-one-to-one-chat-created
{:events [::one-to-one-chat-created]} {:events [::one-to-one-chat-created]}
[{:keys [db]} chat-id response] [{:keys [db]} chat-id response]
(let [chat (chats-store/<-rpc (first (:chats response))) (let [chat (chats-store/<-rpc (first (:chats response)))
contact-rpc (first (:contacts response)) contact-rpc (first (:contacts response))
contact (when contact-rpc (contacts-store/<-rpc contact-rpc))] contact (when contact-rpc (contacts-store/<-rpc contact-rpc))]
{:db (cond-> db {:db (cond-> db
contact contact
(assoc-in [:contacts/contacts chat-id] contact) (assoc-in [:contacts/contacts chat-id] contact)
:always :always
(assoc-in [:chats chat-id] chat) (assoc-in [:chats chat-id] chat)
:always :always
(update :chats-home-list conj chat-id)) (update :chats-home-list conj chat-id))
:dispatch [:chat.ui/navigate-to-chat chat-id]})) :dispatch [:chat.ui/navigate-to-chat chat-id]}))
(fx/defn navigate-to-user-pinned-messages (rf/defn navigate-to-user-pinned-messages
"Takes coeffects map and chat-id, returns effects necessary for navigation and preloading data" "Takes coeffects map and chat-id, returns effects necessary for navigation and preloading data"
{:events [:chat.ui/navigate-to-pinned-messages]} {:events [:chat.ui/navigate-to-pinned-messages]}
[cofx chat-id] [cofx chat-id]
(navigation/navigate-to cofx :chat-pinned-messages {:chat-id chat-id})) (navigation/navigate-to cofx :chat-pinned-messages {:chat-id chat-id}))
(fx/defn start-chat (rf/defn start-chat
"Start a chat, making sure it exists" "Start a chat, making sure it exists"
{:events [:chat.ui/start-chat]} {:events [:chat.ui/start-chat]}
[{:keys [db] :as cofx} chat-id ens-name] [{:keys [db] :as cofx} chat-id ens-name]
;; don't allow to open chat with yourself ;; don't allow to open chat with yourself
(when (not= (multiaccounts.model/current-public-key cofx) chat-id) (when (not= (multiaccounts.model/current-public-key cofx) chat-id)
{::json-rpc/call [{:method "wakuext_createOneToOneChat" {::json-rpc/call [{:method "wakuext_createOneToOneChat"
:params [{:id chat-id :ensName ens-name}] :params [{:id chat-id :ensName ens-name}]
:on-success #(re-frame/dispatch [::one-to-one-chat-created chat-id %]) :on-success #(re-frame/dispatch [::one-to-one-chat-created chat-id %])
:on-error #(log/error "failed to create one-to-on chat" chat-id %)}]})) :on-error #(log/error "failed to create one-to-on chat" chat-id %)}]}))
(defn profile-chat-topic [public-key] (defn profile-chat-topic
[public-key]
(str "@" public-key)) (str "@" public-key))
(defn my-profile-chat-topic [db] (defn my-profile-chat-topic
[db]
(profile-chat-topic (get-in db [:multiaccount :public-key]))) (profile-chat-topic (get-in db [:multiaccount :public-key])))
(fx/defn handle-public-chat-created (rf/defn handle-public-chat-created
{:events [::public-chat-created]} {:events [::public-chat-created]}
[{:keys [db]} chat-id response] [{:keys [db]} chat-id response]
{:db (-> db {:db (-> db
(assoc-in [:chats chat-id] (chats-store/<-rpc (first (:chats response)))) (assoc-in [:chats chat-id] (chats-store/<-rpc (first (:chats response))))
(update :chats-home-list conj chat-id)) (update :chats-home-list conj chat-id))
:dispatch [:chat.ui/navigate-to-chat chat-id]}) :dispatch [:chat.ui/navigate-to-chat chat-id]})
(fx/defn create-public-chat-go [_ chat-id] (rf/defn create-public-chat-go
{::json-rpc/call [{:method "wakuext_createPublicChat" [_ chat-id]
:params [{:id chat-id}] {::json-rpc/call [{:method "wakuext_createPublicChat"
:params [{:id chat-id}]
:on-success #(re-frame/dispatch [::public-chat-created chat-id %]) :on-success #(re-frame/dispatch [::public-chat-created chat-id %])
:on-error #(log/error "failed to create public chat" chat-id %)}]}) :on-error #(log/error "failed to create public chat" chat-id %)}]})
(fx/defn start-public-chat (rf/defn start-public-chat
"Starts a new public chat" "Starts a new public chat"
{:events [:chat.ui/start-public-chat]} {:events [:chat.ui/start-public-chat]}
[cofx topic] [cofx topic]
@@ -353,10 +367,10 @@
{:utils/show-popup {:title (i18n/label :t/cant-open-public-chat) {:utils/show-popup {:title (i18n/label :t/cant-open-public-chat)
:content (i18n/label :t/invalid-public-chat-topic)}})) :content (i18n/label :t/invalid-public-chat-topic)}}))
(fx/defn profile-chat-created (rf/defn profile-chat-created
{:events [::profile-chat-created]} {:events [::profile-chat-created]}
[{:keys [db] :as cofx} chat-id response navigate-to?] [{:keys [db] :as cofx} chat-id response navigate-to?]
(fx/merge (rf/merge
cofx cofx
{:db db} {:db db}
#(when response #(when response
@@ -366,19 +380,19 @@
{:dispatch-n [[:chat.ui/preload-chat-data chat-id] {:dispatch-n [[:chat.ui/preload-chat-data chat-id]
[:open-modal :profile]]}))) [:open-modal :profile]]})))
(fx/defn start-profile-chat (rf/defn start-profile-chat
"Starts a new profile chat" "Starts a new profile chat"
{:events [:start-profile-chat]} {:events [:start-profile-chat]}
[cofx profile-public-key navigate-to?] [cofx profile-public-key navigate-to?]
(let [chat-id (profile-chat-topic profile-public-key)] (let [chat-id (profile-chat-topic profile-public-key)]
(if (active-chat? cofx chat-id) (if (active-chat? cofx chat-id)
{:dispatch [::profile-chat-created chat-id nil navigate-to?]} {:dispatch [::profile-chat-created chat-id nil navigate-to?]}
{::json-rpc/call [{:method "wakuext_createProfileChat" {::json-rpc/call [{:method "wakuext_createProfileChat"
:params [{:id profile-public-key}] :params [{:id profile-public-key}]
:on-success #(re-frame/dispatch [::profile-chat-created chat-id % navigate-to?]) :on-success #(re-frame/dispatch [::profile-chat-created chat-id % navigate-to?])
:on-error #(log/error "failed to create profile chat" chat-id %)}]}))) :on-error #(log/error "failed to create profile chat" chat-id %)}]})))
(fx/defn disable-chat-cooldown (rf/defn disable-chat-cooldown
"Turns off chat cooldown (protection against message spamming)" "Turns off chat cooldown (protection against message spamming)"
{:events [:chat/disable-cooldown]} {:events [:chat/disable-cooldown]}
[{:keys [db]}] [{:keys [db]}]
@@ -392,40 +406,40 @@
(i18n/label :cooldown/warning-message) (i18n/label :cooldown/warning-message)
#()))) #())))
(fx/defn mute-chat-failed (rf/defn mute-chat-failed
{:events [::mute-chat-failed]} {:events [::mute-chat-failed]}
[{:keys [db] :as cofx} chat-id muted? error] [{:keys [db] :as cofx} chat-id muted? error]
(log/error "mute chat failed" chat-id error) (log/error "mute chat failed" chat-id error)
{:db (assoc-in db [:chats chat-id :muted] (not muted?))}) {:db (assoc-in db [:chats chat-id :muted] (not muted?))})
(fx/defn mute-chat-toggled-successfully (rf/defn mute-chat-toggled-successfully
{:events [::mute-chat-toggled-successfully]} {:events [::mute-chat-toggled-successfully]}
[_ chat-id] [_ chat-id]
(log/debug "muted chat successfully" chat-id)) (log/debug "muted chat successfully" chat-id))
(fx/defn mute-chat (rf/defn mute-chat
{:events [::mute-chat-toggled]} {:events [::mute-chat-toggled]}
[{:keys [db] :as cofx} chat-id muted?] [{:keys [db] :as cofx} chat-id muted?]
(let [method (if muted? "wakuext_muteChat" "wakuext_unmuteChat")] (let [method (if muted? "wakuext_muteChat" "wakuext_unmuteChat")]
{:db (assoc-in db [:chats chat-id :muted] muted?) {:db (assoc-in db [:chats chat-id :muted] muted?)
::json-rpc/call [{:method method ::json-rpc/call [{:method method
:params [chat-id] :params [chat-id]
:on-error #(re-frame/dispatch [::mute-chat-failed chat-id muted? %]) :on-error #(re-frame/dispatch [::mute-chat-failed chat-id muted? %])
:on-success #(re-frame/dispatch [::mute-chat-toggled-successfully chat-id])}]})) :on-success #(re-frame/dispatch [::mute-chat-toggled-successfully chat-id])}]}))
(fx/defn show-profile (rf/defn show-profile
{:events [:chat.ui/show-profile]} {:events [:chat.ui/show-profile]}
[{:keys [db] :as cofx} identity ens-name] [{:keys [db] :as cofx} identity ens-name]
(let [my-public-key (get-in db [:multiaccount :public-key])] (let [my-public-key (get-in db [:multiaccount :public-key])]
(when (not= my-public-key identity) (when (not= my-public-key identity)
(fx/merge (rf/merge
cofx cofx
{:db (-> db {:db (-> db
(assoc :contacts/identity identity) (assoc :contacts/identity identity)
(assoc :contacts/ens-name ens-name))} (assoc :contacts/ens-name ens-name))}
(start-profile-chat identity true))))) (start-profile-chat identity true)))))
(fx/defn clear-history-pressed (rf/defn clear-history-pressed
{:events [:chat.ui/clear-history-pressed]} {:events [:chat.ui/clear-history-pressed]}
[_ chat-id] [_ chat-id]
{:ui/show-confirmation {:ui/show-confirmation
@@ -436,19 +450,19 @@
(re-frame/dispatch [:bottom-sheet/hide]) (re-frame/dispatch [:bottom-sheet/hide])
(re-frame/dispatch [:chat.ui/clear-history chat-id false]))}}) (re-frame/dispatch [:chat.ui/clear-history chat-id false]))}})
(fx/defn gaps-failed (rf/defn gaps-failed
{:events [::gaps-failed]} {:events [::gaps-failed]}
[{:keys [db]} chat-id gap-ids error] [{:keys [db]} chat-id gap-ids error]
(log/error "failed to fetch gaps" chat-id gap-ids error) (log/error "failed to fetch gaps" chat-id gap-ids error)
{:db (dissoc db :mailserver/fetching-gaps-in-progress)}) {:db (dissoc db :mailserver/fetching-gaps-in-progress)})
(fx/defn sync-chat-from-sync-from-failed (rf/defn sync-chat-from-sync-from-failed
{:events [::sync-chat-from-sync-from-failed]} {:events [::sync-chat-from-sync-from-failed]}
[{:keys [db]} chat-id error] [{:keys [db]} chat-id error]
(log/error "failed to sync chat" chat-id error) (log/error "failed to sync chat" chat-id error)
{:db (dissoc db :mailserver/fetching-gaps-in-progress)}) {:db (dissoc db :mailserver/fetching-gaps-in-progress)})
(fx/defn sync-chat-from-sync-from-success (rf/defn sync-chat-from-sync-from-success
{:events [::sync-chat-from-sync-from-success]} {:events [::sync-chat-from-sync-from-success]}
[{:keys [db] :as cofx} chat-id synced-from] [{:keys [db] :as cofx} chat-id synced-from]
(log/debug "synced success" chat-id synced-from) (log/debug "synced success" chat-id synced-from)
@@ -457,44 +471,44 @@
(assoc-in [:chats chat-id :synced-from] synced-from) (assoc-in [:chats chat-id :synced-from] synced-from)
(dissoc :mailserver/fetching-gaps-in-progress))}) (dissoc :mailserver/fetching-gaps-in-progress))})
(fx/defn gaps-filled (rf/defn gaps-filled
{:events [::gaps-filled]} {:events [::gaps-filled]}
[{:keys [db] :as cofx} chat-id message-ids] [{:keys [db] :as cofx} chat-id message-ids]
(fx/merge (rf/merge
cofx cofx
{:db (-> db {:db (-> db
(update-in [:messages chat-id] (fn [messages] (apply dissoc messages message-ids))) (update-in [:messages chat-id] (fn [messages] (apply dissoc messages message-ids)))
(dissoc :mailserver/fetching-gaps-in-progress))} (dissoc :mailserver/fetching-gaps-in-progress))}
(message-list/rebuild-message-list chat-id))) (message-list/rebuild-message-list chat-id)))
(fx/defn fill-gaps (rf/defn fill-gaps
[cofx chat-id gap-ids] [cofx chat-id gap-ids]
{::json-rpc/call [{:method "wakuext_fillGaps" {::json-rpc/call [{:method "wakuext_fillGaps"
:params [chat-id gap-ids] :params [chat-id gap-ids]
:on-success #(re-frame/dispatch [::gaps-filled chat-id gap-ids %]) :on-success #(re-frame/dispatch [::gaps-filled chat-id gap-ids %])
:on-error #(re-frame/dispatch [::gaps-failed chat-id gap-ids %])}]}) :on-error #(re-frame/dispatch [::gaps-failed chat-id gap-ids %])}]})
(fx/defn sync-chat-from-sync-from (rf/defn sync-chat-from-sync-from
[cofx chat-id] [cofx chat-id]
(log/debug "syncing chat from sync from") (log/debug "syncing chat from sync from")
{::json-rpc/call [{:method "wakuext_syncChatFromSyncedFrom" {::json-rpc/call [{:method "wakuext_syncChatFromSyncedFrom"
:params [chat-id] :params [chat-id]
:on-success #(re-frame/dispatch [::sync-chat-from-sync-from-success chat-id %]) :on-success #(re-frame/dispatch [::sync-chat-from-sync-from-success chat-id %])
:on-error #(re-frame/dispatch [::sync-chat-from-sync-from-failed chat-id %])}]}) :on-error #(re-frame/dispatch [::sync-chat-from-sync-from-failed chat-id %])}]})
(fx/defn chat-ui-fill-gaps (rf/defn chat-ui-fill-gaps
{:events [:chat.ui/fill-gaps]} {:events [:chat.ui/fill-gaps]}
[{:keys [db] :as cofx} chat-id gap-ids] [{:keys [db] :as cofx} chat-id gap-ids]
(let [use-status-nodes? (mailserver/fetch-use-mailservers? {:db db})] (let [use-status-nodes? (mailserver/fetch-use-mailservers? {:db db})]
(log/info "filling gaps if use-status-nodes = true" chat-id gap-ids) (log/info "filling gaps if use-status-nodes = true" chat-id gap-ids)
(when use-status-nodes? (when use-status-nodes?
(fx/merge cofx (rf/merge cofx
{:db (assoc db :mailserver/fetching-gaps-in-progress gap-ids)} {:db (assoc db :mailserver/fetching-gaps-in-progress gap-ids)}
(if (= gap-ids #{:first-gap}) (if (= gap-ids #{:first-gap})
(sync-chat-from-sync-from chat-id) (sync-chat-from-sync-from chat-id)
(fill-gaps chat-id gap-ids)))))) (fill-gaps chat-id gap-ids))))))
(fx/defn chat-ui-remove-chat-pressed (rf/defn chat-ui-remove-chat-pressed
{:events [:chat.ui/remove-chat-pressed]} {:events [:chat.ui/remove-chat-pressed]}
[_ chat-id] [_ chat-id]
{:ui/show-confirmation {:ui/show-confirmation
@@ -505,7 +519,7 @@
(re-frame/dispatch [:bottom-sheet/hide]) (re-frame/dispatch [:bottom-sheet/hide])
(re-frame/dispatch [:chat.ui/remove-chat chat-id]))}}) (re-frame/dispatch [:chat.ui/remove-chat chat-id]))}})
(fx/defn decrease-unviewed-count (rf/defn decrease-unviewed-count
{:events [:chat/decrease-unviewed-count]} {:events [:chat/decrease-unviewed-count]}
[{:keys [db]} chat-id {:keys [count countWithMentions]}] [{:keys [db]} chat-id {:keys [count countWithMentions]}]
{:db (-> db {:db (-> db
+51 -42
View File
@@ -1,24 +1,25 @@
(ns status-im.chat.models.images (ns status-im.chat.models.images
(:require [re-frame.core :as re-frame] (:require ["@react-native-community/cameraroll" :as CameraRoll]
[status-im.utils.fx :as fx]
["@react-native-community/cameraroll" :as CameraRoll]
["react-native-blob-util" :default ReactNativeBlobUtil] ["react-native-blob-util" :default ReactNativeBlobUtil]
[status-im.utils.types :as types] [clojure.string :as string]
[status-im.utils.config :as config] [re-frame.core :as re-frame]
[status-im.chat.models :as chat]
[status-im.i18n.i18n :as i18n]
[status-im.ui.components.permissions :as permissions] [status-im.ui.components.permissions :as permissions]
[status-im.ui.components.react :as react] [status-im.ui.components.react :as react]
[status-im.utils.image-processing :as image-processing] [status-im.utils.config :as config]
[taoensso.timbre :as log]
[clojure.string :as string]
[status-im.i18n.i18n :as i18n]
[status-im.utils.utils :as utils]
[status-im.utils.platform :as platform]
[status-im.utils.fs :as fs] [status-im.utils.fs :as fs]
[status-im.chat.models :as chat])) [status-im.utils.image-processing :as image-processing]
[status-im.utils.platform :as platform]
[status-im.utils.types :as types]
[status-im.utils.utils :as utils]
[taoensso.timbre :as log]
[utils.re-frame :as rf]))
(def maximum-image-size-px 2000) (def maximum-image-size-px 2000)
(defn- resize-and-call [uri cb] (defn- resize-and-call
[uri cb]
(react/image-get-size (react/image-get-size
uri uri
(fn [width height] (fn [width height]
@@ -34,16 +35,19 @@
(cb path))) (cb path)))
#(log/error "could not resize image" %)))))) #(log/error "could not resize image" %))))))
(defn result->id [^js result] (defn result->id
[^js result]
(if platform/ios? (if platform/ios?
(.-localIdentifier result) (.-localIdentifier result)
(.-path result))) (.-path result)))
(def temp-image-url (str (fs/cache-dir) "/StatusIm_Image.jpeg")) (def temp-image-url (str (fs/cache-dir) "/StatusIm_Image.jpeg"))
(defn download-image-http [base64-uri on-success] (defn download-image-http
(-> (.config ReactNativeBlobUtil (clj->js {:trusty platform/ios? [base64-uri on-success]
:path temp-image-url})) (-> (.config ReactNativeBlobUtil
(clj->js {:trusty platform/ios?
:path temp-image-url}))
(.fetch "GET" base64-uri) (.fetch "GET" base64-uri)
(.then #(on-success (.path %))) (.then #(on-success (.path %)))
(.catch #(log/error "could not save image")))) (.catch #(log/error "could not save image"))))
@@ -70,15 +74,16 @@
::chat-open-image-picker-camera ::chat-open-image-picker-camera
(fn [current-chat-id] (fn [current-chat-id]
(react/show-image-picker-camera (react/show-image-picker-camera
#(re-frame/dispatch [:chat.ui/image-captured current-chat-id (.-path %)]) {}))) #(re-frame/dispatch [:chat.ui/image-captured current-chat-id (.-path %)])
{})))
(re-frame/reg-fx (re-frame/reg-fx
::chat-open-image-picker ::chat-open-image-picker
(fn [chat-id] (fn [chat-id]
(react/show-image-picker (react/show-image-picker
(fn [^js images] (fn [^js images]
;; NOTE(Ferossgp): Because we can't highlight the already selected images inside ;; NOTE(Ferossgp): Because we can't highlight the already selected images inside
;; gallery, we just clean previous state and set all newly picked images ;; gallery, we just clean previous state and set all newly picked images
(when (and platform/ios? (pos? (count images))) (when (and platform/ios? (pos? (count images)))
(re-frame/dispatch [:chat.ui/clear-sending-images chat-id])) (re-frame/dispatch [:chat.ui/clear-sending-images chat-id]))
(doseq [^js result (if platform/ios? (doseq [^js result (if platform/ios?
@@ -86,8 +91,8 @@
[images])] [images])]
(resize-and-call (.-path result) (resize-and-call (.-path result)
#(re-frame/dispatch [:chat.ui/image-selected chat-id (result->id result) %])))) #(re-frame/dispatch [:chat.ui/image-selected chat-id (result->id result) %]))))
;; NOTE(Ferossgp): On android you cannot set max limit on images, when a user ;; NOTE(Ferossgp): On android you cannot set max limit on images, when a user
;; selects too many images the app crashes. ;; selects too many images the app crashes.
{:media-type "photo" {:media-type "photo"
:multiple platform/ios?}))) :multiple platform/ios?})))
@@ -105,13 +110,17 @@
{:permissions [:read-external-storage] {:permissions [:read-external-storage]
:on-allowed (fn [] :on-allowed (fn []
(-> (if end-cursor (-> (if end-cursor
(.getPhotos CameraRoll #js {:first num :after end-cursor :assetType "Photos" :groupTypes "All"}) (.getPhotos
(.getPhotos CameraRoll #js {:first num :assetType "Photos" :groupTypes "All"})) CameraRoll
#js {:first num :after end-cursor :assetType "Photos" :groupTypes "All"})
(.getPhotos CameraRoll
#js {:first num :assetType "Photos" :groupTypes "All"}))
(.then #(let [response (types/js->clj %)] (.then #(let [response (types/js->clj %)]
(re-frame/dispatch [:on-camera-roll-get-photos (:edges response) (:page_info response) end-cursor]))) (re-frame/dispatch [:on-camera-roll-get-photos (:edges response)
(:page_info response) end-cursor])))
(.catch #(log/warn "could not get camera roll photos"))))}))) (.catch #(log/warn "could not get camera roll photos"))))})))
(fx/defn image-captured (rf/defn image-captured
{:events [:chat.ui/image-captured]} {:events [:chat.ui/image-captured]}
[{:keys [db]} chat-id uri] [{:keys [db]} chat-id uri]
(let [current-chat-id (or chat-id (:current-chat-id db)) (let [current-chat-id (or chat-id (:current-chat-id db))
@@ -120,24 +129,24 @@
(not (get images uri))) (not (get images uri)))
{::image-selected [uri current-chat-id]}))) {::image-selected [uri current-chat-id]})))
(fx/defn on-end-reached (rf/defn on-end-reached
{:events [:camera-roll/on-end-reached]} {:events [:camera-roll/on-end-reached]}
[_ end-cursor loading? has-next-page?] [_ end-cursor loading? has-next-page?]
(when (and (not loading?) has-next-page?) (when (and (not loading?) has-next-page?)
(re-frame/dispatch [:chat.ui/camera-roll-loading-more true]) (re-frame/dispatch [:chat.ui/camera-roll-loading-more true])
(re-frame/dispatch [:chat.ui/camera-roll-get-photos 20 end-cursor]))) (re-frame/dispatch [:chat.ui/camera-roll-get-photos 20 end-cursor])))
(fx/defn camera-roll-get-photos (rf/defn camera-roll-get-photos
{:events [:chat.ui/camera-roll-get-photos]} {:events [:chat.ui/camera-roll-get-photos]}
[_ num end-cursor] [_ num end-cursor]
{::camera-roll-get-photos [num end-cursor]}) {::camera-roll-get-photos [num end-cursor]})
(fx/defn camera-roll-loading-more (rf/defn camera-roll-loading-more
{:events [:chat.ui/camera-roll-loading-more]} {:events [:chat.ui/camera-roll-loading-more]}
[{:keys [db]} is-loading] [{:keys [db]} is-loading]
{:db (assoc db :camera-roll/loading-more is-loading)}) {:db (assoc db :camera-roll/loading-more is-loading)})
(fx/defn on-camera-roll-get-photos (rf/defn on-camera-roll-get-photos
{:events [:on-camera-roll-get-photos]} {:events [:on-camera-roll-get-photos]}
[{:keys [db] :as cofx} photos page-info end-cursor] [{:keys [db] :as cofx} photos page-info end-cursor]
(let [photos_x (when end-cursor (:camera-roll/photos db))] (let [photos_x (when end-cursor (:camera-roll/photos db))]
@@ -147,34 +156,34 @@
(assoc :camera-roll/has-next-page (:has_next_page page-info)) (assoc :camera-roll/has-next-page (:has_next_page page-info))
(assoc :camera-roll/loading-more false))})) (assoc :camera-roll/loading-more false))}))
(fx/defn clear-sending-images (rf/defn clear-sending-images
{:events [:chat.ui/clear-sending-images]} {:events [:chat.ui/clear-sending-images]}
[{:keys [db]} current-chat-id] [{:keys [db]} current-chat-id]
{:db (update-in db [:chat/inputs current-chat-id :metadata] assoc :sending-image {})}) {:db (update-in db [:chat/inputs current-chat-id :metadata] assoc :sending-image {})})
(fx/defn cancel-sending-image (rf/defn cancel-sending-image
{:events [:chat.ui/cancel-sending-image]} {:events [:chat.ui/cancel-sending-image]}
[{:keys [db] :as cofx} chat-id] [{:keys [db] :as cofx} chat-id]
(let [current-chat-id (or chat-id (:current-chat-id db))] (let [current-chat-id (or chat-id (:current-chat-id db))]
(clear-sending-images cofx current-chat-id))) (clear-sending-images cofx current-chat-id)))
(fx/defn cancel-sending-image-timeline (rf/defn cancel-sending-image-timeline
{:events [:chat.ui/cancel-sending-image-timeline]} {:events [:chat.ui/cancel-sending-image-timeline]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(cancel-sending-image cofx (chat/my-profile-chat-topic db))) (cancel-sending-image cofx (chat/my-profile-chat-topic db)))
(fx/defn image-selected (rf/defn image-selected
{:events [:chat.ui/image-selected]} {:events [:chat.ui/image-selected]}
[{:keys [db]} current-chat-id original uri] [{:keys [db]} current-chat-id original uri]
{:db (update-in db [:chat/inputs current-chat-id :metadata :sending-image original] merge {:uri uri})}) {:db (update-in db [:chat/inputs current-chat-id :metadata :sending-image original] merge {:uri uri})})
(fx/defn image-unselected (rf/defn image-unselected
{:events [:chat.ui/image-unselected]} {:events [:chat.ui/image-unselected]}
[{:keys [db]} original] [{:keys [db]} original]
(let [current-chat-id (:current-chat-id db)] (let [current-chat-id (:current-chat-id db)]
{:db (update-in db [:chat/inputs current-chat-id :metadata :sending-image] dissoc original)})) {:db (update-in db [:chat/inputs current-chat-id :metadata :sending-image] dissoc original)}))
(fx/defn chat-open-image-picker (rf/defn chat-open-image-picker
{:events [:chat.ui/open-image-picker]} {:events [:chat.ui/open-image-picker]}
[{:keys [db]} chat-id] [{:keys [db]} chat-id]
(let [current-chat-id (or chat-id (:current-chat-id db)) (let [current-chat-id (or chat-id (:current-chat-id db))
@@ -182,12 +191,12 @@
(when (< (count images) config/max-images-batch) (when (< (count images) config/max-images-batch)
{::chat-open-image-picker current-chat-id}))) {::chat-open-image-picker current-chat-id})))
(fx/defn chat-open-image-picker-timeline (rf/defn chat-open-image-picker-timeline
{:events [:chat.ui/open-image-picker-timeline]} {:events [:chat.ui/open-image-picker-timeline]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(chat-open-image-picker cofx (chat/my-profile-chat-topic db))) (chat-open-image-picker cofx (chat/my-profile-chat-topic db)))
(fx/defn chat-show-image-picker-camera (rf/defn chat-show-image-picker-camera
{:events [:chat.ui/show-image-picker-camera]} {:events [:chat.ui/show-image-picker-camera]}
[{:keys [db]} chat-id] [{:keys [db]} chat-id]
(let [current-chat-id (or chat-id (:current-chat-id db)) (let [current-chat-id (or chat-id (:current-chat-id db))
@@ -195,12 +204,12 @@
(when (< (count images) config/max-images-batch) (when (< (count images) config/max-images-batch)
{::chat-open-image-picker-camera current-chat-id}))) {::chat-open-image-picker-camera current-chat-id})))
(fx/defn chat-show-image-picker-camera-timeline (rf/defn chat-show-image-picker-camera-timeline
{:events [:chat.ui/show-image-picker-camera-timeline]} {:events [:chat.ui/show-image-picker-camera-timeline]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(chat-show-image-picker-camera cofx (chat/my-profile-chat-topic db))) (chat-show-image-picker-camera cofx (chat/my-profile-chat-topic db)))
(fx/defn camera-roll-pick (rf/defn camera-roll-pick
{:events [:chat.ui/camera-roll-pick]} {:events [:chat.ui/camera-roll-pick]}
[{:keys [db]} uri chat-id] [{:keys [db]} uri chat-id]
(let [current-chat-id (or chat-id (:current-chat-id db)) (let [current-chat-id (or chat-id (:current-chat-id db))
@@ -212,12 +221,12 @@
(not (get images uri))) (not (get images uri)))
{::image-selected [uri current-chat-id]})))) {::image-selected [uri current-chat-id]}))))
(fx/defn camera-roll-pick-timeline (rf/defn camera-roll-pick-timeline
{:events [:chat.ui/camera-roll-pick-timeline]} {:events [:chat.ui/camera-roll-pick-timeline]}
[{:keys [db] :as cofx} uri] [{:keys [db] :as cofx} uri]
(camera-roll-pick cofx uri (chat/my-profile-chat-topic db))) (camera-roll-pick cofx uri (chat/my-profile-chat-topic db)))
(fx/defn save-image-to-gallery (rf/defn save-image-to-gallery
{:events [:chat.ui/save-image-to-gallery]} {:events [:chat.ui/save-image-to-gallery]}
[_ base64-uri] [_ base64-uri]
{::save-image-to-gallery base64-uri}) {::save-image-to-gallery base64-uri})
+117 -98
View File
@@ -12,9 +12,9 @@
[status-im.ethereum.json-rpc :as json-rpc] [status-im.ethereum.json-rpc :as json-rpc]
[status-im.i18n.i18n :as i18n] [status-im.i18n.i18n :as i18n]
[status-im.utils.datetime :as datetime] [status-im.utils.datetime :as datetime]
[status-im.utils.fx :as fx]
[status-im.utils.utils :as utils] [status-im.utils.utils :as utils]
[taoensso.timbre :as log])) [taoensso.timbre :as log]
[utils.re-frame :as rf]))
(defn text->emoji (defn text->emoji
"Replaces emojis in a specified `text`" "Replaces emojis in a specified `text`"
@@ -26,7 +26,7 @@
(.-char ^js emoji-map) (.-char ^js emoji-map)
original)))) original))))
(fx/defn set-chat-input-text (rf/defn set-chat-input-text
"Set input text for current-chat. Takes db and input text and cofx "Set input text for current-chat. Takes db and input text and cofx
as arguments and returns new fx. Always clear all validation messages." as arguments and returns new fx. Always clear all validation messages."
{:events [:chat.ui/set-chat-input-text]} {:events [:chat.ui/set-chat-input-text]}
@@ -34,24 +34,25 @@
(let [current-chat-id (or chat-id (:current-chat-id db))] (let [current-chat-id (or chat-id (:current-chat-id db))]
{:db (assoc-in db [:chat/inputs current-chat-id :input-text] (text->emoji new-input))})) {:db (assoc-in db [:chat/inputs current-chat-id :input-text] (text->emoji new-input))}))
(fx/defn set-timeline-input-text (rf/defn set-timeline-input-text
{:events [:chat.ui/set-timeline-input-text]} {:events [:chat.ui/set-timeline-input-text]}
[{db :db} new-input] [{db :db} new-input]
{:db (assoc-in db [:chat/inputs (chat/my-profile-chat-topic db) :input-text] {:db (assoc-in db
(text->emoji new-input))}) [:chat/inputs (chat/my-profile-chat-topic db) :input-text]
(text->emoji new-input))})
(fx/defn select-mention (rf/defn select-mention
{:events [:chat.ui/select-mention]} {:events [:chat.ui/select-mention]}
[{:keys [db] :as cofx} text-input-ref {:keys [alias name searched-text match] :as user}] [{:keys [db] :as cofx} text-input-ref {:keys [alias name searched-text match] :as user}]
(let [chat-id (:current-chat-id db) (let [chat-id (:current-chat-id db)
new-text (mentions/new-input-text-with-mention cofx user) new-text (mentions/new-input-text-with-mention cofx user)
at-sign-idx (get-in db [:chats/mentions chat-id :mentions :at-sign-idx]) at-sign-idx (get-in db [:chats/mentions chat-id :mentions :at-sign-idx])
cursor (+ at-sign-idx (count name) 2)] cursor (+ at-sign-idx (count name) 2)]
(fx/merge (rf/merge
cofx cofx
{:db (-> db {:db (-> db
(assoc-in [:chats/cursor chat-id] cursor) (assoc-in [:chats/cursor chat-id] cursor)
(assoc-in [:chats/mention-suggestions chat-id] nil)) (assoc-in [:chats/mention-suggestions chat-id] nil))
:set-text-input-value [chat-id new-text text-input-ref]} :set-text-input-value [chat-id new-text text-input-ref]}
(set-chat-input-text new-text chat-id) (set-chat-input-text new-text chat-id)
;; NOTE(rasom): Some keyboards do not react on selection property passed to ;; NOTE(rasom): Some keyboards do not react on selection property passed to
@@ -74,67 +75,77 @@
:end end})) :end end}))
(mentions/recheck-at-idxs {alias user})))) (mentions/recheck-at-idxs {alias user}))))
(defn- start-cooldown [{:keys [db]} cooldowns] (defn- start-cooldown
[{:keys [db]} cooldowns]
{:dispatch-later [{:dispatch [:chat/disable-cooldown] {:dispatch-later [{:dispatch [:chat/disable-cooldown]
:ms (chat.constants/cooldown-periods-ms :ms (chat.constants/cooldown-periods-ms
cooldowns cooldowns
chat.constants/default-cooldown-period-ms)}] chat.constants/default-cooldown-period-ms)}]
:show-cooldown-warning nil :show-cooldown-warning nil
:db (assoc db :db (assoc db
:chat/cooldowns (if (= chat.constants/cooldown-reset-threshold cooldowns) :chat/cooldowns (if
0 (=
cooldowns) chat.constants/cooldown-reset-threshold
cooldowns)
0
cooldowns)
:chat/spam-messages-frequency 0 :chat/spam-messages-frequency 0
:chat/cooldown-enabled? true)}) :chat/cooldown-enabled? true)})
(fx/defn process-cooldown (rf/defn process-cooldown
"Process cooldown to protect against message spammers" "Process cooldown to protect against message spammers"
[{{:keys [chat/last-outgoing-message-sent-at [{{:keys [chat/last-outgoing-message-sent-at
chat/cooldowns chat/cooldowns
chat/spam-messages-frequency chat/spam-messages-frequency
current-chat-id] :as db} :db :as cofx}] current-chat-id]
:as db}
:db
:as cofx}]
(when (chat/public-chat? cofx current-chat-id) (when (chat/public-chat? cofx current-chat-id)
(let [spamming-fast? (< (- (datetime/timestamp) last-outgoing-message-sent-at) (let [spamming-fast? (< (- (datetime/timestamp) last-outgoing-message-sent-at)
(+ chat.constants/spam-interval-ms (* 1000 cooldowns))) (+ chat.constants/spam-interval-ms (* 1000 cooldowns)))
spamming-frequently? (= chat.constants/spam-message-frequency-threshold spam-messages-frequency)] spamming-frequently? (= chat.constants/spam-message-frequency-threshold
spam-messages-frequency)]
(cond-> {:db (assoc db (cond-> {:db (assoc db
:chat/last-outgoing-message-sent-at (datetime/timestamp) :chat/last-outgoing-message-sent-at (datetime/timestamp)
:chat/spam-messages-frequency (if spamming-fast? :chat/spam-messages-frequency (if spamming-fast?
(inc spam-messages-frequency) (inc spam-messages-frequency)
0))} 0))}
(and spamming-fast? spamming-frequently?) (and spamming-fast? spamming-frequently?)
(start-cooldown (inc cooldowns)))))) (start-cooldown (inc cooldowns))))))
(fx/defn reply-to-message (rf/defn reply-to-message
"Sets reference to previous chat message and focuses on input" "Sets reference to previous chat message and focuses on input"
{:events [:chat.ui/reply-to-message]} {:events [:chat.ui/reply-to-message]}
[{:keys [db] :as cofx} message] [{:keys [db] :as cofx} message]
(let [current-chat-id (:current-chat-id db)] (let [current-chat-id (:current-chat-id db)]
(fx/merge cofx (rf/merge cofx
{:db (-> db {:db (-> db
(assoc-in [:chat/inputs current-chat-id :metadata :responding-to-message] (assoc-in [:chat/inputs current-chat-id :metadata :responding-to-message]
message) message)
(assoc-in [:chat/inputs current-chat-id :metadata :editing-message] nil) (assoc-in [:chat/inputs current-chat-id :metadata :editing-message] nil)
(update-in [:chat/inputs current-chat-id :metadata] (update-in [:chat/inputs current-chat-id :metadata]
dissoc :sending-image))}))) dissoc
:sending-image))})))
(fx/defn edit-message (rf/defn edit-message
"Sets reference to previous chat message and focuses on input" "Sets reference to previous chat message and focuses on input"
{:events [:chat.ui/edit-message]} {:events [:chat.ui/edit-message]}
[{:keys [db] :as cofx} message] [{:keys [db] :as cofx} message]
(let [current-chat-id (:current-chat-id db) (let [current-chat-id (:current-chat-id db)
text (get-in message [:content :text])] text (get-in message [:content :text])]
{:dispatch [:chat.ui.input/set-chat-input-text text current-chat-id] {:dispatch [:chat.ui.input/set-chat-input-text text current-chat-id]
:db (-> db :db (-> db
(assoc-in [:chat/inputs current-chat-id :metadata :editing-message] (assoc-in [:chat/inputs current-chat-id :metadata :editing-message]
message) message)
(assoc-in [:chat/inputs current-chat-id :metadata :responding-to-message] nil) (assoc-in [:chat/inputs current-chat-id :metadata :responding-to-message] nil)
(update-in [:chat/inputs current-chat-id :metadata] (update-in [:chat/inputs current-chat-id :metadata]
dissoc :sending-image))})) dissoc
:sending-image))}))
(fx/defn show-contact-request-input (rf/defn show-contact-request-input
"Sets reference to previous chat message and focuses on input" "Sets reference to previous chat message and focuses on input"
{:events [:chat.ui/send-contact-request]} {:events [:chat.ui/send-contact-request]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
@@ -146,9 +157,10 @@
nil) nil)
(assoc-in [:chat/inputs current-chat-id :metadata :editing-message] nil) (assoc-in [:chat/inputs current-chat-id :metadata :editing-message] nil)
(update-in [:chat/inputs current-chat-id :metadata] (update-in [:chat/inputs current-chat-id :metadata]
dissoc :sending-image))})) dissoc
:sending-image))}))
(fx/defn cancel-message-reply (rf/defn cancel-message-reply
"Cancels stage message reply" "Cancels stage message reply"
{:events [:chat.ui/cancel-message-reply]} {:events [:chat.ui/cancel-message-reply]}
[{:keys [db]}] [{:keys [db]}]
@@ -160,16 +172,16 @@
(when-not (string/blank? input-text) (when-not (string/blank? input-text)
(let [{:keys [message-id]} (let [{:keys [message-id]}
(get-in db [:chat/inputs current-chat-id :metadata :responding-to-message]) (get-in db [:chat/inputs current-chat-id :metadata :responding-to-message])
preferred-name (get-in db [:multiaccount :preferred-name]) preferred-name (get-in db [:multiaccount :preferred-name])
emoji? (message-content/emoji-only-content? {:text input-text emoji? (message-content/emoji-only-content? {:text input-text
:response-to message-id})] :response-to message-id})]
{:chat-id current-chat-id {:chat-id current-chat-id
:content-type (if emoji? :content-type (if emoji?
constants/content-type-emoji constants/content-type-emoji
constants/content-type-text) constants/content-type-text)
:text input-text :text input-text
:response-to message-id :response-to message-id
:ens-name preferred-name}))) :ens-name preferred-name})))
(defn build-image-messages (defn build-image-messages
[{db :db} chat-id] [{db :db} chat-id]
@@ -181,8 +193,9 @@
:text (i18n/label :t/update-to-see-image {"locale" "en"})}) :text (i18n/label :t/update-to-see-image {"locale" "en"})})
images))) images)))
(fx/defn clean-input [{:keys [db] :as cofx} current-chat-id] (rf/defn clean-input
(fx/merge cofx [{:keys [db] :as cofx} current-chat-id]
(rf/merge cofx
{:db (-> db {:db (-> db
(assoc-in [:chat/inputs current-chat-id :metadata :sending-contact-request] nil) (assoc-in [:chat/inputs current-chat-id :metadata :sending-contact-request] nil)
(assoc-in [:chat/inputs current-chat-id :metadata :sending-image] nil) (assoc-in [:chat/inputs current-chat-id :metadata :sending-image] nil)
@@ -190,131 +203,137 @@
(assoc-in [:chat/inputs current-chat-id :metadata :responding-to-message] nil))} (assoc-in [:chat/inputs current-chat-id :metadata :responding-to-message] nil))}
(set-chat-input-text nil current-chat-id))) (set-chat-input-text nil current-chat-id)))
(fx/defn cancel-message-edit (rf/defn cancel-message-edit
"Cancels stage message edit" "Cancels stage message edit"
{:events [:chat.ui/cancel-message-edit]} {:events [:chat.ui/cancel-message-edit]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(let [current-chat-id (:current-chat-id db)] (let [current-chat-id (:current-chat-id db)]
(fx/merge cofx (rf/merge cofx
{:set-text-input-value [current-chat-id ""]} {:set-text-input-value [current-chat-id ""]}
(clean-input current-chat-id) (clean-input current-chat-id)
(mentions/clear-mentions) (mentions/clear-mentions)
(mentions/clear-cursor)))) (mentions/clear-cursor))))
(fx/defn send-messages [{:keys [db] :as cofx} input-text current-chat-id] (rf/defn send-messages
[{:keys [db] :as cofx} input-text current-chat-id]
(let [image-messages (build-image-messages cofx current-chat-id) (let [image-messages (build-image-messages cofx current-chat-id)
text-message (build-text-message cofx input-text current-chat-id) text-message (build-text-message cofx input-text current-chat-id)
messages (keep identity (conj image-messages text-message))] messages (keep identity (conj image-messages text-message))]
(when (seq messages) (when (seq messages)
(fx/merge cofx (rf/merge cofx
(clean-input (:current-chat-id db)) (clean-input (:current-chat-id db))
(process-cooldown) (process-cooldown)
(chat.message/send-messages messages))))) (chat.message/send-messages messages)))))
(fx/defn send-my-status-message (rf/defn send-my-status-message
"when not empty, proceed by sending text message with public key topic" "when not empty, proceed by sending text message with public key topic"
{:events [:profile.ui/send-my-status-message]} {:events [:profile.ui/send-my-status-message]}
[{db :db :as cofx}] [{db :db :as cofx}]
(let [current-chat-id (chat/my-profile-chat-topic db) (let [current-chat-id (chat/my-profile-chat-topic db)
{:keys [input-text]} (get-in db [:chat/inputs current-chat-id]) {:keys [input-text]} (get-in db [:chat/inputs current-chat-id])
image-messages (build-image-messages cofx current-chat-id) image-messages (build-image-messages cofx current-chat-id)
text-message (build-text-message cofx input-text current-chat-id) text-message (build-text-message cofx input-text current-chat-id)
messages (keep identity (conj image-messages text-message))] messages (keep identity (conj image-messages text-message))]
(when (seq messages) (when (seq messages)
(fx/merge cofx (rf/merge cofx
(clean-input current-chat-id) (clean-input current-chat-id)
(chat.message/send-messages messages))))) (chat.message/send-messages messages)))))
(fx/defn send-audio-message (rf/defn send-audio-message
[cofx audio-path duration current-chat-id] [cofx audio-path duration current-chat-id]
(when-not (string/blank? audio-path) (when-not (string/blank? audio-path)
(chat.message/send-message cofx {:chat-id current-chat-id (chat.message/send-message
:content-type constants/content-type-audio cofx
:audio-path audio-path {:chat-id current-chat-id
:audio-duration-ms duration :content-type constants/content-type-audio
:text (i18n/label :t/update-to-listen-audio {"locale" "en"})}))) :audio-path audio-path
:audio-duration-ms duration
:text (i18n/label :t/update-to-listen-audio {"locale" "en"})})))
(fx/defn send-sticker-message (rf/defn send-sticker-message
[cofx {:keys [hash packID pack]} current-chat-id] [cofx {:keys [hash packID pack]} current-chat-id]
(when-not (or (string/blank? hash) (and (string/blank? packID) (string/blank? pack))) (when-not (or (string/blank? hash) (and (string/blank? packID) (string/blank? pack)))
(chat.message/send-message cofx {:chat-id current-chat-id (chat.message/send-message cofx
:content-type constants/content-type-sticker {:chat-id current-chat-id
:sticker {:hash hash :content-type constants/content-type-sticker
:sticker {:hash hash
:pack (int (if (string/blank? packID) pack packID))} :pack (int (if (string/blank? packID) pack packID))}
:text (i18n/label :t/update-to-see-sticker {"locale" "en"})}))) :text (i18n/label :t/update-to-see-sticker {"locale" "en"})})))
(fx/defn send-edited-message [{:keys [db] :as cofx} text {:keys [message-id quoted-message]}] (rf/defn send-edited-message
(fx/merge [{:keys [db] :as cofx} text {:keys [message-id quoted-message]}]
(rf/merge
cofx cofx
{::json-rpc/call [{:method "wakuext_editMessage" {::json-rpc/call [{:method "wakuext_editMessage"
:params [{:id message-id :params [{:id message-id
:text text :text text
:content-type (if (message-content/emoji-only-content? {:text text :response-to quoted-message}) :content-type (if (message-content/emoji-only-content?
constants/content-type-emoji {:text text :response-to quoted-message})
constants/content-type-text)}] constants/content-type-emoji
constants/content-type-text)}]
:js-response true :js-response true
:on-error #(log/error "failed to edit message " %) :on-error #(log/error "failed to edit message " %)
:on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %])}]} :on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %])}]}
(cancel-message-edit) (cancel-message-edit)
(process-cooldown))) (process-cooldown)))
(fx/defn send-current-message (rf/defn send-current-message
"Sends message from current chat input" "Sends message from current chat input"
{:events [:chat.ui/send-current-message]} {:events [:chat.ui/send-current-message]}
[{{:keys [current-chat-id] :as db} :db :as cofx}] [{{:keys [current-chat-id] :as db} :db :as cofx}]
(let [{:keys [input-text metadata]} (get-in db [:chat/inputs current-chat-id]) (let [{:keys [input-text metadata]} (get-in db [:chat/inputs current-chat-id])
editing-message (:editing-message metadata) editing-message (:editing-message metadata)
input-text-with-mentions (mentions/check-mentions cofx input-text)] input-text-with-mentions (mentions/check-mentions cofx input-text)]
(fx/merge cofx (rf/merge cofx
(if editing-message (if editing-message
(send-edited-message input-text-with-mentions editing-message) (send-edited-message input-text-with-mentions editing-message)
(send-messages input-text-with-mentions current-chat-id)) (send-messages input-text-with-mentions current-chat-id))
(mentions/clear-mentions) (mentions/clear-mentions)
(mentions/clear-cursor)))) (mentions/clear-cursor))))
(fx/defn send-contact-request (rf/defn send-contact-request
{:events [:contacts/send-contact-request]} {:events [:contacts/send-contact-request]}
[{:keys [db] :as cofx} public-key message] [{:keys [db] :as cofx} public-key message]
(fx/merge cofx (rf/merge cofx
{:chat.ui/clear-inputs nil {:chat.ui/clear-inputs nil
:chat.ui/clear-inputs-old nil :chat.ui/clear-inputs-old nil
::json-rpc/call [{:method "wakuext_sendContactRequest" ::json-rpc/call [{:method "wakuext_sendContactRequest"
:js-response true :js-response true
:params [{:id public-key :message message}] :params [{:id public-key :message message}]
:on-error #(log/warn "failed to send a contact request" %) :on-error #(log/warn "failed to send a contact request" %)
:on-success #(re-frame/dispatch [:transport/message-sent %])}]} :on-success #(re-frame/dispatch [:transport/message-sent %])}]}
(mentions/clear-mentions) (mentions/clear-mentions)
(mentions/clear-cursor) (mentions/clear-cursor)
(clean-input (:current-chat-id db)) (clean-input (:current-chat-id db))
(process-cooldown))) (process-cooldown)))
(fx/defn cancel-contact-request (rf/defn cancel-contact-request
"Cancels contact request" "Cancels contact request"
{:events [:chat.ui/cancel-contact-request]} {:events [:chat.ui/cancel-contact-request]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(let [current-chat-id (:current-chat-id db)] (let [current-chat-id (:current-chat-id db)]
(fx/merge cofx (rf/merge cofx
{:db (assoc-in db [:chat/inputs current-chat-id :metadata :sending-contact-request] nil)} {:db (assoc-in db [:chat/inputs current-chat-id :metadata :sending-contact-request] nil)}
(mentions/clear-mentions) (mentions/clear-mentions)
(mentions/clear-cursor) (mentions/clear-cursor)
(clean-input (:current-chat-id db)) (clean-input (:current-chat-id db))
(process-cooldown)))) (process-cooldown))))
(fx/defn chat-send-sticker (rf/defn chat-send-sticker
{:events [:chat/send-sticker]} {:events [:chat/send-sticker]}
[{{:keys [current-chat-id] :as db} :db :as cofx} {:keys [hash packID pack] :as sticker}] [{{:keys [current-chat-id] :as db} :db :as cofx} {:keys [hash packID pack] :as sticker}]
(fx/merge (rf/merge
cofx cofx
{:db (update db {:db (update db
:stickers/recent-stickers :stickers/recent-stickers
(fn [recent] (fn [recent]
(conj (remove #(= hash (:hash %)) recent) sticker))) (conj (remove #(= hash (:hash %)) recent) sticker)))
::json-rpc/call [{:method "stickers_addRecent" ::json-rpc/call [{:method "stickers_addRecent"
:params [(int (if (string/blank? packID) pack packID)) hash] :params [(int (if (string/blank? packID) pack packID)) hash]
:on-success #()}]} :on-success #()}]}
(send-sticker-message sticker current-chat-id))) (send-sticker-message sticker current-chat-id)))
(fx/defn chat-send-audio (rf/defn chat-send-audio
{:events [:chat/send-audio]} {:events [:chat/send-audio]}
[{{:keys [current-chat-id]} :db :as cofx} audio-path duration] [{{:keys [current-chat-id]} :db :as cofx} audio-path duration]
(send-audio-message cofx audio-path duration current-chat-id)) (send-audio-message cofx audio-path duration current-chat-id))
+37 -29
View File
@@ -1,15 +1,15 @@
(ns status-im.chat.models.link-preview (ns status-im.chat.models.link-preview
(:require [re-frame.core :as re-frame] (:require [re-frame.core :as re-frame]
[status-im.utils.fx :as fx]
[status-im.multiaccounts.update.core :as multiaccounts.update]
[status-im.ethereum.json-rpc :as json-rpc]
[status-im.communities.core :as models.communities] [status-im.communities.core :as models.communities]
[taoensso.timbre :as log])) [status-im.ethereum.json-rpc :as json-rpc]
[status-im.multiaccounts.update.core :as multiaccounts.update]
[taoensso.timbre :as log]
[utils.re-frame :as rf]))
(fx/defn enable (rf/defn enable
{:events [::enable]} {:events [::enable]}
[{{:keys [multiaccount]} :db :as cofx} site enabled?] [{{:keys [multiaccount]} :db :as cofx} site enabled?]
(fx/merge cofx (rf/merge cofx
(multiaccounts.update/multiaccount-update (multiaccounts.update/multiaccount-update
:link-previews-enabled-sites :link-previews-enabled-sites
(if enabled? (if enabled?
@@ -17,10 +17,10 @@
(disj (get multiaccount :link-previews-enabled-sites #{}) site)) (disj (get multiaccount :link-previews-enabled-sites #{}) site))
{}))) {})))
(fx/defn enable-all (rf/defn enable-all
{:events [::enable-all]} {:events [::enable-all]}
[{{:keys [multiaccount]} :db :as cofx} link-previews-whitelist enabled?] [{{:keys [multiaccount]} :db :as cofx} link-previews-whitelist enabled?]
(fx/merge cofx (rf/merge cofx
(multiaccounts.update/multiaccount-update (multiaccounts.update/multiaccount-update
:link-previews-enabled-sites :link-previews-enabled-sites
(if enabled? (if enabled?
@@ -28,37 +28,42 @@
#{}) #{})
{}))) {})))
(defn community-resolved [db community-id] (defn community-resolved
[db community-id]
(update db :communities/resolve-community-info dissoc community-id)) (update db :communities/resolve-community-info dissoc community-id))
(defn community-failed-to-resolve [db community-id] (defn community-failed-to-resolve
[db community-id]
(update db :communities/resolve-community-info dissoc community-id)) (update db :communities/resolve-community-info dissoc community-id))
(defn community-resolving [db community-id] (defn community-resolving
[db community-id]
(assoc-in db [:communities/resolve-community-info community-id] true)) (assoc-in db [:communities/resolve-community-info community-id] true))
(fx/defn handle-community-failed-to-resolve (rf/defn handle-community-failed-to-resolve
{:events [::community-failed-to-resolve]} {:events [::community-failed-to-resolve]}
[{:keys [db]} community-id] [{:keys [db]} community-id]
{:db (community-failed-to-resolve db community-id)}) {:db (community-failed-to-resolve db community-id)})
(defn community-link [id] (defn community-link
[id]
(str "https://join.status.im/c/" id)) (str "https://join.status.im/c/" id))
(fx/defn handle-community-resolved (rf/defn handle-community-resolved
{:events [::community-resolved]} {:events [::community-resolved]}
[{:keys [db] :as cofx} community-id community] [{:keys [db] :as cofx} community-id community]
(fx/merge cofx (rf/merge cofx
(cond-> {:db (community-resolved db community-id)} (cond-> {:db (community-resolved db community-id)}
(some? community) (some? community)
(assoc :dispatch [::cache-link-preview-data (assoc :dispatch
(community-link community-id) community])) [::cache-link-preview-data
(community-link community-id) community]))
(models.communities/handle-community community))) (models.communities/handle-community community)))
(fx/defn resolve-community-info (rf/defn resolve-community-info
{:events [::resolve-community-info]} {:events [::resolve-community-info]}
[{:keys [db]} community-id] [{:keys [db]} community-id]
{:db (community-resolving db community-id) {:db (community-resolving db community-id)
::json-rpc/call [{:method "wakuext_requestCommunityInfoFromMailserver" ::json-rpc/call [{:method "wakuext_requestCommunityInfoFromMailserver"
:params [community-id] :params [community-id]
:on-success #(re-frame/dispatch [::community-resolved community-id %]) :on-success #(re-frame/dispatch [::community-resolved community-id %])
@@ -66,17 +71,18 @@
(re-frame/dispatch [::community-failed-to-resolve community-id]) (re-frame/dispatch [::community-failed-to-resolve community-id])
(log/error "Failed to request community info from mailserver"))}]}) (log/error "Failed to request community info from mailserver"))}]})
(fx/defn load-link-preview-data (rf/defn load-link-preview-data
{:events [::load-link-preview-data]} {:events [::load-link-preview-data]}
[cofx link] [cofx link]
{::json-rpc/call [{:method "wakuext_getLinkPreviewData" {::json-rpc/call [{:method "wakuext_getLinkPreviewData"
:params [link] :params [link]
:on-success #(re-frame/dispatch [::cache-link-preview-data link %]) :on-success #(re-frame/dispatch [::cache-link-preview-data link %])
:on-error #(re-frame/dispatch [::cache-link-preview-data :on-error #(re-frame/dispatch
link [::cache-link-preview-data
{:error (str "Can't get preview data for " link)}])}]}) link
{:error (str "Can't get preview data for " link)}])}]})
(fx/defn cache-link-preview-data (rf/defn cache-link-preview-data
{:events [::cache-link-preview-data]} {:events [::cache-link-preview-data]}
[{{:keys [multiaccount]} :db :as cofx} site data] [{{:keys [multiaccount]} :db :as cofx} site data]
(multiaccounts.update/optimistic (multiaccounts.update/optimistic
@@ -90,23 +96,25 @@
(community-link id) (community-link id)
community])) community]))
(fx/defn should-suggest-link-preview (rf/defn should-suggest-link-preview
{:events [::should-suggest-link-preview]} {:events [::should-suggest-link-preview]}
[{:keys [db] :as cofx} enabled?] [{:keys [db] :as cofx} enabled?]
(multiaccounts.update/multiaccount-update (multiaccounts.update/multiaccount-update
cofx cofx
:link-preview-request-enabled (boolean enabled?) :link-preview-request-enabled
(boolean enabled?)
{})) {}))
(fx/defn request-link-preview-whitelist (rf/defn request-link-preview-whitelist
[_] [_]
{::json-rpc/call [{:method "wakuext_getLinkPreviewWhitelist" {::json-rpc/call [{:method "wakuext_getLinkPreviewWhitelist"
:params [] :params []
:on-success #(re-frame/dispatch [::link-preview-whitelist-received %]) :on-success #(re-frame/dispatch [::link-preview-whitelist-received %])
:on-error #(log/error "Failed to get link preview whitelist")}]}) :on-error #(log/error "Failed to get link preview whitelist")}]})
(fx/defn save-link-preview-whitelist (rf/defn save-link-preview-whitelist
{:events [::link-preview-whitelist-received]} {:events [::link-preview-whitelist-received]}
[{:keys [db]} whitelist] [{:keys [db]} whitelist]
{:db (assoc db :link-previews-whitelist {:db (assoc db
:link-previews-whitelist
whitelist)}) whitelist)})
+53 -43
View File
@@ -1,24 +1,25 @@
(ns status-im.chat.models.loading (ns status-im.chat.models.loading
(:require [re-frame.core :as re-frame] (:require [re-frame.core :as re-frame]
[status-im.chat.models.message-list :as message-list]
[status-im.constants :as constants] [status-im.constants :as constants]
[status-im.data-store.chats :as data-store.chats] [status-im.data-store.chats :as data-store.chats]
[status-im.data-store.messages :as data-store.messages] [status-im.data-store.messages :as data-store.messages]
[status-im.utils.fx :as fx]
[status-im.chat.models.message-list :as message-list]
[taoensso.timbre :as log]
[status-im.ethereum.json-rpc :as json-rpc] [status-im.ethereum.json-rpc :as json-rpc]
[status-im2.contexts.activity-center.events :as activity-center])) [status-im2.contexts.activity-center.events :as activity-center]
[taoensso.timbre :as log]
[utils.re-frame :as rf]))
(defn cursor->clock-value (defn cursor->clock-value
[^js cursor] [^js cursor]
(js/parseInt (.substring cursor 51 64))) (js/parseInt (.substring cursor 51 64)))
(defn clock-value->cursor [clock-value] (defn clock-value->cursor
[clock-value]
(str "000000000000000000000000000000000000000000000000000" (str "000000000000000000000000000000000000000000000000000"
clock-value clock-value
"0x0000000000000000000000000000000000000000000000000000000000000000")) "0x0000000000000000000000000000000000000000000000000000000000000000"))
(fx/defn update-chats-in-app-db (rf/defn update-chats-in-app-db
{:events [:chats-list/load-success]} {:events [:chats-list/load-success]}
[{:keys [db]} ^js new-chats-js] [{:keys [db]} ^js new-chats-js]
(let [{:keys [all-chats chats-home-list]} (let [{:keys [all-chats chats-home-list]}
@@ -30,27 +31,28 @@
(update :chats-home-list conj chat-id) (update :chats-home-list conj chat-id)
:always :always
(assoc-in [:all-chats chat-id] chat)))) (assoc-in [:all-chats chat-id] chat))))
{:all-chats {} {:all-chats {}
:chats-home-list #{}} :chats-home-list #{}}
new-chats-js)] new-chats-js)]
{:db (assoc db :chats all-chats {:db (assoc db
:chats all-chats
:chats-home-list chats-home-list :chats-home-list chats-home-list
:chats/loading? false)})) :chats/loading? false)}))
(fx/defn load-chat-success (rf/defn load-chat-success
{:events [:chats-list/load-chat-success]} {:events [:chats-list/load-chat-success]}
[{:keys [db]} ^js chat] [{:keys [db]} ^js chat]
(let [{:keys [chat-id] :as chat} (data-store.chats/<-rpc chat)] (let [{:keys [chat-id] :as chat} (data-store.chats/<-rpc chat)]
{:db (update-in db [:chats chat-id] merge chat)})) {:db (update-in db [:chats chat-id] merge chat)}))
(fx/defn load-chat (rf/defn load-chat
[_ chat-id] [_ chat-id]
{::json-rpc/call [{:method "wakuext_chat" {::json-rpc/call [{:method "wakuext_chat"
:params [chat-id] :params [chat-id]
:on-success #(re-frame/dispatch [:chats-list/load-chat-success %]) :on-success #(re-frame/dispatch [:chats-list/load-chat-success %])
:on-error #(log/error "failed to fetch chats" 0 -1 %)}]}) :on-error #(log/error "failed to fetch chats" 0 -1 %)}]})
(fx/defn handle-failed-loading-messages (rf/defn handle-failed-loading-messages
{:events [::failed-loading-messages]} {:events [::failed-loading-messages]}
[{:keys [db]} current-chat-id _ err] [{:keys [db]} current-chat-id _ err]
(log/error "failed loading messages" current-chat-id err) (log/error "failed loading messages" current-chat-id err)
@@ -59,52 +61,55 @@
(defn mark-chat-all-read (defn mark-chat-all-read
[db chat-id] [db chat-id]
(update-in db [:chats chat-id] assoc (update-in db
[:chats chat-id]
assoc
:unviewed-messages-count 0 :unviewed-messages-count 0
:unviewed-mentions-count 0 :unviewed-mentions-count 0
:highlight false)) :highlight false))
(fx/defn handle-mark-all-read-successful (rf/defn handle-mark-all-read-successful
{:events [::mark-all-read-successful]} {:events [::mark-all-read-successful]}
[cofx] [cofx]
(activity-center/notifications-fetch-unread-count cofx)) (activity-center/notifications-fetch-unread-count cofx))
(fx/defn handle-mark-all-read-in-community-successful (rf/defn handle-mark-all-read-in-community-successful
{:events [::mark-all-read-in-community-successful]} {:events [::mark-all-read-in-community-successful]}
[{:keys [db] :as cofx} chat-ids] [{:keys [db] :as cofx} chat-ids]
(fx/merge cofx (rf/merge cofx
{:db (reduce mark-chat-all-read db chat-ids)} {:db (reduce mark-chat-all-read db chat-ids)}
(activity-center/notifications-fetch-unread-count))) (activity-center/notifications-fetch-unread-count)))
(fx/defn handle-mark-all-read (rf/defn handle-mark-all-read
{:events [:chat.ui/mark-all-read-pressed :chat/mark-all-as-read]} {:events [:chat.ui/mark-all-read-pressed :chat/mark-all-as-read]}
[{db :db} chat-id] [{db :db} chat-id]
{:db (mark-chat-all-read db chat-id) {:db (mark-chat-all-read db chat-id)
:clear-message-notifications [[chat-id] :clear-message-notifications [[chat-id]
(get-in db [:multiaccount :remote-push-notifications-enabled?])] (get-in db [:multiaccount :remote-push-notifications-enabled?])]
::json-rpc/call [{:method "wakuext_markAllRead" ::json-rpc/call [{:method "wakuext_markAllRead"
:params [chat-id] :params [chat-id]
:on-success #(re-frame/dispatch [::mark-all-read-successful])}]}) :on-success #(re-frame/dispatch [::mark-all-read-successful])}]})
(fx/defn handle-mark-mark-all-read-in-community (rf/defn handle-mark-mark-all-read-in-community
{:events [:chat.ui/mark-all-read-in-community-pressed]} {:events [:chat.ui/mark-all-read-in-community-pressed]}
[{db :db} community-id] [{db :db} community-id]
(let [community-chat-ids (map #(str community-id %) (let [community-chat-ids (map #(str community-id %)
(keys (get-in db [:communities community-id :chats])))] (keys (get-in db [:communities community-id :chats])))]
{:clear-message-notifications [community-chat-ids {:clear-message-notifications [community-chat-ids
(get-in db [:multiaccount :remote-push-notifications-enabled?])] (get-in db [:multiaccount :remote-push-notifications-enabled?])]
::json-rpc/call [{:method "wakuext_markAllReadInCommunity" ::json-rpc/call [{:method "wakuext_markAllReadInCommunity"
:params [community-id] :params [community-id]
:on-success #(re-frame/dispatch [::mark-all-read-in-community-successful %])}]})) :on-success #(re-frame/dispatch
[::mark-all-read-in-community-successful %])}]}))
(fx/defn messages-loaded (rf/defn messages-loaded
"Loads more messages for current chat" "Loads more messages for current chat"
{:events [::messages-loaded]} {:events [::messages-loaded]}
[{db :db} chat-id session-id {:keys [cursor messages]}] [{db :db} chat-id session-id {:keys [cursor messages]}]
(when-not (and (get-in db [:pagination-info chat-id :messages-initialized?]) (when-not (and (get-in db [:pagination-info chat-id :messages-initialized?])
(not= session-id (not= session-id
(get-in db [:pagination-info chat-id :messages-initialized?]))) (get-in db [:pagination-info chat-id :messages-initialized?])))
(let [already-loaded-messages (get-in db [:messages chat-id]) (let [already-loaded-messages (get-in db [:messages chat-id])
;; We remove those messages that are already loaded, as we might get some duplicates ;; We remove those messages that are already loaded, as we might get some duplicates
{:keys [all-messages new-messages senders contacts]} {:keys [all-messages new-messages senders contacts]}
(reduce (fn [{:keys [all-messages] :as acc} (reduce (fn [{:keys [all-messages] :as acc}
@@ -124,8 +129,11 @@
:contacts {} :contacts {}
:new-messages []} :new-messages []}
messages) messages)
current-clock-value (get-in db [:pagination-info chat-id :cursor-clock-value]) current-clock-value (get-in db
clock-value (when cursor (cursor->clock-value cursor))] [:pagination-info chat-id
:cursor-clock-value])
clock-value (when cursor
(cursor->clock-value cursor))]
{:dispatch [:chat/add-senders-to-chat-users (vals senders)] {:dispatch [:chat/add-senders-to-chat-users (vals senders)]
:db (-> db :db (-> db
(update-in [:pagination-info chat-id :cursor-clock-value] (update-in [:pagination-info chat-id :cursor-clock-value]
@@ -134,7 +142,9 @@
%)) %))
(update-in [:pagination-info chat-id :cursor] (update-in [:pagination-info chat-id :cursor]
#(if (or (empty? cursor) (not current-clock-value) (< clock-value current-clock-value)) #(if (or (empty? cursor)
(not current-clock-value)
(< clock-value current-clock-value))
cursor cursor
%)) %))
(assoc-in [:pagination-info chat-id :loading-messages?] false) (assoc-in [:pagination-info chat-id :loading-messages?] false)
@@ -144,7 +154,7 @@
(empty? cursor)) (empty? cursor))
(update :contacts/contacts merge contacts))}))) (update :contacts/contacts merge contacts))})))
(fx/defn load-more-messages (rf/defn load-more-messages
{:events [:chat.ui/load-more-messages]} {:events [:chat.ui/load-more-messages]}
[{:keys [db]} chat-id first-request] [{:keys [db]} chat-id first-request]
(when-let [session-id (get-in db [:pagination-info chat-id :messages-initialized?])] (when-let [session-id (get-in db [:pagination-info chat-id :messages-initialized?])]
@@ -163,16 +173,16 @@
#(re-frame/dispatch [::messages-loaded chat-id session-id %]) #(re-frame/dispatch [::messages-loaded chat-id session-id %])
#(re-frame/dispatch [::failed-loading-messages chat-id session-id %])))))))) #(re-frame/dispatch [::failed-loading-messages chat-id session-id %]))))))))
(fx/defn load-more-messages-for-current-chat (rf/defn load-more-messages-for-current-chat
{:events [:chat.ui/load-more-messages-for-current-chat]} {:events [:chat.ui/load-more-messages-for-current-chat]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(load-more-messages cofx (:current-chat-id db) false)) (load-more-messages cofx (:current-chat-id db) false))
(fx/defn load-messages (rf/defn load-messages
[{:keys [db now] :as cofx} chat-id] [{:keys [db now] :as cofx} chat-id]
(when-not (get-in db [:pagination-info chat-id :messages-initialized?]) (when-not (get-in db [:pagination-info chat-id :messages-initialized?])
(fx/merge cofx (rf/merge cofx
{:db (assoc-in db [:pagination-info chat-id :messages-initialized?] now) {:db (assoc-in db [:pagination-info chat-id :messages-initialized?] now)
:utils/dispatch-later [{:ms 50 :dispatch [:chat.ui/mark-all-read-pressed chat-id]} :utils/dispatch-later [{:ms 50 :dispatch [:chat.ui/mark-all-read-pressed chat-id]}
(when-not (get-in cofx [:db :chats chat-id :public?]) (when-not (get-in cofx [:db :chats chat-id :public?])
{:ms 100 :dispatch [:pin-message/load-pin-messages chat-id]})]} {:ms 100 :dispatch [:pin-message/load-pin-messages chat-id]})]}
+232 -186
View File
@@ -1,23 +1,26 @@
(ns status-im.chat.models.mentions (ns status-im.chat.models.mentions
(:require [clojure.string :as string] (:require [clojure.string :as string]
[quo.react :as react]
[quo.react-native :as rn]
[re-frame.core :as re-frame] [re-frame.core :as re-frame]
[status-im.constants :as constants] [status-im.constants :as constants]
[status-im.utils.fx :as fx]
[status-im.contact.db :as contact.db] [status-im.contact.db :as contact.db]
[status-im.utils.platform :as platform] [status-im.multiaccounts.core :as multiaccounts]
[taoensso.timbre :as log]
[status-im.utils.utils :as utils]
[status-im.native-module.core :as status] [status-im.native-module.core :as status]
[quo.react-native :as rn] [status-im.utils.platform :as platform]
[quo.react :as react] [status-im.utils.utils :as utils]
[status-im.multiaccounts.core :as multiaccounts])) [taoensso.timbre :as log]
[utils.re-frame :as rf]))
(def at-sign "@") (def at-sign "@")
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn re-pos [re s] (defn re-pos
(loop [res [] s s last-idx 0] [re s]
(loop [res []
s s
last-idx 0]
(if-let [m (.exec re s)] (if-let [m (.exec re s)]
(let [new-idx (.-index m) (let [new-idx (.-index m)
idx (+ last-idx new-idx) idx (+ last-idx new-idx)
@@ -25,7 +28,8 @@
(recur (conj res [idx c]) (subs s (inc new-idx)) (inc idx))) (recur (conj res [idx c]) (subs s (inc new-idx)) (inc idx)))
res))) res)))
(defn check-style-tag [text idxs idx] (defn check-style-tag
[text idxs idx]
(let [[pos c] (get idxs idx) (let [[pos c] (get idxs idx)
[pos2 c2] (get idxs (inc idx)) [pos2 c2] (get idxs (inc idx))
[pos3 c3] (get idxs (+ 2 idx)) [pos3 c3] (get idxs (+ 2 idx))
@@ -63,7 +67,8 @@
(doall (doall
((fnil concat []) checked new-idxs))))))) ((fnil concat []) checked new-idxs)))))))
(defn apply-style-tag [data idx pos c len start? end?] (defn apply-style-tag
[data idx pos c len start? end?]
(let [was-started? (get data c) (let [was-started? (get data c)
tripple-tilde? (and (= "~" c) (= 3 len))] tripple-tilde? (and (= "~" c) (= 3 len))]
(cond (cond
@@ -87,8 +92,9 @@
start? start?
{:data (-> data {:data (-> data
(assoc c {:len len (assoc c
:idx pos}) {:len len
:idx pos})
(clear-pending-at-signs pos)) (clear-pending-at-signs pos))
:next-idx (+ idx len)} :next-idx (+ idx len)}
@@ -96,7 +102,8 @@
{:data data {:data data
:next-idx (+ idx len)}))) :next-idx (+ idx len)})))
(defn code-tag-len [idxs idx] (defn code-tag-len
[idxs idx]
(let [[pos c] (get idxs idx) (let [[pos c] (get idxs idx)
[pos2 c2] (get idxs (inc idx)) [pos2 c2] (get idxs (inc idx))
[pos3 c3] (get idxs (+ 2 idx))] [pos3 c3] (get idxs (+ 2 idx))]
@@ -147,7 +154,8 @@
(string/blank? (subs text (first prev-newlines) (dec pos))))) (string/blank? (subs text (first prev-newlines) (dec pos)))))
(recur (-> data (recur (-> data
(dissoc :newline "*" "_" "~" "`") (dissoc :newline "*" "_" "~" "`")
(assoc ">" {:idx pos})) (inc idx)) (assoc ">" {:idx pos}))
(inc idx))
(recur data (inc idx)))) (recur data (inc idx))))
at-sign? at-sign?
@@ -155,7 +163,7 @@
(inc idx)) (inc idx))
code-tag? code-tag?
(let [len (code-tag-len idxs idx) (let [len (code-tag-len idxs idx)
{:keys [data next-idx]} {:keys [data next-idx]}
(apply-style-tag data idx pos c len true true)] (apply-style-tag data idx pos c len true true)]
(recur data next-idx)) (recur data next-idx))
@@ -167,7 +175,7 @@
(apply-style-tag data idx pos c len can-be-start? can-be-end?)] (apply-style-tag data idx pos c len can-be-start? can-be-end?)]
(recur data next-idx)) (recur data next-idx))
:else (recur data (inc idx))))))))) :else (recur data (inc idx)))))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -201,7 +209,8 @@
:ens-verified ens-verified :ens-verified ens-verified
:public-key public-key}))) :public-key public-key})))
(defn mentionable-contacts [contacts] (defn mentionable-contacts
[contacts]
(reduce (reduce
(fn [acc [key contact]] (fn [acc [key contact]]
(let [mentionable-contact (add-searchable-phrases-to-contact contact false)] (let [mentionable-contact (add-searchable-phrases-to-contact contact false)]
@@ -211,38 +220,49 @@
{} {}
contacts)) contacts))
(defn mentionable-contacts-from-identites [contacts my-public-key identities] (defn mentionable-contacts-from-identites
[contacts my-public-key identities]
(reduce (fn [acc identity] (reduce (fn [acc identity]
(let [contact (multiaccounts/contact-by-identity (let [contact (multiaccounts/contact-by-identity
contacts identity) contacts
identity)
contact (if (string/blank? (:alias contact)) contact (if (string/blank? (:alias contact))
(assoc contact :alias (assoc contact
:alias
(get-in contact [:names :three-words-name])) (get-in contact [:names :three-words-name]))
contact) contact)
mentionable-contact (add-searchable-phrases-to-contact mentionable-contact (add-searchable-phrases-to-contact
contact true)] contact
(if (nil? mentionable-contact) acc true)]
(assoc acc identity mentionable-contact)))) (if (nil? mentionable-contact)
acc
(assoc acc identity mentionable-contact))))
{} {}
(remove #(= my-public-key %) identities))) (remove #(= my-public-key %) identities)))
(defn get-mentionable-users [chat all-contacts current-multiaccount community-members] (defn get-mentionable-users
(let [{:keys [name preferred-name public-key]} current-multiaccount [chat all-contacts current-multiaccount community-members]
(let [{:keys [name preferred-name public-key]} current-multiaccount
{:keys [chat-id users contacts chat-type]} chat {:keys [chat-id users contacts chat-type]} chat
mentionable-contacts (mentionable-contacts all-contacts) mentionable-contacts (mentionable-contacts all-contacts)
mentionable-users (assoc users public-key {:alias name mentionable-users (assoc users
:name (or preferred-name name) public-key
:public-key public-key})] {:alias name
:name (or preferred-name name)
:public-key public-key})]
(cond (cond
(= chat-type constants/private-group-chat-type) (= chat-type constants/private-group-chat-type)
(merge mentionable-users (merge mentionable-users
(mentionable-contacts-from-identites all-contacts public-key contacts)) (mentionable-contacts-from-identites all-contacts public-key contacts))
(= chat-type constants/one-to-one-chat-type) (= chat-type constants/one-to-one-chat-type)
(assoc mentionable-users chat-id (get mentionable-contacts chat-id (assoc mentionable-users
(-> chat-id chat-id
contact.db/public-key->new-contact (get mentionable-contacts
contact.db/enrich-contact))) chat-id
(-> chat-id
contact.db/public-key->new-contact
contact.db/enrich-contact)))
(= chat-type constants/community-chat-type) (= chat-type constants/community-chat-type)
(mentionable-contacts-from-identites (mentionable-contacts-from-identites
@@ -253,13 +273,14 @@
(= chat-type constants/public-chat-type) (= chat-type constants/public-chat-type)
(merge mentionable-users (select-keys mentionable-contacts (keys mentionable-users))) (merge mentionable-users (select-keys mentionable-contacts (keys mentionable-users)))
:else mentionable-users))) :else mentionable-users)))
(def ending-chars "[\\s\\.,;:]") (def ending-chars "[\\s\\.,;:]")
(def ending-chars-regex (re-pattern ending-chars)) (def ending-chars-regex (re-pattern ending-chars))
(def word-regex (re-pattern (str "^[\\w\\d]*" ending-chars "|^[\\S]*$"))) (def word-regex (re-pattern (str "^[\\w\\d]*" ending-chars "|^[\\S]*$")))
(defn mentioned? [{:keys [alias name]} text] (defn mentioned?
[{:keys [alias name]} text]
(let [lcase-name (string/lower-case name) (let [lcase-name (string/lower-case name)
lcase-alias (string/lower-case alias) lcase-alias (string/lower-case alias)
regex (re-pattern regex (re-pattern
@@ -272,7 +293,8 @@
lcase-text (string/lower-case text)] lcase-text (string/lower-case text)]
(re-find regex lcase-text))) (re-find regex lcase-text)))
(defn get-user-suggestions [users searched-text] (defn get-user-suggestions
[users searched-text]
(reduce (reduce
(fn [acc [k {:keys [alias name nickname searchable-phrases] :as user}]] (fn [acc [k {:keys [alias name nickname searchable-phrases] :as user}]]
(if-let [match (if-let [match
@@ -301,9 +323,11 @@
(string/lower-case name) (string/lower-case name)
searched-text) searched-text)
name))] name))]
(assoc acc k (assoc user (assoc acc
:match match k
:searched-text searched-text)) (assoc user
:match match
:searched-text searched-text))
acc)) acc))
{} {}
users)) users))
@@ -324,21 +348,25 @@
text)) text))
user-suggestions (get-user-suggestions users searched-text) user-suggestions (get-user-suggestions users searched-text)
user-suggestions-cnt (count user-suggestions)] user-suggestions-cnt (count user-suggestions)]
(cond (zero? user-suggestions-cnt) (cond
nil (zero? user-suggestions-cnt)
nil
(and (= 1 user-suggestions-cnt) (and (= 1 user-suggestions-cnt)
(mentioned? (second (first user-suggestions)) (mentioned? (second (first user-suggestions))
(subs text (inc mention-key-idx)))) (subs text (inc mention-key-idx))))
(second (first user-suggestions)) (second (first user-suggestions))
(> user-suggestions-cnt 1) (> user-suggestions-cnt 1)
(let [word-len (count word) (let [word-len (count word)
text-len (count text) text-len (count text)
next-word-start (+ next-word-idx word-len)] next-word-start (+ next-word-idx word-len)]
(when (> text-len next-word-start) (when (> text-len next-word-start)
(match-mention text users mention-key-idx (match-mention text
next-word-start new-words)))))))) users
mention-key-idx
next-word-start
new-words))))))))
(defn replace-mentions (defn replace-mentions
([text users] ([text users]
@@ -360,18 +388,25 @@
(let [new-text (string/join (let [new-text (string/join
[(subs text 0 (inc mention-key-idx)) [(subs text 0 (inc mention-key-idx))
public-key public-key
(subs text (+ (inc mention-key-idx) (subs text
(count match)))])] (+ (inc mention-key-idx)
(recur new-text users (rest idxs) (count match)))])]
(recur new-text
users
(rest idxs)
(+ diff (- (count text) (count new-text))))))))))))) (+ diff (- (count text) (count new-text)))))))))))))
(defn check-mentions [{:keys [db]} text] (defn check-mentions
[{:keys [db]} text]
(let [current-chat-id (:current-chat-id db) (let [current-chat-id (:current-chat-id db)
chat (get-in db [:chats current-chat-id]) chat (get-in db [:chats current-chat-id])
all-contacts (:contacts/contacts db) all-contacts (:contacts/contacts db)
current-multiaccount (:multiaccount db) current-multiaccount (:multiaccount db)
community-members (get-in db [:communities (:community-id chat) :members]) community-members (get-in db [:communities (:community-id chat) :members])
mentionable-users (get-mentionable-users chat all-contacts current-multiaccount community-members)] mentionable-users (get-mentionable-users chat
all-contacts
current-multiaccount
community-members)]
(replace-mentions text mentionable-users))) (replace-mentions text mentionable-users)))
(defn get-at-sign-idxs (defn get-at-sign-idxs
@@ -396,7 +431,7 @@
{:from idx {:from idx
:checked? false}) :checked? false})
new-idxs) new-idxs)
(let [diff (- new-text-len old-text-len) (let [diff (- new-text-len old-text-len)
{:keys [state added?]} {:keys [state added?]}
(->> at-idxs (->> at-idxs
(keep (fn [{:keys [from to] :as entry}] (keep (fn [{:keys [from to] :as entry}]
@@ -406,7 +441,7 @@
(>= from old-end) (>= from old-end)
(assoc entry (assoc entry
:from (+ from diff) :from (+ from diff)
:to (+ to diff)) :to (+ to diff))
;; starts and end before change ;; starts and end before change
(and (and
@@ -431,20 +466,22 @@
(> from last-new-idx) (> from last-new-idx)
(not added?)) (not added?))
{:state (conj {:state (conj
(into state (map (fn [idx] (into state
{:from idx (map (fn [idx]
:checked? false}) {:from idx
new-idxs)) :checked? false})
new-idxs))
entry) entry)
:added? true} :added? true}
(update acc :state conj entry))) (update acc :state conj entry)))
{:state []}))] {:state []}))]
(if added? (if added?
state state
(into state (map (fn [idx] (into state
{:from idx (map (fn [idx]
:checked? false}) {:from idx
new-idxs))))))) :checked? false})
new-idxs)))))))
(defn check-entry (defn check-entry
[text {:keys [from checked?] :as entry} mentionable-users] [text {:keys [from checked?] :as entry} mentionable-users]
@@ -453,14 +490,14 @@
(let [{user-match :match} (let [{user-match :match}
(match-mention (str text "@") mentionable-users from)] (match-mention (str text "@") mentionable-users from)]
(if user-match (if user-match
{:from from {:from from
:to (+ from (count user-match)) :to (+ from (count user-match))
:checked? true :checked? true
:mention? true} :mention? true}
{:from from {:from from
:to (count text) :to (count text)
:checked? true :checked? true
:mention false})))) :mention false}))))
(defn check-idx-for-mentions (defn check-idx-for-mentions
[text idxs mentionable-users] [text idxs mentionable-users]
@@ -489,7 +526,7 @@
(assoc-in [last-idx :to] (dec (count text))) (assoc-in [last-idx :to] (dec (count text)))
(assoc-in [last-idx :checked?] false))))))) (assoc-in [last-idx :checked?] false)))))))
(fx/defn on-text-input (rf/defn on-text-input
{:events [::on-text-input]} {:events [::on-text-input]}
[{:keys [db]} {:keys [previous-text start end] :as args}] [{:keys [db]} {:keys [previous-text start end] :as args}]
(log/debug "[mentions] on-text-input args" args) (log/debug "[mentions] on-text-input args" args)
@@ -499,31 +536,34 @@
(if platform/android? (if platform/android?
previous-text previous-text
(subs previous-text start end)) (subs previous-text start end))
chat-id (:current-chat-id db) chat-id (:current-chat-id db)
previous-state (get-in db [:chats/mentions chat-id :mentions]) previous-state (get-in db [:chats/mentions chat-id :mentions])
new-state (-> previous-state new-state (-> previous-state
(merge args) (merge args)
(assoc :previous-text normalized-previous-text)) (assoc :previous-text normalized-previous-text))
new-at-idxs (calc-at-idxs new-state) new-at-idxs (calc-at-idxs new-state)
new-state (assoc new-state :at-idxs new-at-idxs)] new-state (assoc new-state :at-idxs new-at-idxs)]
(log/debug "[mentions] on-text-input state" new-state) (log/debug "[mentions] on-text-input state" new-state)
{:db (assoc-in db [:chats/mentions chat-id :mentions] new-state)})) {:db (assoc-in db [:chats/mentions chat-id :mentions] new-state)}))
(defn calculate-input [text [first-idx :as idxs]] (defn calculate-input
[text [first-idx :as idxs]]
(if-not first-idx (if-not first-idx
[[:text text]] [[:text text]]
(let [idx-cnt (count idxs) (let [idx-cnt (count idxs)
last-from (get-in idxs [(dec idx-cnt) :from])] last-from (get-in idxs [(dec idx-cnt) :from])]
(reduce (reduce
(fn [acc {:keys [from to next-at-idx mention?]}] (fn [acc {:keys [from to next-at-idx mention?]}]
(cond (cond
(and mention? next-at-idx) (and mention? next-at-idx)
(into acc [[:mention (subs text from (inc to))] (into acc
[:text (subs text (inc to) next-at-idx)]]) [[:mention (subs text from (inc to))]
[:text (subs text (inc to) next-at-idx)]])
(and mention? (= last-from from)) (and mention? (= last-from from))
(into acc [[:mention (subs text from (inc to))] (into acc
[:text (subs text (inc to))]]) [[:mention (subs text from (inc to))]
[:text (subs text (inc to))]])
:else :else
(conj acc [:text (subs text from (inc to))]))) (conj acc [:text (subs text from (inc to))])))
@@ -533,79 +573,82 @@
[[:text (subs text 0 first-from)]])) [[:text (subs text 0 first-from)]]))
idxs)))) idxs))))
(fx/defn recheck-at-idxs (rf/defn recheck-at-idxs
[{:keys [db]} mentionable-users] [{:keys [db]} mentionable-users]
(let [chat-id (:current-chat-id db) (let [chat-id (:current-chat-id db)
text (get-in db [:chat/inputs chat-id :input-text]) text (get-in db [:chat/inputs chat-id :input-text])
state (get-in db [:chats/mentions chat-id :mentions]) state (get-in db [:chats/mentions chat-id :mentions])
new-at-idxs (check-idx-for-mentions new-at-idxs (check-idx-for-mentions
text text
(:at-idxs state) (:at-idxs state)
mentionable-users) mentionable-users)
calculated-input (calculate-input text new-at-idxs)] calculated-input (calculate-input text new-at-idxs)]
(log/debug "[mentions] new-at-idxs" new-at-idxs calculated-input) (log/debug "[mentions] new-at-idxs" new-at-idxs calculated-input)
{:db (-> db {:db (-> db
(update-in (update-in
[:chats/mentions chat-id :mentions] [:chats/mentions chat-id :mentions]
assoc assoc
:at-idxs new-at-idxs) :at-idxs
new-at-idxs)
(assoc-in [:chat/inputs-with-mentions chat-id] calculated-input))})) (assoc-in [:chat/inputs-with-mentions chat-id] calculated-input))}))
(fx/defn calculate-suggestions (rf/defn calculate-suggestions
{:events [::calculate-suggestions]} {:events [::calculate-suggestions]}
[{:keys [db]} mentionable-users] [{:keys [db]} mentionable-users]
(let [chat-id (:current-chat-id db) (let [chat-id (:current-chat-id db)
text (get-in db [:chat/inputs chat-id :input-text]) text (get-in db [:chat/inputs chat-id :input-text])
{:keys [new-text at-idxs start end] :as state} {:keys [new-text at-idxs start end] :as state}
(get-in db [:chats/mentions chat-id :mentions]) (get-in db [:chats/mentions chat-id :mentions])
new-text (or new-text text)] new-text (or new-text text)]
(log/info "[mentions] calculate suggestions" (log/info "[mentions] calculate suggestions"
"state" state) "state"
state)
(if-not (seq at-idxs) (if-not (seq at-idxs)
{:db (-> db {:db (-> db
(assoc-in [:chats/mention-suggestions chat-id] nil) (assoc-in [:chats/mention-suggestions chat-id] nil)
(assoc-in [:chats/mentions chat-id :mentions :at-idxs] nil) (assoc-in [:chats/mentions chat-id :mentions :at-idxs] nil)
(assoc-in [:chat/inputs-with-mentions chat-id] [[:text text]]))} (assoc-in [:chat/inputs-with-mentions chat-id] [[:text text]]))}
(let [new-at-idxs (check-idx-for-mentions (let [new-at-idxs (check-idx-for-mentions
text text
at-idxs at-idxs
mentionable-users) mentionable-users)
calculated-input (calculate-input text new-at-idxs) calculated-input (calculate-input text new-at-idxs)
addition? (<= start end) addition? (<= start end)
end (if addition? end (if addition?
(+ start (count new-text)) (+ start (count new-text))
start) start)
at-sign-idx (string/last-index-of text at-sign start) at-sign-idx (string/last-index-of text at-sign start)
searched-text (string/lower-case (subs text (inc at-sign-idx) end)) searched-text (string/lower-case (subs text (inc at-sign-idx) end))
mentions mentions
(when (and (not (> at-sign-idx start)) (when (and (not (> at-sign-idx start))
(not (> (- end at-sign-idx) 100))) (not (> (- end at-sign-idx) 100)))
(get-user-suggestions mentionable-users searched-text))] (get-user-suggestions mentionable-users searched-text))]
(log/debug "[mentions] mention check" (log/debug "[mentions] mention check"
"addition" addition? "addition" addition?
"at-sign-idx" at-sign-idx "at-sign-idx" at-sign-idx
"start" start "start" start
"end" end "end" end
"searched-text" (pr-str searched-text) "searched-text" (pr-str searched-text)
"mentions" (count mentions)) "mentions" (count mentions))
(log/debug "[mentions] new-at-idxs" new-at-idxs calculated-input) (log/debug "[mentions] new-at-idxs" new-at-idxs calculated-input)
{:db (-> db {:db (-> db
(update-in [:chats/mentions chat-id :mentions] (update-in [:chats/mentions chat-id :mentions]
assoc assoc
:at-sign-idx at-sign-idx :at-sign-idx at-sign-idx
:at-idxs new-at-idxs :at-idxs new-at-idxs
:mention-end end) :mention-end end)
(assoc-in [:chat/inputs-with-mentions chat-id] calculated-input) (assoc-in [:chat/inputs-with-mentions chat-id] calculated-input)
(assoc-in [:chats/mention-suggestions chat-id] mentions))})))) (assoc-in [:chats/mention-suggestions chat-id] mentions))}))))
(defn new-input-text-with-mention (defn new-input-text-with-mention
[{:keys [db]} {:keys [name]}] [{:keys [db]} {:keys [name]}]
(let [chat-id (:current-chat-id db) (let [chat-id (:current-chat-id db)
text (get-in db [:chat/inputs chat-id :input-text]) text (get-in db [:chat/inputs chat-id :input-text])
{:keys [mention-end at-sign-idx]} {:keys [mention-end at-sign-idx]}
(get-in db [:chats/mentions chat-id :mentions])] (get-in db [:chats/mentions chat-id :mentions])]
(log/debug "[mentions] clear suggestions" (log/debug "[mentions] clear suggestions"
"state" new-input-text-with-mention) "state"
new-input-text-with-mention)
(string/join (string/join
[(subs text 0 (inc at-sign-idx)) [(subs text 0 (inc at-sign-idx))
name name
@@ -616,36 +659,36 @@
" ")) " "))
(subs text mention-end)]))) (subs text mention-end)])))
(fx/defn clear-suggestions (rf/defn clear-suggestions
[{:keys [db]}] [{:keys [db]}]
(log/debug "[mentions] clear suggestions") (log/debug "[mentions] clear suggestions")
(let [chat-id (:current-chat-id db)] (let [chat-id (:current-chat-id db)]
{:db (update db :chats/mention-suggestions dissoc chat-id)})) {:db (update db :chats/mention-suggestions dissoc chat-id)}))
(fx/defn clear-mentions (rf/defn clear-mentions
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(log/debug "[mentions] clear mentions") (log/debug "[mentions] clear mentions")
(let [chat-id (:current-chat-id db)] (let [chat-id (:current-chat-id db)]
(fx/merge (rf/merge
cofx cofx
{:db (-> db {:db (-> db
(update-in [:chats/mentions chat-id] dissoc :mentions) (update-in [:chats/mentions chat-id] dissoc :mentions)
(update :chat/inputs-with-mentions dissoc chat-id))} (update :chat/inputs-with-mentions dissoc chat-id))}
(clear-suggestions)))) (clear-suggestions))))
(fx/defn clear-cursor (rf/defn clear-cursor
{:events [::clear-cursor]} {:events [::clear-cursor]}
[{:keys [db]}] [{:keys [db]}]
(log/debug "[mentions] clear cursor") (log/debug "[mentions] clear cursor")
{:db {:db
(update db :chats/cursor dissoc (:current-chat-id db))}) (update db :chats/cursor dissoc (:current-chat-id db))})
(fx/defn check-selection (rf/defn check-selection
{:events [::on-selection-change]} {:events [::on-selection-change]}
[{:keys [db] :as cofx} [{:keys [db] :as cofx}
{:keys [start end] :as selection} {:keys [start end] :as selection}
mentionable-users] mentionable-users]
(let [chat-id (:current-chat-id db) (let [chat-id (:current-chat-id db)
{:keys [mention-end at-idxs]} {:keys [mention-end at-idxs]}
(get-in db [:chats/mentions chat-id :mentions])] (get-in db [:chats/mentions chat-id :mentions])]
(when (seq at-idxs) (when (seq at-idxs)
@@ -655,12 +698,13 @@
(<= (dec end) to)) (<= (dec end) to))
idx)) idx))
at-idxs) at-idxs)
(fx/merge (rf/merge
cofx cofx
{:db (update-in db [:chats/mentions chat-id :mentions] {:db (update-in db
[:chats/mentions chat-id :mentions]
assoc assoc
:start end :start end
:end end :end end
:new-text "")} :new-text "")}
(calculate-suggestions mentionable-users)) (calculate-suggestions mentionable-users))
(clear-suggestions cofx))))) (clear-suggestions cofx)))))
@@ -673,26 +717,28 @@
(rn/find-node-handle (react/current-ref ref)) (rn/find-node-handle (react/current-ref ref))
cursor)))) cursor))))
(fx/defn reset-text-input-cursor (rf/defn reset-text-input-cursor
[_ ref cursor] [_ ref cursor]
{::reset-text-input-cursor [ref cursor]}) {::reset-text-input-cursor [ref cursor]})
(defn is-valid-terminating-character? [c] (defn is-valid-terminating-character?
[c]
(case c (case c
"\t" true ; tab "\t" true ; tab
"\n" true ; newline "\n" true ; newline
"\f" true ; new page "\f" true ; new page
"\r" true ; carriage return "\r" true ; carriage return
" " true ; whitespace " " true ; whitespace
"," true "," true
"." true "." true
":" true ":" true
";" true ";" true
false)) false))
(def hex-reg #"[0-9a-f]") (def hex-reg #"[0-9a-f]")
(defn is-public-key-character? [c] (defn is-public-key-character?
[c]
(.test hex-reg c)) (.test hex-reg c))
(def mention-length 133) (def mention-length 133)
@@ -710,7 +756,7 @@
current-text current-text
current-mention current-mention
current-mention-length]} character] current-mention-length]} character]
(let [is-pk-character (is-public-key-character? character) (let [is-pk-character (is-public-key-character? character)
is-termination-character (is-valid-terminating-character? character)] is-termination-character (is-valid-terminating-character? character)]
(cond (cond
;; It's a valid mention. ;; It's a valid mention.
@@ -720,13 +766,13 @@
(and (= current-mention-length mention-length) (and (= current-mention-length mention-length)
is-termination-character) is-termination-character)
{:current-mention-length 0 {:current-mention-length 0
:current-mention "" :current-mention ""
:current-text character :current-text character
:text (cond-> text :text (cond-> text
(seq current-text) (seq current-text)
(conj [:text current-text]) (conj [:text current-text])
:always :always
(conj [:mention current-mention]))} (conj [:mention current-mention]))}
;; It's either a pk character, or the `x` in the pk ;; It's either a pk character, or the `x` in the pk
@@ -739,9 +785,9 @@
(and (= 2 current-mention-length) (and (= 2 current-mention-length)
(= "x" character))) (= "x" character)))
{:current-mention-length (inc current-mention-length) {:current-mention-length (inc current-mention-length)
:current-text current-text :current-text current-text
:current-mention (str current-mention character) :current-mention (str current-mention character)
:text text} :text text}
;; The beginning of a mention, discard the @ sign ;; The beginning of a mention, discard the @ sign
@@ -750,29 +796,29 @@
(= "@" character) (= "@" character)
{:current-mention-length 1 {:current-mention-length 1
:current-mention "" :current-mention ""
:current-text current-text :current-text current-text
:text text} :text text}
;; Not a mention character, but we were following a mention ;; Not a mention character, but we were following a mention
;; discard everything up to know an count as text ;; discard everything up to know an count as text
(and (not is-pk-character) (and (not is-pk-character)
(pos? current-mention-length)) (pos? current-mention-length))
{:current-mention-length 0 {:current-mention-length 0
:current-text (str current-text "@" current-mention character) :current-text (str current-text "@" current-mention character)
:current-mention "" :current-mention ""
:text text} :text text}
;; Just a normal text character ;; Just a normal text character
:else :else
{:current-mention-length 0 {:current-mention-length 0
:current-mention "" :current-mention ""
:current-text (str current-text character) :current-text (str current-text character)
:text text}))) :text text})))
{:current-mention-length 0 {:current-mention-length 0
:current-text "" :current-text ""
:current-mention "" :current-mention ""
:text []} :text []}
text)] text)]
;; Process any remaining mention/text ;; Process any remaining mention/text
(cond-> text (cond-> text
@@ -788,32 +834,32 @@
[m] [m]
(reduce (fn [{:keys [start end at-idxs at-sign-idx mention-end]} [t text]] (reduce (fn [{:keys [start end at-idxs at-sign-idx mention-end]} [t text]]
(if (= :mention t) (if (= :mention t)
(let [new-mention {:checked? true (let [new-mention {:checked? true
:mention? true :mention? true
:from mention-end :from mention-end
:to (+ start (count text))} :to (+ start (count text))}
has-previous? (seq at-idxs)] has-previous? (seq at-idxs)]
{:new-text (last text) {:new-text (last text)
:previous-text "" :previous-text ""
:start (+ start (count text)) :start (+ start (count text))
:end (+ end (count text)) :end (+ end (count text))
:at-idxs (cond-> at-idxs :at-idxs (cond-> at-idxs
has-previous? has-previous?
(-> pop (-> pop
(conj (assoc (peek at-idxs) :next-at-idx mention-end))) (conj (assoc (peek at-idxs) :next-at-idx mention-end)))
:always :always
(conj new-mention)) (conj new-mention))
:at-sign-idx mention-end :at-sign-idx mention-end
:mention-end (+ mention-end (count text))}) :mention-end (+ mention-end (count text))})
{:new-text (last text) {:new-text (last text)
:previous-text "" :previous-text ""
:start (+ start (count text)) :start (+ start (count text))
:end (+ end (count text)) :end (+ end (count text))
:at-idxs at-idxs :at-idxs at-idxs
:at-sign-idx at-sign-idx :at-sign-idx at-sign-idx
:mention-end (+ mention-end (count text))})) :mention-end (+ mention-end (count text))}))
{:start -1 {:start -1
:end -1 :end -1
:at-idxs [] :at-idxs []
:mention-end 0} :mention-end 0}
m)) m))
+80 -56
View File
@@ -2,7 +2,6 @@
(:require [clojure.string :as string] (:require [clojure.string :as string]
[re-frame.core :as re-frame] [re-frame.core :as re-frame]
[status-im.chat.models :as chat-model] [status-im.chat.models :as chat-model]
[status-im2.contexts.chat.messages.message.delete-message.events :as delete-message]
[status-im.chat.models.loading :as chat.loading] [status-im.chat.models.loading :as chat.loading]
[status-im.chat.models.mentions :as mentions] [status-im.chat.models.mentions :as mentions]
[status-im.chat.models.message-list :as message-list] [status-im.chat.models.message-list :as message-list]
@@ -11,11 +10,12 @@
[status-im.ethereum.json-rpc :as json-rpc] [status-im.ethereum.json-rpc :as json-rpc]
[status-im.transport.message.protocol :as protocol] [status-im.transport.message.protocol :as protocol]
[status-im.ui.screens.chat.state :as view.state] [status-im.ui.screens.chat.state :as view.state]
[status-im.utils.fx :as fx]
[status-im.utils.gfycat.core :as gfycat] [status-im.utils.gfycat.core :as gfycat]
[status-im.utils.platform :as platform] [status-im.utils.platform :as platform]
[status-im.utils.types :as types] [status-im.utils.types :as types]
[taoensso.timbre :as log])) [status-im2.contexts.chat.messages.message.delete-message.events :as delete-message]
[taoensso.timbre :as log]
[utils.re-frame :as rf]))
(defn- message-loaded? (defn- message-loaded?
[db chat-id message-id] [db chat-id message-id]
@@ -25,7 +25,8 @@
[db chat-id clock-value] [db chat-id clock-value]
(>= (get-in db [:chats chat-id :deleted-at-clock-value]) clock-value)) (>= (get-in db [:chats chat-id :deleted-at-clock-value]) clock-value))
(defn add-timeline-message [acc chat-id message-id message] (defn add-timeline-message
[acc chat-id message-id message]
(-> acc (-> acc
(update-in [:db :messages chat-id] assoc message-id message) (update-in [:db :messages chat-id] assoc message-id message)
(update-in [:db :message-lists chat-id] message-list/add message))) (update-in [:db :message-lists chat-id] message-list/add message)))
@@ -36,14 +37,16 @@
;; TODO this is too expensive, probably we could mark message somehow and just hide it in the UI ;; TODO this is too expensive, probably we could mark message somehow and just hide it in the UI
(message-list/rebuild-message-list {:db (update-in db [:messages chat-id] dissoc message-id)} chat-id)) (message-list/rebuild-message-list {:db (update-in db [:messages chat-id] dissoc message-id)} chat-id))
(fx/defn add-senders-to-chat-users (rf/defn add-senders-to-chat-users
{:events [:chat/add-senders-to-chat-users]} {:events [:chat/add-senders-to-chat-users]}
[{:keys [db]} messages] [{:keys [db]} messages]
(reduce (fn [acc {:keys [chat-id alias name identicon from]}] (reduce (fn [acc {:keys [chat-id alias name identicon from]}]
(let [alias (if (string/blank? alias) (let [alias (if (string/blank? alias)
(gfycat/generate-gfy from) (gfycat/generate-gfy from)
alias)] alias)]
(update-in acc [:db :chats chat-id :users] assoc (update-in acc
[:db :chats chat-id :users]
assoc
from from
(mentions/add-searchable-phrases (mentions/add-searchable-phrases
{:alias alias {:alias alias
@@ -54,7 +57,8 @@
{:db db} {:db db}
messages)) messages))
(defn timeline-message? [db chat-id] (defn timeline-message?
[db chat-id]
(and (and
(get-in db [:pagination-info constants/timeline-chat-id :messages-initialized?]) (get-in db [:pagination-info constants/timeline-chat-id :messages-initialized?])
(or (or
@@ -62,11 +66,13 @@
(when-let [pub-key (get-in db [:chats chat-id :profile-public-key])] (when-let [pub-key (get-in db [:chats chat-id :profile-public-key])]
(get-in db [:contacts/contacts pub-key :added]))))) (get-in db [:contacts/contacts pub-key :added])))))
(defn get-timeline-message [db chat-id message-js] (defn get-timeline-message
[db chat-id message-js]
(when (timeline-message? db chat-id) (when (timeline-message? db chat-id)
(data-store.messages/<-rpc (types/js->clj message-js)))) (data-store.messages/<-rpc (types/js->clj message-js))))
(defn add-message [{:keys [db] :as acc} message-js chat-id message-id cursor-clock-value] (defn add-message
[{:keys [db] :as acc} message-js chat-id message-id cursor-clock-value]
(let [{:keys [replace from clock-value] :as message} (let [{:keys [replace from clock-value] :as message}
(data-store.messages/<-rpc (types/js->clj message-js))] (data-store.messages/<-rpc (types/js->clj message-js))]
(if (message-loaded? db chat-id message-id) (if (message-loaded? db chat-id message-id)
@@ -82,8 +88,9 @@
(update-in [:db :message-lists chat-id] message-list/add message) (update-in [:db :message-lists chat-id] message-list/add message)
(or (not cursor-clock-value) (< clock-value cursor-clock-value)) (or (not cursor-clock-value) (< clock-value cursor-clock-value))
(update-in [:db :pagination-info chat-id] assoc (update-in [:db :pagination-info chat-id]
:cursor (chat.loading/clock-value->cursor clock-value) assoc
:cursor (chat.loading/clock-value->cursor clock-value)
:cursor-clock-value clock-value) :cursor-clock-value clock-value)
;;conj sender for add-sender-to-chat-users ;;conj sender for add-sender-to-chat-users
@@ -94,11 +101,12 @@
;;TODO this is expensive ;;TODO this is expensive
(hide-message chat-id replace))))) (hide-message chat-id replace)))))
(defn reduce-js-messages [{:keys [db] :as acc} ^js message-js] (defn reduce-js-messages
(let [chat-id (.-localChatId message-js) [{:keys [db] :as acc} ^js message-js]
clock-value (.-clock message-js) (let [chat-id (.-localChatId message-js)
message-id (.-id message-js) clock-value (.-clock message-js)
current-chat-id (:current-chat-id db) message-id (.-id message-js)
current-chat-id (:current-chat-id db)
cursor-clock-value (get-in db [:pagination-info current-chat-id :cursor-clock-value])] cursor-clock-value (get-in db [:pagination-info current-chat-id :cursor-clock-value])]
;;ignore not opened chats and earlier clock ;;ignore not opened chats and earlier clock
(if (and (get-in db [:pagination-info chat-id :messages-initialized?]) (if (and (get-in db [:pagination-info chat-id :messages-initialized?])
@@ -110,26 +118,30 @@
(add-message acc message-js chat-id message-id cursor-clock-value) (add-message acc message-js chat-id message-id cursor-clock-value)
;; Not in the current view, set all-loaded to false ;; Not in the current view, set all-loaded to false
;; and offload to db and update cursor if necessary ;; and offload to db and update cursor if necessary
;;TODO if we'll offload messages , it will conflict with end reached, so probably if we reached the end of visible area, ;;TODO if we'll offload messages , it will conflict with end reached, so probably if we reached
;; we need to drop other messages with (< clock-value cursor-clock-value) from response-js so we don't update ;;the end of visible area,
;; we need to drop other messages with (< clock-value cursor-clock-value) from response-js so we
;; don't update
;; :cursor-clock-value because it will be changed when we loadMore message ;; :cursor-clock-value because it will be changed when we loadMore message
{:db (cond-> (assoc-in db [:pagination-info chat-id :all-loaded?] false) {:db (cond-> (assoc-in db [:pagination-info chat-id :all-loaded?] false)
(> clock-value cursor-clock-value) (> clock-value cursor-clock-value)
;;TODO cut older messages from messages-list ;;TODO cut older messages from messages-list
(update-in [:pagination-info chat-id] assoc (update-in [:pagination-info chat-id]
:cursor (chat.loading/clock-value->cursor clock-value) assoc
:cursor (chat.loading/clock-value->cursor clock-value)
:cursor-clock-value clock-value))}) :cursor-clock-value clock-value))})
acc))) acc)))
(defn receive-many [{:keys [db]} ^js response-js] (defn receive-many
(let [messages-js ^js (.splice (.-messages response-js) 0 (if platform/low-device? 3 10)) [{:keys [db]} ^js response-js]
(let [messages-js ^js (.splice (.-messages response-js) 0 (if platform/low-device? 3 10))
{:keys [db senders]} {:keys [db senders]}
(reduce reduce-js-messages (reduce reduce-js-messages
{:db db :chats #{} :senders {} :transactions #{}} {:db db :chats #{} :senders {} :transactions #{}}
messages-js)] messages-js)]
;;we want to render new messages as soon as possible ;;we want to render new messages as soon as possible
;;so we dispatch later all other events which can be handled async ;;so we dispatch later all other events which can be handled async
{:db db {:db db
:utils/dispatch-later :utils/dispatch-later
(concat [{:ms 20 :dispatch [:process-response response-js]}] (concat [{:ms 20 :dispatch [:process-response response-js]}]
(when (and (:current-chat-id db) (= "active" (:app-state db))) (when (and (:current-chat-id db) (= "active" (:app-state db)))
@@ -137,7 +149,8 @@
(when (seq senders) (when (seq senders)
[{:ms 100 :dispatch [:chat/add-senders-to-chat-users (vals senders)]}]))})) [{:ms 100 :dispatch [:chat/add-senders-to-chat-users (vals senders)]}]))}))
(defn reduce-js-statuses [db ^js message-js] (defn reduce-js-statuses
[db ^js message-js]
(let [chat-id (.-localChatId message-js) (let [chat-id (.-localChatId message-js)
profile-initialized (get-in db [:pagination-info chat-id :messages-initialized?]) profile-initialized (get-in db [:pagination-info chat-id :messages-initialized?])
timeline-message (timeline-message? db chat-id) timeline-message (timeline-message? db chat-id)
@@ -155,60 +168,66 @@
(update-in [:message-lists constants/timeline-chat-id] message-list/add message))) (update-in [:message-lists constants/timeline-chat-id] message-list/add message)))
db))) db)))
(fx/defn process-statuses (rf/defn process-statuses
{:events [:process-statuses]} {:events [:process-statuses]}
[{:keys [db]} statuses] [{:keys [db]} statuses]
{:db (reduce reduce-js-statuses db statuses)}) {:db (reduce reduce-js-statuses db statuses)})
(fx/defn update-db-message-status (rf/defn update-db-message-status
[{:keys [db] :as cofx} chat-id message-id status] [{:keys [db] :as cofx} chat-id message-id status]
(when (get-in db [:messages chat-id message-id]) (when (get-in db [:messages chat-id message-id])
(fx/merge cofx (rf/merge cofx
{:db (assoc-in db {:db (assoc-in db
[:messages chat-id message-id :outgoing-status] [:messages chat-id message-id :outgoing-status]
status)}))) status)})))
(fx/defn update-message-status (rf/defn update-message-status
[{:keys [db] :as cofx} chat-id message-id status] [{:keys [db] :as cofx} chat-id message-id status]
(fx/merge cofx (rf/merge cofx
(update-db-message-status chat-id message-id status))) (update-db-message-status chat-id message-id status)))
(fx/defn resend-message (rf/defn resend-message
[{:keys [db] :as cofx} chat-id message-id] [{:keys [db] :as cofx} chat-id message-id]
(fx/merge cofx (rf/merge cofx
{::json-rpc/call [{:method "wakuext_reSendChatMessage" {::json-rpc/call [{:method "wakuext_reSendChatMessage"
:params [message-id] :params [message-id]
:on-success #(log/debug "re-sent message successfully") :on-success #(log/debug "re-sent message successfully")
:on-error #(log/error "failed to re-send message" %)}]} :on-error #(log/error "failed to re-send message" %)}]}
(update-message-status chat-id message-id :sending))) (update-message-status chat-id message-id :sending)))
(fx/defn send-message (rf/defn send-message
[cofx message] [cofx message]
(protocol/send-chat-messages cofx [message])) (protocol/send-chat-messages cofx [message]))
(fx/defn send-messages (rf/defn send-messages
[cofx messages] [cofx messages]
(protocol/send-chat-messages cofx messages)) (protocol/send-chat-messages cofx messages))
(fx/defn handle-removed-messages (rf/defn handle-removed-messages
{:events [::handle-removed-messages]} {:events [::handle-removed-messages]}
[{:keys [db] :as cofx} removed-messages] [{:keys [db] :as cofx} removed-messages]
(let [mark-as-deleted-fx (->> removed-messages (let [mark-as-deleted-fx (->> removed-messages
(map #(assoc % :message-id (:messageId %))) (map #(assoc % :message-id (:messageId %)))
(group-by :chatId) (group-by :chatId)
(mapv (fn [[chat-id messages]] (delete-message/delete-messages-localy messages chat-id)))) (mapv (fn [[chat-id messages]]
mark-as-seen-fx (mapv (fn [removed-message] (delete-message/delete-messages-localy messages chat-id))))
(let [chat-id (:chatId removed-message) mark-as-seen-fx (mapv
message-id (:messageId removed-message)] (fn [removed-message]
(data-store.messages/mark-messages-seen chat-id (let [chat-id (:chatId removed-message)
[message-id] message-id (:messageId removed-message)]
#(re-frame/dispatch [:chat/decrease-unviewed-count chat-id %3])))) (data-store.messages/mark-messages-seen chat-id
removed-messages) [message-id]
#(re-frame/dispatch
[:chat/decrease-unviewed-count
chat-id %3]))))
removed-messages)
remove-messages-fx (fn [{:keys [db]}] remove-messages-fx (fn [{:keys [db]}]
{:dispatch [:activity-center.notifications/fetch-unread-count]})] {:dispatch [:activity-center.notifications/fetch-unread-count]})]
(apply fx/merge cofx (-> mark-as-deleted-fx (apply rf/merge
(concat mark-as-seen-fx) cofx
(conj remove-messages-fx))))) (-> mark-as-deleted-fx
(concat mark-as-seen-fx)
(conj remove-messages-fx)))))
(comment (comment
(handle-removed-messages (handle-removed-messages
@@ -218,15 +237,20 @@
:m4 {:chat-id :c2 :message-id :m4}}}}} :m4 {:chat-id :c2 :message-id :m4}}}}}
[:m1 :m3])) [:m1 :m3]))
(defn remove-cleared-message [messages cleared-at] (defn remove-cleared-message
(into {} (remove #(let [message-clock (:clock-value (second %))] [messages cleared-at]
(<= message-clock cleared-at)) (into {}
messages))) (remove #(let [message-clock (:clock-value (second %))]
(<= message-clock cleared-at))
messages)))
(fx/defn handle-cleared-histories-messages (rf/defn handle-cleared-histories-messages
{:events [::handle-cleared-hisotories-messages]} {:events [::handle-cleared-hisotories-messages]}
[{:keys [db]} cleared-histories] [{:keys [db]} cleared-histories]
{:db (reduce (fn [acc current] {:db (reduce (fn [acc current]
(update-in acc [:messages (:chatId current)] remove-cleared-message (:clearedAt current))) (update-in acc
[:messages (:chatId current)]
remove-cleared-message
(:clearedAt current)))
db db
cleared-histories)}) cleared-histories)})
+44 -33
View File
@@ -2,22 +2,25 @@
(:require ["functional-red-black-tree" :as rb-tree] (:require ["functional-red-black-tree" :as rb-tree]
[status-im.constants :as constants] [status-im.constants :as constants]
[status-im.utils.datetime :as time] [status-im.utils.datetime :as time]
[status-im.utils.fx :as fx])) [utils.re-frame :as rf]))
(defn- add-datemark [{:keys [whisper-timestamp] :as msg}] (defn- add-datemark
[{:keys [whisper-timestamp] :as msg}]
;;TODO this is slow ;;TODO this is slow
(assoc msg :datemark (time/day-relative whisper-timestamp))) (assoc msg :datemark (time/day-relative whisper-timestamp)))
(defn- add-timestamp [{:keys [whisper-timestamp] :as msg}] (defn- add-timestamp
[{:keys [whisper-timestamp] :as msg}]
(assoc msg :timestamp-str (time/timestamp->time whisper-timestamp))) (assoc msg :timestamp-str (time/timestamp->time whisper-timestamp)))
(defn prepare-message [{:keys [message-id (defn prepare-message
clock-value [{:keys [message-id
message-type clock-value
from message-type
outgoing from
whisper-timestamp outgoing
deleted-for-me?]}] whisper-timestamp
deleted-for-me?]}]
(-> {:whisper-timestamp whisper-timestamp (-> {:whisper-timestamp whisper-timestamp
:from from :from from
:one-to-one? (= constants/message-type-one-to-one message-type) :one-to-one? (= constants/message-type-one-to-one message-type)
@@ -69,7 +72,8 @@
the most recent message, similarly :first-in-group? is the most recent message the most recent message, similarly :first-in-group? is the most recent message
in a group." in a group."
[{:keys [system-message? [{:keys [system-message?
one-to-one? outgoing] :as current-message} one-to-one? outgoing]
:as current-message}
{:keys [outgoing-seen?] :as previous-message} {:keys [outgoing-seen?] :as previous-message}
next-message] next-message]
(let [last-in-group? (or (nil? next-message) (let [last-in-group? (or (nil? next-message)
@@ -87,7 +91,7 @@
(not system-message?) (not system-message?)
(not outgoing) (not outgoing)
(not one-to-one?)) (not one-to-one?))
:display-photo? (display-photo? current-message)))) :display-photo? (display-photo? current-message))))
(defn update-next-message (defn update-next-message
"Update next message in the list, we set :first? to false, and check if it "Update next message in the list, we set :first? to false, and check if it
@@ -95,26 +99,28 @@
[current-message next-message] [current-message next-message]
(assoc (assoc
next-message next-message
:first? false :first? false
:first-outgoing? (and :first-outgoing? (and
(not (:first-outgoing? current-message)) (not (:first-outgoing? current-message))
(:first-outgoing? next-message)) (:first-outgoing? next-message))
:outgoing-seen? (:outgoing-seen? current-message) :outgoing-seen? (:outgoing-seen? current-message)
:first-in-group? :first-in-group?
(not (same-group? current-message next-message)))) (not (same-group? current-message next-message))))
(defn update-previous-message (defn update-previous-message
"If this is a new group, we mark the previous as the last one in the group" "If this is a new group, we mark the previous as the last one in the group"
[current-message {:keys [one-to-one? [current-message
system-message? {:keys [one-to-one?
outgoing] :as previous-message}] system-message?
outgoing]
:as previous-message}]
(let [last-in-group? (not (same-group? current-message previous-message))] (let [last-in-group? (not (same-group? current-message previous-message))]
(assoc previous-message (assoc previous-message
:display-username? (and last-in-group? :display-username? (and last-in-group?
(not system-message?) (not system-message?)
(not outgoing) (not outgoing)
(not one-to-one?)) (not one-to-one?))
:last-in-group? last-in-group?))) :last-in-group? last-in-group?)))
(defn get-prev-element (defn get-prev-element
"Get previous item in the iterator, and wind it back to the initial state" "Get previous item in the iterator, and wind it back to the initial state"
@@ -135,11 +141,11 @@
(defn update-message (defn update-message
"Update the message and siblings with positional info" "Update the message and siblings with positional info"
[^js tree message] [^js tree message]
(let [^js iter (.find tree message) (let [^js iter (.find tree message)
^js previous-message (when (.-hasPrev iter) ^js previous-message (when (.-hasPrev iter)
(get-prev-element iter)) (get-prev-element iter))
^js next-message (when (.-hasNext iter) ^js next-message (when (.-hasNext iter)
(get-next-element iter)) (get-next-element iter))
^js message-with-pos-data (add-group-info message previous-message next-message)] ^js message-with-pos-data (add-group-info message previous-message next-message)]
(cond-> (.update iter message-with-pos-data) (cond-> (.update iter message-with-pos-data)
next-message next-message
@@ -157,9 +163,9 @@
(let [iter (.find tree prepared-message)] (let [iter (.find tree prepared-message)]
(if (not iter) (if (not iter)
tree tree
(let [^js new-tree (.remove iter) (let [^js new-tree (.remove iter)
^js next-message (when (.-hasNext iter) ^js next-message (when (.-hasNext iter)
(get-next-element iter))] (get-next-element iter))]
(if (not next-message) (if (not next-message)
new-tree new-tree
(update-message new-tree next-message)))))) (update-message new-tree next-message))))))
@@ -173,21 +179,26 @@
(let [^js tree (.insert old-message-list prepared-message prepared-message)] (let [^js tree (.insert old-message-list prepared-message prepared-message)]
(update-message tree prepared-message))) (update-message tree prepared-message)))
(defn add [message-list message] (defn add
[message-list message]
(insert-message (or message-list (rb-tree compare-fn)) (prepare-message message))) (insert-message (or message-list (rb-tree compare-fn)) (prepare-message message)))
(defn add-many [message-list messages] (defn add-many
[message-list messages]
(reduce add (reduce add
message-list message-list
messages)) messages))
(defn ->seq [^js message-list] (defn ->seq
[^js message-list]
(if message-list (if message-list
(array-seq (.-values message-list)) (array-seq (.-values message-list))
[])) []))
;; NOTE(performance): this is too expensive, probably we could mark message somehow and just hide it in the UI ;; NOTE(performance): this is too expensive, probably we could mark message somehow and just hide it in
(fx/defn rebuild-message-list ;; the UI
(rf/defn rebuild-message-list
[{:keys [db]} chat-id] [{:keys [db]} chat-id]
{:db (assoc-in db [:message-lists chat-id] {:db (assoc-in db
(add-many nil (vals (get-in db [:messages chat-id]))))}) [:message-lists chat-id]
(add-many nil (vals (get-in db [:messages chat-id]))))})
+33 -22
View File
@@ -1,28 +1,36 @@
(ns status-im.chat.models.reactions (ns status-im.chat.models.reactions
(:require [status-im.constants :as constants] (:require [re-frame.core :as re-frame]
[re-frame.core :as re-frame] [status-im.constants :as constants]
[status-im.utils.fx :as fx] [status-im.data-store.reactions :as data-store.reactions]
[taoensso.timbre :as log]
[status-im.transport.message.protocol :as message.protocol] [status-im.transport.message.protocol :as message.protocol]
[status-im.data-store.reactions :as data-store.reactions])) [taoensso.timbre :as log]
[utils.re-frame :as rf]))
(defn update-reaction [acc retracted chat-id message-id emoji-id emoji-reaction-id reaction] (defn update-reaction
[acc retracted chat-id message-id emoji-id emoji-reaction-id reaction]
;; NOTE(Ferossgp): For a better performance, better to not keep in db all retracted reactions ;; NOTE(Ferossgp): For a better performance, better to not keep in db all retracted reactions
;; retraction will always come after the reaction so there shouldn't be a conflict ;; retraction will always come after the reaction so there shouldn't be a conflict
(if retracted (if retracted
(update-in acc [chat-id message-id emoji-id] dissoc emoji-reaction-id) (update-in acc [chat-id message-id emoji-id] dissoc emoji-reaction-id)
(assoc-in acc [chat-id message-id emoji-id emoji-reaction-id] reaction))) (assoc-in acc [chat-id message-id emoji-id emoji-reaction-id] reaction)))
(defn process-reactions [chats] (defn process-reactions
[chats]
(fn [reactions new-reactions] (fn [reactions new-reactions]
;; TODO(Ferossgp): handling own reaction in subscription could be expensive, ;; TODO(Ferossgp): handling own reaction in subscription could be expensive,
;; for better performance we can here separate own reaction into 2 maps ;; for better performance we can here separate own reaction into 2 maps
(reduce (reduce
(fn [acc {:keys [chat-id message-id emoji-id emoji-reaction-id retracted] (fn [acc
:as reaction}] {:keys [chat-id message-id emoji-id emoji-reaction-id retracted]
:as reaction}]
(cond-> (update-reaction acc retracted chat-id message-id emoji-id emoji-reaction-id reaction) (cond-> (update-reaction acc retracted chat-id message-id emoji-id emoji-reaction-id reaction)
(get-in chats [chat-id :profile-public-key]) (get-in chats [chat-id :profile-public-key])
(update-reaction retracted constants/timeline-chat-id message-id emoji-id emoji-reaction-id reaction))) (update-reaction retracted
constants/timeline-chat-id
message-id
emoji-id
emoji-reaction-id
reaction)))
reactions reactions
new-reactions))) new-reactions)))
@@ -32,12 +40,12 @@
(get-in db [:chats chat-id])] (get-in db [:chats chat-id])]
(>= deleted-at-clock-value clock-value))) (>= deleted-at-clock-value clock-value)))
(fx/defn receive-signal (rf/defn receive-signal
[{:keys [db] :as cofx} reactions] [{:keys [db] :as cofx} reactions]
(let [reactions (filter (partial earlier-than-deleted-at? cofx) reactions)] (let [reactions (filter (partial earlier-than-deleted-at? cofx) reactions)]
{:db (update db :reactions (process-reactions (:chats db)) reactions)})) {:db (update db :reactions (process-reactions (:chats db)) reactions)}))
(fx/defn load-more-reactions (rf/defn load-more-reactions
{:events [:load-more-reactions]} {:events [:load-more-reactions]}
[{:keys [db]} cursor chat-id] [{:keys [db]} cursor chat-id]
(when-let [session-id (get-in db [:pagination-info chat-id :messages-initialized?])] (when-let [session-id (get-in db [:pagination-info chat-id :messages-initialized?])]
@@ -48,7 +56,7 @@
#(re-frame/dispatch [::reactions-loaded chat-id session-id %]) #(re-frame/dispatch [::reactions-loaded chat-id session-id %])
#(log/error "failed loading reactions" chat-id %)))) #(log/error "failed loading reactions" chat-id %))))
(fx/defn reactions-loaded (rf/defn reactions-loaded
{:events [::reactions-loaded]} {:events [::reactions-loaded]}
[{db :db} [{db :db}
chat-id chat-id
@@ -64,33 +72,36 @@
;; Send reactions ;; Send reactions
(fx/defn send-emoji-reaction (rf/defn send-emoji-reaction
{:events [::send-emoji-reaction]} {:events [::send-emoji-reaction]}
[{{:keys [current-chat-id]} :db :as cofx} reaction] [{{:keys [current-chat-id]} :db :as cofx} reaction]
(message.protocol/send-reaction cofx (message.protocol/send-reaction cofx
(update reaction :chat-id #(or % current-chat-id)))) (update reaction :chat-id #(or % current-chat-id))))
(fx/defn send-retract-emoji-reaction (rf/defn send-retract-emoji-reaction
{:events [::send-emoji-reaction-retraction]} {:events [::send-emoji-reaction-retraction]}
[{{:keys [current-chat-id]} :db :as cofx} reaction] [{{:keys [current-chat-id]} :db :as cofx} reaction]
(message.protocol/send-retract-reaction cofx (message.protocol/send-retract-reaction cofx
(update reaction :chat-id #(or % current-chat-id)))) (update reaction :chat-id #(or % current-chat-id))))
(fx/defn receive-one (rf/defn receive-one
{:events [::receive-one]} {:events [::receive-one]}
[{:keys [db]} reaction] [{:keys [db]} reaction]
{:db (update db :reactions (process-reactions (:chats db)) [reaction])}) {:db (update db :reactions (process-reactions (:chats db)) [reaction])})
(defn message-reactions [current-public-key reactions] (defn message-reactions
[current-public-key reactions]
(reduce (reduce
(fn [acc [emoji-id reactions]] (fn [acc [emoji-id reactions]]
(if (pos? (count reactions)) (if (pos? (count reactions))
(let [own (first (filter (fn [[_ {:keys [from]}]] (let [own (first (filter (fn [[_ {:keys [from]}]]
(= from current-public-key)) reactions))] (= from current-public-key))
(conj acc {:emoji-id emoji-id reactions))]
:own (boolean (seq own)) (conj acc
:emoji-reaction-id (:emoji-reaction-id (second own)) {:emoji-id emoji-id
:quantity (count reactions)})) :own (boolean (seq own))
:emoji-reaction-id (:emoji-reaction-id (second own))
:quantity (count reactions)}))
acc)) acc))
[] []
reactions)) reactions))
+5 -5
View File
@@ -1,14 +1,14 @@
;;this ns is needed because of cycled deps ;;this ns is needed because of cycled deps
(ns status-im.chat.models.transport (ns status-im.chat.models.transport
(:require [status-im.utils.fx :as fx] (:require [status-im.chat.models.message :as chat.message]
[status-im.transport.message.core :as transport.message] [status-im.transport.message.core :as transport.message]
[status-im.chat.models.message :as chat.message])) [utils.re-frame :as rf]))
(fx/defn chat-ui-resend-message (rf/defn chat-ui-resend-message
{:events [:chat.ui/resend-message]} {:events [:chat.ui/resend-message]}
[{:keys [db] :as cofx} chat-id message-id] [{:keys [db] :as cofx} chat-id message-id]
(let [message (get-in db [:messages chat-id message-id])] (let [message (get-in db [:messages chat-id message-id])]
(fx/merge (rf/merge
cofx cofx
(transport.message/set-message-envelope-hash chat-id message-id (:message-type message)) (transport.message/set-message-envelope-hash chat-id message-id (:message-type message))
(chat.message/resend-message chat-id message-id)))) (chat.message/resend-message chat-id message-id))))
+20 -20
View File
@@ -2,44 +2,44 @@
(:require [re-frame.core :as re-frame] (:require [re-frame.core :as re-frame]
[status-im.ethereum.core :as ethereum] [status-im.ethereum.core :as ethereum]
[status-im.ethereum.json-rpc :as json-rpc] [status-im.ethereum.json-rpc :as json-rpc]
[status-im.utils.fx :as fx])) [utils.re-frame :as rf]))
(fx/defn handle-prepare-accept-request-address-for-transaction (rf/defn handle-prepare-accept-request-address-for-transaction
{:events [::prepare-accept-request-address-for-transaction]} {:events [::prepare-accept-request-address-for-transaction]}
[{:keys [db]} message] [{:keys [db]} message]
{:db (assoc db {:db (assoc db
:commands/select-account :commands/select-account
{:message message {:message message
:from (ethereum/get-default-account (:multiaccount/accounts db))}) :from (ethereum/get-default-account (:multiaccount/accounts db))})
:show-select-acc-sheet nil}) :show-select-acc-sheet nil})
(fx/defn set-selected-account (rf/defn set-selected-account
{:events [::set-selected-account]} {:events [::set-selected-account]}
[{:keys [db]} _ account] [{:keys [db]} _ account]
{:db (-> (assoc-in db [:commands/select-account :from] account) {:db (-> (assoc-in db [:commands/select-account :from] account)
(assoc :bottom-sheet/show? false))}) (assoc :bottom-sheet/show? false))})
(fx/defn handle-accept-request-address-for-transaction (rf/defn handle-accept-request-address-for-transaction
{:events [::accept-request-address-for-transaction]} {:events [::accept-request-address-for-transaction]}
[{:keys [db]} message-id address] [{:keys [db]} message-id address]
{:db (dissoc db :commands/select-account) {:db (dissoc db :commands/select-account)
::json-rpc/call [{:method "wakuext_acceptRequestAddressForTransaction" ::json-rpc/call [{:method "wakuext_acceptRequestAddressForTransaction"
:params [message-id address] :params [message-id address]
:js-response true :js-response true
:on-success #(re-frame/dispatch [:transport/message-sent %])}]}) :on-success #(re-frame/dispatch [:transport/message-sent %])}]})
(fx/defn handle-decline-request-address-for-transaction (rf/defn handle-decline-request-address-for-transaction
{:events [::decline-request-address-for-transaction]} {:events [::decline-request-address-for-transaction]}
[_ message-id] [_ message-id]
{::json-rpc/call [{:method "wakuext_declineRequestAddressForTransaction" {::json-rpc/call [{:method "wakuext_declineRequestAddressForTransaction"
:params [message-id] :params [message-id]
:js-response true :js-response true
:on-success #(re-frame/dispatch [:transport/message-sent %])}]}) :on-success #(re-frame/dispatch [:transport/message-sent %])}]})
(fx/defn handle-decline-request-transaction (rf/defn handle-decline-request-transaction
{:events [::decline-request-transaction]} {:events [::decline-request-transaction]}
[cofx message-id] [cofx message-id]
{::json-rpc/call [{:method "wakuext_declineRequestTransaction" {::json-rpc/call [{:method "wakuext_declineRequestTransaction"
:params [message-id] :params [message-id]
:js-response true :js-response true
:on-success #(re-frame/dispatch [:transport/message-sent %])}]}) :on-success #(re-frame/dispatch [:transport/message-sent %])}]})
+201 -155
View File
@@ -1,25 +1,24 @@
(ns status-im.communities.core (ns status-im.communities.core
(:require (:require [clojure.set :as clojure.set]
[re-frame.core :as re-frame] [clojure.string :as string]
[clojure.walk :as walk] [clojure.walk :as walk]
[clojure.string :as string] [quo.design-system.colors :as colors]
[clojure.set :as clojure.set] [re-frame.core :as re-frame]
[taoensso.timbre :as log] [status-im.async-storage.core :as async-storage]
[status-im.async-storage.core :as async-storage] [status-im.bottom-sheet.core :as bottom-sheet]
[status-im.utils.fx :as fx] [status-im.constants :as constants]
[status-im.constants :as constants] [status-im.ethereum.json-rpc :as json-rpc]
[status-im.bottom-sheet.core :as bottom-sheet] [status-im.ui.components.emoji-thumbnail.styles :as emoji-thumbnail-styles]
[status-im.utils.universal-links.core :as universal-links] [status-im.utils.universal-links.core :as universal-links]
[status-im.ethereum.json-rpc :as json-rpc] [status-im2.contexts.activity-center.events :as activity-center]
[quo.design-system.colors :as colors] [status-im2.navigation.events :as navigation]
[status-im2.navigation.events :as navigation] [taoensso.timbre :as log]
[status-im.utils.handlers :refer [>evt]] [utils.re-frame :as rf]))
[status-im.ui.components.emoji-thumbnail.styles :as emoji-thumbnail-styles]
[status-im2.contexts.activity-center.events :as activity-center]))
(def crop-size 1000) (def crop-size 1000)
(defn universal-link [community-id] (defn universal-link
[community-id]
(str (:external universal-links/domains) (str (:external universal-links/domains)
"/c/" "/c/"
community-id)) community-id))
@@ -28,18 +27,22 @@
[{:name "Status" [{:name "Status"
:id constants/status-community-id}]) :id constants/status-community-id}])
(defn <-request-to-join-community-rpc [r] (defn <-request-to-join-community-rpc
(clojure.set/rename-keys r {:communityId :community-id [r]
:publicKey :public-key (clojure.set/rename-keys r
:chatId :chat-id})) {:communityId :community-id
:publicKey :public-key
:chatId :chat-id}))
(defn <-requests-to-join-community-rpc [requests] (defn <-requests-to-join-community-rpc
[requests]
(reduce (fn [acc r] (reduce (fn [acc r]
(assoc acc (:id r) (<-request-to-join-community-rpc r))) (assoc acc (:id r) (<-request-to-join-community-rpc r)))
{} {}
requests)) requests))
(defn <-chats-rpc [chats] (defn <-chats-rpc
[chats]
(reduce-kv (fn [acc k v] (reduce-kv (fn [acc k v]
(assoc acc (assoc acc
(name k) (name k)
@@ -50,7 +53,8 @@
{} {}
chats)) chats))
(defn <-categories-rpc [categ] (defn <-categories-rpc
[categ]
(reduce-kv (fn [acc k v] (reduce-kv (fn [acc k v]
(assoc acc (assoc acc
(name k) (name k)
@@ -58,7 +62,8 @@
{} {}
categ)) categ))
(defn <-rpc [c] (defn <-rpc
[c]
(-> c (-> c
(clojure.set/rename-keys {:canRequestAccess :can-request-access? (clojure.set/rename-keys {:canRequestAccess :can-request-access?
:canManageUsers :can-manage-users? :canManageUsers :can-manage-users?
@@ -70,25 +75,28 @@
(update :chats <-chats-rpc) (update :chats <-chats-rpc)
(update :categories <-categories-rpc))) (update :categories <-categories-rpc)))
(defn fetch-community-id-input [{:keys [db]}] (defn fetch-community-id-input
[{:keys [db]}]
(:communities/community-id-input db)) (:communities/community-id-input db))
(fx/defn handle-request-to-join [{:keys [db]} r] (rf/defn handle-request-to-join
[{:keys [db]} r]
(let [{:keys [id community-id] :as request} (<-request-to-join-community-rpc r)] (let [{:keys [id community-id] :as request} (<-request-to-join-community-rpc r)]
{:db (assoc-in db [:communities/requests-to-join community-id id] request)})) {:db (assoc-in db [:communities/requests-to-join community-id id] request)}))
(fx/defn handle-removed-chats [{:keys [db]} chat-ids] (rf/defn handle-removed-chats
[{:keys [db]} chat-ids]
{:db (reduce (fn [db chat-id] {:db (reduce (fn [db chat-id]
(update db :chats dissoc chat-id)) (update db :chats dissoc chat-id))
db db
chat-ids)}) chat-ids)})
(fx/defn handle-community (rf/defn handle-community
[{:keys [db]} {:keys [id] :as community}] [{:keys [db]} {:keys [id] :as community}]
(when id (when id
{:db (assoc-in db [:communities id] (<-rpc community))})) {:db (assoc-in db [:communities id] (<-rpc community))}))
(fx/defn handle-communities (rf/defn handle-communities
{:events [::fetched]} {:events [::fetched]}
[{:keys [db]} communities] [{:keys [db]} communities]
{:db (reduce (fn [db {:keys [id] :as community}] {:db (reduce (fn [db {:keys [id] :as community}]
@@ -96,34 +104,36 @@
db db
communities)}) communities)})
(fx/defn handle-response [_ response-js] (rf/defn handle-response
[_ response-js]
{:dispatch [:sanitize-messages-and-process-response response-js]}) {:dispatch [:sanitize-messages-and-process-response response-js]})
(fx/defn left (rf/defn left
{:events [::left]} {:events [::left]}
[cofx response-js] [cofx response-js]
(fx/merge cofx (rf/merge cofx
(handle-response cofx response-js) (handle-response cofx response-js)
(navigation/pop-to-root-tab :chat-stack) (navigation/pop-to-root-tab :chat-stack)
(activity-center/notifications-fetch-unread-count))) (activity-center/notifications-fetch-unread-count)))
(fx/defn joined (rf/defn joined
{:events [::joined ::requested-to-join]} {:events [::joined ::requested-to-join]}
[cofx response-js] [cofx response-js]
(handle-response cofx response-js)) (handle-response cofx response-js))
(fx/defn export (rf/defn export
{:events [::export-pressed]} {:events [::export-pressed]}
[cofx community-id] [cofx community-id]
{::json-rpc/call [{:method "wakuext_exportCommunity" {::json-rpc/call [{:method "wakuext_exportCommunity"
:params [community-id] :params [community-id]
:on-success #(re-frame/dispatch [:show-popover {:view :export-community :on-success #(re-frame/dispatch [:show-popover
:community-key %}]) {:view :export-community
:community-key %}])
:on-error #(do :on-error #(do
(log/error "failed to export community" community-id %) (log/error "failed to export community" community-id %)
(re-frame/dispatch [::failed-to-export %]))}]}) (re-frame/dispatch [::failed-to-export %]))}]})
(fx/defn import-community (rf/defn import-community
{:events [::import]} {:events [::import]}
[cofx community-key] [cofx community-key]
{::json-rpc/call [{:method "wakuext_importCommunity" {::json-rpc/call [{:method "wakuext_importCommunity"
@@ -134,7 +144,7 @@
(log/error "failed to import community" %) (log/error "failed to import community" %)
(re-frame/dispatch [::failed-to-import %]))}]}) (re-frame/dispatch [::failed-to-import %]))}]})
(fx/defn join (rf/defn join
{:events [:communities/join]} {:events [:communities/join]}
[cofx community-id] [cofx community-id]
{::json-rpc/call [{:method "wakuext_joinCommunity" {::json-rpc/call [{:method "wakuext_joinCommunity"
@@ -145,7 +155,7 @@
(log/error "failed to join community" community-id %) (log/error "failed to join community" community-id %)
(re-frame/dispatch [::failed-to-join %]))}]}) (re-frame/dispatch [::failed-to-join %]))}]})
(fx/defn request-to-join (rf/defn request-to-join
{:events [::request-to-join]} {:events [::request-to-join]}
[cofx community-id] [cofx community-id]
{::json-rpc/call [{:method "wakuext_requestToJoinCommunity" {::json-rpc/call [{:method "wakuext_requestToJoinCommunity"
@@ -156,7 +166,7 @@
(log/error "failed to request to join community" community-id %) (log/error "failed to request to join community" community-id %)
(re-frame/dispatch [::failed-to-request-to-join %]))}]}) (re-frame/dispatch [::failed-to-request-to-join %]))}]})
(fx/defn leave (rf/defn leave
{:events [:communities/leave]} {:events [:communities/leave]}
[{:keys [db]} community-id] [{:keys [db]} community-id]
(let [community-chat-ids (map #(str community-id %) (let [community-chat-ids (map #(str community-id %)
@@ -168,10 +178,13 @@
:js-response true :js-response true
:on-success #(re-frame/dispatch [::left %]) :on-success #(re-frame/dispatch [::left %])
:on-error #(do :on-error #(do
(log/error "failed to leave community" community-id %) (log/error "failed to leave community"
community-id
%)
(re-frame/dispatch [::failed-to-leave %]))}]})) (re-frame/dispatch [::failed-to-leave %]))}]}))
(fx/defn fetch [_] (rf/defn fetch
[_]
{::json-rpc/call [{:method "wakuext_communities" {::json-rpc/call [{:method "wakuext_communities"
:params [] :params []
:on-success #(re-frame/dispatch [::fetched %]) :on-success #(re-frame/dispatch [::fetched %])
@@ -179,7 +192,7 @@
(log/error "failed to fetch communities" %) (log/error "failed to fetch communities" %)
(re-frame/dispatch [::failed-to-fetch %]))}]}) (re-frame/dispatch [::failed-to-fetch %]))}]})
(fx/defn chat-created (rf/defn chat-created
{:events [::chat-created]} {:events [::chat-created]}
[_ community-id user-pk] [_ community-id user-pk]
{::json-rpc/call [{:method "wakuext_sendChatMessage" {::json-rpc/call [{:method "wakuext_sendChatMessage"
@@ -193,7 +206,7 @@
:on-error :on-error
#(log/error "failed to send a message" %)}]}) #(log/error "failed to send a message" %)}]})
(fx/defn invite-users (rf/defn invite-users
{:events [::invite-people-confirmation-pressed]} {:events [::invite-people-confirmation-pressed]}
[cofx user-pk contacts] [cofx user-pk contacts]
(let [community-id (fetch-community-id-input cofx) (let [community-id (fetch-community-id-input cofx)
@@ -209,7 +222,7 @@
:on-error #(do :on-error #(do
(log/error "failed to invite-user community" %) (log/error "failed to invite-user community" %)
(re-frame/dispatch [::failed-to-invite-people %]))}]}))) (re-frame/dispatch [::failed-to-invite-people %]))}]})))
(fx/defn share-community (rf/defn share-community
{:events [::share-community-confirmation-pressed]} {:events [::share-community-confirmation-pressed]}
[cofx user-pk contacts] [cofx user-pk contacts]
(let [community-id (fetch-community-id-input cofx) (let [community-id (fetch-community-id-input cofx)
@@ -226,7 +239,7 @@
(log/error "failed to invite-user community" %) (log/error "failed to invite-user community" %)
(re-frame/dispatch [::failed-to-share-community %]))}]}))) (re-frame/dispatch [::failed-to-share-community %]))}]})))
(fx/defn create (rf/defn create
{:events [::create-confirmation-pressed]} {:events [::create-confirmation-pressed]}
[{:keys [db]}] [{:keys [db]}]
(let [{:keys [name description membership image]} (get db :communities/create)] (let [{:keys [name description membership image]} (get db :communities/create)]
@@ -248,7 +261,7 @@
(log/error "failed to create community" %) (log/error "failed to create community" %)
(re-frame/dispatch [::failed-to-create-community %]))}]}))) (re-frame/dispatch [::failed-to-create-community %]))}]})))
(fx/defn edit (rf/defn edit
{:events [::edit-confirmation-pressed]} {:events [::edit-confirmation-pressed]}
[{:keys [db]}] [{:keys [db]}]
(let [{:keys [id name description membership new-image color]} (get db :communities/create)] (let [{:keys [id name description membership new-image color]} (get db :communities/create)]
@@ -268,10 +281,10 @@
(log/error "failed to edit community" %) (log/error "failed to edit community" %)
(re-frame/dispatch [::failed-to-edit-community %]))}]})) (re-frame/dispatch [::failed-to-edit-community %]))}]}))
(fx/defn create-channel (rf/defn create-channel
{:events [::create-channel-confirmation-pressed]} {:events [::create-channel-confirmation-pressed]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(let [community-id (fetch-community-id-input cofx) (let [community-id (fetch-community-id-input cofx)
{:keys [name description color emoji]} (get db :communities/create-channel)] {:keys [name description color emoji]} (get db :communities/create-channel)]
{::json-rpc/call [{:method "wakuext_createCommunityChat" {::json-rpc/call [{:method "wakuext_createCommunityChat"
:params [community-id :params [community-id
@@ -279,7 +292,8 @@
:description description :description description
:color color :color color
:emoji emoji} :emoji emoji}
:permissions {:access constants/community-channel-access-no-membership}}] :permissions {:access
constants/community-channel-access-no-membership}}]
:js-response true :js-response true
:on-success #(re-frame/dispatch [::community-channel-created %]) :on-success #(re-frame/dispatch [::community-channel-created %])
:on-error #(do :on-error #(do
@@ -288,10 +302,11 @@
(def community-chat-id-length 68) (def community-chat-id-length 68)
(defn to-community-chat-id [chat-id] (defn to-community-chat-id
[chat-id]
(subs chat-id community-chat-id-length)) (subs chat-id community-chat-id-length))
(fx/defn edit-channel (rf/defn edit-channel
{:events [::edit-channel-confirmation-pressed]} {:events [::edit-channel-confirmation-pressed]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(let [{:keys [name description color community-id emoji edit-channel-id category-id position]} (let [{:keys [name description color community-id emoji edit-channel-id category-id position]}
@@ -305,161 +320,171 @@
:emoji emoji} :emoji emoji}
:category_id category-id :category_id category-id
:position position :position position
:permissions {:access constants/community-channel-access-no-membership}}] :permissions {:access
constants/community-channel-access-no-membership}}]
:js-response true :js-response true
:on-success #(re-frame/dispatch [::community-channel-edited %]) :on-success #(re-frame/dispatch [::community-channel-edited %])
:on-error #(do :on-error #(do
(log/error "failed to edit community channel" %) (log/error "failed to edit community channel" %)
(re-frame/dispatch [::failed-to-edit-community-channel %]))}]})) (re-frame/dispatch [::failed-to-edit-community-channel %]))}]}))
(defn require-membership? [permissions] (defn require-membership?
[permissions]
(not= constants/community-no-membership-access (:access permissions))) (not= constants/community-no-membership-access (:access permissions)))
(defn can-post? [community _ local-chat-id] (defn can-post?
[community _ local-chat-id]
(let [chat-id (to-community-chat-id local-chat-id)] (let [chat-id (to-community-chat-id local-chat-id)]
(get-in community [:chats chat-id :can-post?]))) (get-in community [:chats chat-id :can-post?])))
(fx/defn reset-community-id-input [{:keys [db]} id] (rf/defn reset-community-id-input
[{:keys [db]} id]
{:db (assoc db :communities/community-id-input id)}) {:db (assoc db :communities/community-id-input id)})
(fx/defn reset-channel-info [{:keys [db]}] (rf/defn reset-channel-info
[{:keys [db]}]
{:db (assoc db :communities/create-channel {})}) {:db (assoc db :communities/create-channel {})})
(fx/defn invite-people-pressed (rf/defn invite-people-pressed
{:events [:communities/invite-people-pressed]} {:events [:communities/invite-people-pressed]}
[cofx id] [cofx id]
(fx/merge cofx (rf/merge cofx
(reset-community-id-input id) (reset-community-id-input id)
(bottom-sheet/hide-bottom-sheet) (bottom-sheet/hide-bottom-sheet)
(navigation/open-modal :invite-people-community {:invite? true}))) (navigation/open-modal :invite-people-community {:invite? true})))
(fx/defn share-community-pressed (rf/defn share-community-pressed
{:events [:communities/share-community-pressed]} {:events [:communities/share-community-pressed]}
[cofx id] [cofx id]
(fx/merge cofx (rf/merge cofx
(reset-community-id-input id) (reset-community-id-input id)
(bottom-sheet/hide-bottom-sheet) (bottom-sheet/hide-bottom-sheet)
(navigation/open-modal :invite-people-community {}))) (navigation/open-modal :invite-people-community {})))
(fx/defn create-channel-pressed (rf/defn create-channel-pressed
{:events [::create-channel-pressed]} {:events [::create-channel-pressed]}
[{:keys [db] :as cofx} id] [{:keys [db] :as cofx} id]
(fx/merge cofx (rf/merge cofx
(reset-community-id-input id) (reset-community-id-input id)
(reset-channel-info) (reset-channel-info)
(>evt [::create-channel-fields (rand-nth emoji-thumbnail-styles/emoji-picker-default-thumbnails)]) (rf/dispatch [::create-channel-fields
(rand-nth emoji-thumbnail-styles/emoji-picker-default-thumbnails)])
(navigation/open-modal :create-community-channel {:community-id id}))) (navigation/open-modal :create-community-channel {:community-id id})))
(fx/defn edit-channel-pressed (rf/defn edit-channel-pressed
{:events [::edit-channel-pressed]} {:events [::edit-channel-pressed]}
[{:keys [db] :as cofx} community-id chat-name description color emoji chat-id category-id position] [{:keys [db] :as cofx} community-id chat-name description color emoji chat-id category-id position]
(let [{:keys [color emoji]} (if (string/blank? emoji) (let [{:keys [color emoji]} (if (string/blank? emoji)
(rand-nth emoji-thumbnail-styles/emoji-picker-default-thumbnails) (rand-nth emoji-thumbnail-styles/emoji-picker-default-thumbnails)
{:color color :emoji emoji})] {:color color :emoji emoji})]
(fx/merge cofx (rf/merge cofx
{:db (assoc db :communities/create-channel {:name chat-name {:db (assoc db
:description description :communities/create-channel
:color color {:name chat-name
:community-id community-id :description description
:emoji emoji :color color
:edit-channel-id chat-id :community-id community-id
:category-id category-id :emoji emoji
:position position})} :edit-channel-id chat-id
:category-id category-id
:position position})}
(navigation/open-modal :edit-community-channel nil)))) (navigation/open-modal :edit-community-channel nil))))
(fx/defn community-created (rf/defn community-created
{:events [::community-created]} {:events [::community-created]}
[cofx response-js] [cofx response-js]
(fx/merge cofx (rf/merge cofx
(navigation/navigate-back) (navigation/navigate-back)
(handle-response response-js))) (handle-response response-js)))
(fx/defn community-edited (rf/defn community-edited
{:events [::community-edited]} {:events [::community-edited]}
[cofx response-js] [cofx response-js]
(fx/merge cofx (rf/merge cofx
(navigation/navigate-back) (navigation/navigate-back)
(handle-response response-js))) (handle-response response-js)))
(fx/defn open-create-community (rf/defn open-create-community
{:events [::open-create-community]} {:events [::open-create-community]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(fx/merge cofx (rf/merge cofx
{:db (assoc db :communities/create {:membership constants/community-no-membership-access})} {:db (assoc db :communities/create {:membership constants/community-no-membership-access})}
(navigation/navigate-to :community-create nil))) (navigation/navigate-to :community-create nil)))
(fx/defn open-edit-community (rf/defn open-edit-community
{:events [::open-edit-community]} {:events [::open-edit-community]}
[{:keys [db] :as cofx} id] [{:keys [db] :as cofx} id]
(let [{:keys [name description images permissions color]} (get-in db [:communities id]) (let [{:keys [name description images permissions color]} (get-in db [:communities id])
{:keys [access]} permissions] {:keys [access]} permissions]
(fx/merge cofx (rf/merge cofx
{:db (assoc db :communities/create {:id id {:db (assoc db
:name name :communities/create
:description description {:id id
:image (get-in images [:large :uri]) :name name
:membership access :description description
:color color :image (get-in images [:large :uri])
:editing? true})} :membership access
:color color
:editing? true})}
(navigation/navigate-to :community-edit nil)))) (navigation/navigate-to :community-edit nil))))
(fx/defn community-imported (rf/defn community-imported
{:events [::community-imported]} {:events [::community-imported]}
[cofx response-js] [cofx response-js]
(fx/merge cofx (rf/merge cofx
(navigation/navigate-back) (navigation/navigate-back)
(handle-response response-js))) (handle-response response-js)))
(fx/defn people-invited (rf/defn people-invited
{:events [::people-invited]} {:events [::people-invited]}
[cofx response-js] [cofx response-js]
(fx/merge cofx (rf/merge cofx
(navigation/navigate-back) (navigation/navigate-back)
(handle-response response-js))) (handle-response response-js)))
(fx/defn community-channel-created (rf/defn community-channel-created
{:events [::community-channel-created]} {:events [::community-channel-created]}
[cofx response-js] [cofx response-js]
(fx/merge cofx (rf/merge cofx
(navigation/navigate-back) (navigation/navigate-back)
(handle-response response-js))) (handle-response response-js)))
(fx/defn community-channel-edited (rf/defn community-channel-edited
{:events [::community-channel-edited]} {:events [::community-channel-edited]}
[cofx response-js] [cofx response-js]
(fx/merge cofx (rf/merge cofx
(navigation/navigate-back) (navigation/navigate-back)
(handle-response response-js))) (handle-response response-js)))
(fx/defn create-field (rf/defn create-field
{:events [::create-field]} {:events [::create-field]}
[{:keys [db]} field value] [{:keys [db]} field value]
{:db (assoc-in db [:communities/create field] value)}) {:db (assoc-in db [:communities/create field] value)})
(fx/defn remove-field (rf/defn remove-field
{:events [::remove-field]} {:events [::remove-field]}
[{:keys [db]} field] [{:keys [db]} field]
{:db (update-in db [:communities/create] dissoc field)}) {:db (update-in db [:communities/create] dissoc field)})
(fx/defn create-channel-field (rf/defn create-channel-field
{:events [::create-channel-field]} {:events [::create-channel-field]}
[{:keys [db]} field value] [{:keys [db]} field value]
{:db (assoc-in db [:communities/create-channel field] value)}) {:db (assoc-in db [:communities/create-channel field] value)})
(fx/defn create-channel-fields (rf/defn create-channel-fields
{:events [::create-channel-fields]} {:events [::create-channel-fields]}
[{:keys [db]} field-values] [{:keys [db]} field-values]
{:db (update-in db [:communities/create-channel] merge field-values)}) {:db (update-in db [:communities/create-channel] merge field-values)})
(fx/defn member-banned (rf/defn member-banned
{:events [::member-banned]} {:events [::member-banned]}
[cofx response-js] [cofx response-js]
(fx/merge cofx (rf/merge cofx
(bottom-sheet/hide-bottom-sheet) (bottom-sheet/hide-bottom-sheet)
(handle-response response-js) (handle-response response-js)
(activity-center/notifications-fetch-unread-count))) (activity-center/notifications-fetch-unread-count)))
(fx/defn member-ban (rf/defn member-ban
{:events [::member-ban]} {:events [::member-ban]}
[cofx community-id public-key] [cofx community-id public-key]
{::json-rpc/call [{:method "wakuext_banUserFromCommunity" {::json-rpc/call [{:method "wakuext_banUserFromCommunity"
@@ -467,35 +492,43 @@
:user public-key}] :user public-key}]
:js-response true :js-response true
:on-success #(re-frame/dispatch [::member-banned %]) :on-success #(re-frame/dispatch [::member-banned %])
:on-error #(log/error "failed to ban user from community" community-id public-key %)}]}) :on-error #(log/error "failed to ban user from community"
community-id
public-key
%)}]})
(fx/defn member-kicked (rf/defn member-kicked
{:events [::member-kicked]} {:events [::member-kicked]}
[cofx response-js] [cofx response-js]
(fx/merge cofx (rf/merge cofx
(bottom-sheet/hide-bottom-sheet) (bottom-sheet/hide-bottom-sheet)
(handle-response response-js))) (handle-response response-js)))
(fx/defn member-kick (rf/defn member-kick
{:events [::member-kick]} {:events [::member-kick]}
[cofx community-id public-key] [cofx community-id public-key]
{::json-rpc/call [{:method "wakuext_removeUserFromCommunity" {::json-rpc/call [{:method "wakuext_removeUserFromCommunity"
:params [community-id public-key] :params [community-id public-key]
:js-response true :js-response true
:on-success #(re-frame/dispatch [::member-kicked %]) :on-success #(re-frame/dispatch [::member-kicked %])
:on-error #(log/error "failed to remove user from community" community-id public-key %)}]}) :on-error #(log/error "failed to remove user from community"
community-id
public-key
%)}]})
(fx/defn delete-community (rf/defn delete-community
{:events [::delete-community]} {:events [::delete-community]}
[cofx community-id] [cofx community-id]
(log/error "Community delete is not yet implemented")) (log/error "Community delete is not yet implemented"))
(fx/defn requests-to-join-fetched (rf/defn requests-to-join-fetched
{:events [::requests-to-join-fetched]} {:events [::requests-to-join-fetched]}
[{:keys [db]} community-id requests] [{:keys [db]} community-id requests]
{:db (assoc-in db [:communities/requests-to-join community-id] (<-requests-to-join-community-rpc requests))}) {:db (assoc-in db
[:communities/requests-to-join community-id]
(<-requests-to-join-community-rpc requests))})
(fx/defn fetch-requests-to-join (rf/defn fetch-requests-to-join
{:events [::fetch-requests-to-join]} {:events [::fetch-requests-to-join]}
[cofx community-id] [cofx community-id]
{::json-rpc/call [{:method "wakuext_pendingRequestsToJoinForCommunity" {::json-rpc/call [{:method "wakuext_pendingRequestsToJoinForCommunity"
@@ -503,48 +536,56 @@
:on-success #(re-frame/dispatch [::requests-to-join-fetched community-id %]) :on-success #(re-frame/dispatch [::requests-to-join-fetched community-id %])
:on-error #(log/error "failed to fetch requests-to-join" community-id %)}]}) :on-error #(log/error "failed to fetch requests-to-join" community-id %)}]})
(defn fetch-requests-to-join! [community-id] (defn fetch-requests-to-join!
[community-id]
(re-frame/dispatch [::fetch-requests-to-join community-id])) (re-frame/dispatch [::fetch-requests-to-join community-id]))
(fx/defn request-to-join-accepted (rf/defn request-to-join-accepted
{:events [::request-to-join-accepted]} {:events [::request-to-join-accepted]}
[{:keys [db] :as cofx} community-id request-id response-js] [{:keys [db] :as cofx} community-id request-id response-js]
(fx/merge cofx (rf/merge cofx
{:db (update-in db [:communities/requests-to-join community-id] dissoc request-id)} {:db (update-in db [:communities/requests-to-join community-id] dissoc request-id)}
(handle-response response-js))) (handle-response response-js)))
(fx/defn request-to-join-declined (rf/defn request-to-join-declined
{:events [::request-to-join-declined]} {:events [::request-to-join-declined]}
[{:keys [db] :as cofx} community-id request-id response-js] [{:keys [db] :as cofx} community-id request-id response-js]
(fx/merge cofx (rf/merge cofx
{:db (update-in db [:communities/requests-to-join community-id] dissoc request-id)} {:db (update-in db [:communities/requests-to-join community-id] dissoc request-id)}
(handle-response response-js))) (handle-response response-js)))
(fx/defn accept-request-to-join-pressed (rf/defn accept-request-to-join-pressed
{:events [:communities.ui/accept-request-to-join-pressed]} {:events [:communities.ui/accept-request-to-join-pressed]}
[cofx community-id request-id] [cofx community-id request-id]
{::json-rpc/call [{:method "wakuext_acceptRequestToJoinCommunity" {::json-rpc/call [{:method "wakuext_acceptRequestToJoinCommunity"
:params [{:id request-id}] :params [{:id request-id}]
:js-response true :js-response true
:on-success #(re-frame/dispatch [::request-to-join-accepted community-id request-id %]) :on-success #(re-frame/dispatch [::request-to-join-accepted community-id request-id
:on-error #(log/error "failed to accept requests-to-join" community-id request-id %)}]}) %])
:on-error #(log/error "failed to accept requests-to-join"
community-id
request-id
%)}]})
(fx/defn decline-request-to-join-pressed (rf/defn decline-request-to-join-pressed
{:events [:communities.ui/decline-request-to-join-pressed]} {:events [:communities.ui/decline-request-to-join-pressed]}
[cofx community-id request-id] [cofx community-id request-id]
{::json-rpc/call [{:method "wakuext_declineRequestToJoinCommunity" {::json-rpc/call [{:method "wakuext_declineRequestToJoinCommunity"
:params [{:id request-id}] :params [{:id request-id}]
:js-response true :js-response true
:on-success #(re-frame/dispatch [::request-to-join-declined community-id request-id %]) :on-success #(re-frame/dispatch [::request-to-join-declined community-id request-id
:on-error #(log/error "failed to decline requests-to-join" community-id request-id)}]}) %])
:on-error #(log/error "failed to decline requests-to-join"
community-id
request-id)}]})
(fx/defn switch-communities-enabled (rf/defn switch-communities-enabled
{:events [:multiaccounts.ui/switch-communities-enabled]} {:events [:multiaccounts.ui/switch-communities-enabled]}
[{:keys [db]} enabled?] [{:keys [db]} enabled?]
{::async-storage/set! {:communities-enabled? enabled?} {::async-storage/set! {:communities-enabled? enabled?}
:db (assoc db :communities/enabled? enabled?)}) :db (assoc db :communities/enabled? enabled?)})
(fx/defn create-category (rf/defn create-category
{:events [::create-category-confirmation-pressed]} {:events [::create-category-confirmation-pressed]}
[_ community-id category-title chat-ids] [_ community-id category-title chat-ids]
{::json-rpc/call [{:method "wakuext_createCommunityCategory" {::json-rpc/call [{:method "wakuext_createCommunityCategory"
@@ -557,12 +598,13 @@
(re-frame/dispatch [:sanitize-messages-and-process-response %])) (re-frame/dispatch [:sanitize-messages-and-process-response %]))
:on-error #(log/error "failed to create community category" %)}]}) :on-error #(log/error "failed to create community category" %)}]})
(fx/defn remove-chat-from-category (rf/defn remove-chat-from-category
{:events [:remove-chat-from-community-category]} {:events [:remove-chat-from-community-category]}
[{:keys [db]} community-id id categoryID] [{:keys [db]} community-id id categoryID]
(let [category (get-in db [:communities community-id :categories categoryID]) (let [category (get-in db [:communities community-id :categories categoryID])
category-chats (map :id (filter #(and (= (:categoryID %) categoryID) (not= id (:id %))) category-chats (map :id
(vals (get-in db [:communities community-id :chats]))))] (filter #(and (= (:categoryID %) categoryID) (not= id (:id %)))
(vals (get-in db [:communities community-id :chats]))))]
{::json-rpc/call [{:method "wakuext_editCommunityCategory" {::json-rpc/call [{:method "wakuext_editCommunityCategory"
:params [{:communityId community-id :params [{:communityId community-id
:categoryId categoryID :categoryId categoryID
@@ -572,7 +614,7 @@
:on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %]) :on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %])
:on-error #(log/error "failed to remove chat from community" %)}]})) :on-error #(log/error "failed to remove chat from community" %)}]}))
(fx/defn delete-community-chat (rf/defn delete-community-chat
{:events [:delete-community-chat]} {:events [:delete-community-chat]}
[_ community-id chat-id] [_ community-id chat-id]
{::json-rpc/call [{:method "wakuext_deleteCommunityChat" {::json-rpc/call [{:method "wakuext_deleteCommunityChat"
@@ -581,7 +623,7 @@
:on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %]) :on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %])
:on-error #(log/error "failed to delete community chat" %)}]}) :on-error #(log/error "failed to delete community chat" %)}]})
(fx/defn delete-category (rf/defn delete-category
{:events [:delete-community-category]} {:events [:delete-community-category]}
[_ community-id category-id] [_ community-id category-id]
{::json-rpc/call [{:method "wakuext_deleteCommunityCategory" {::json-rpc/call [{:method "wakuext_deleteCommunityCategory"
@@ -591,7 +633,7 @@
:on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %]) :on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %])
:on-error #(log/error "failed to delete community category" %)}]}) :on-error #(log/error "failed to delete community category" %)}]})
(fx/defn change-category (rf/defn change-category
{:events [::change-category-confirmation-pressed]} {:events [::change-category-confirmation-pressed]}
[cofx community-id category-id {:keys [id position categoryID]}] [cofx community-id category-id {:keys [id position categoryID]}]
(if (not (string/blank? category-id)) (if (not (string/blank? category-id))
@@ -605,11 +647,11 @@
(re-frame/dispatch [:navigate-back]) (re-frame/dispatch [:navigate-back])
(re-frame/dispatch [:sanitize-messages-and-process-response %])) (re-frame/dispatch [:sanitize-messages-and-process-response %]))
:on-error #(log/error "failed to change community category" %)}]} :on-error #(log/error "failed to change community category" %)}]}
(fx/merge cofx (rf/merge cofx
(navigation/navigate-back) (navigation/navigate-back)
(remove-chat-from-category community-id id categoryID)))) (remove-chat-from-category community-id id categoryID))))
(fx/defn reorder-category-chat (rf/defn reorder-category-chat
{:events [::reorder-community-category-chat]} {:events [::reorder-community-category-chat]}
[_ community-id category-id chat-id new-position] [_ community-id category-id chat-id new-position]
{::json-rpc/call [{:method "wakuext_reorderCommunityChat" {::json-rpc/call [{:method "wakuext_reorderCommunityChat"
@@ -621,7 +663,7 @@
:on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %]) :on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %])
:on-error #(log/error "failed to reorder community category chat" %)}]}) :on-error #(log/error "failed to reorder community category chat" %)}]})
(fx/defn reorder-category (rf/defn reorder-category
{:events [::reorder-community-category]} {:events [::reorder-community-category]}
[_ community-id category-id new-position] [_ community-id category-id new-position]
{::json-rpc/call [{:method "wakuext_reorderCommunityCategories" {::json-rpc/call [{:method "wakuext_reorderCommunityCategories"
@@ -632,17 +674,18 @@
:on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %]) :on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %])
:on-error #(log/error "failed to reorder community category" %)}]}) :on-error #(log/error "failed to reorder community category" %)}]})
(defn category-hash [public-key community-id category-id] (defn category-hash
[public-key community-id category-id]
(hash (str public-key community-id category-id))) (hash (str public-key community-id category-id)))
(fx/defn store-category-state (rf/defn store-category-state
{:events [::store-category-state]} {:events [::store-category-state]}
[{:keys [db]} community-id category-id state-open?] [{:keys [db]} community-id category-id state-open?]
(let [public-key (get-in db [:multiaccount :public-key]) (let [public-key (get-in db [:multiaccount :public-key])
hash (category-hash public-key community-id category-id)] hash (category-hash public-key community-id category-id)]
{::async-storage/set! {hash state-open?}})) {::async-storage/set! {hash state-open?}}))
(fx/defn update-category-states-in-db (rf/defn update-category-states-in-db
{:events [::category-states-loaded]} {:events [::category-states-loaded]}
[{:keys [db]} community-id hashes states] [{:keys [db]} community-id hashes states]
(let [public-key (get-in db [:multiaccount :public-key]) (let [public-key (get-in db [:multiaccount :public-key])
@@ -652,10 +695,11 @@
state (get states hash) state (get states hash)
category-updated (assoc category :state state)] category-updated (assoc category :state state)]
(assoc acc category-id category-updated))) (assoc acc category-id category-updated)))
{} categories-old)] {}
categories-old)]
{:db (update-in db [:communities community-id :categories] merge categories)})) {:db (update-in db [:communities community-id :categories] merge categories)}))
(fx/defn load-category-states (rf/defn load-category-states
{:events [:communities/load-category-states]} {:events [:communities/load-category-states]}
[{:keys [db]} community-id] [{:keys [db]} community-id]
(let [public-key (get-in db [:multiaccount :public-key]) (let [public-key (get-in db [:multiaccount :public-key])
@@ -669,26 +713,27 @@
(-> acc (-> acc
(assoc-in [:hashes category-id] hash) (assoc-in [:hashes category-id] hash)
(update :keys #(conj % hash))))) (update :keys #(conj % hash)))))
{} categories)] {}
categories)]
{::async-storage/get {:keys keys {::async-storage/get {:keys keys
:cb #(re-frame/dispatch :cb #(re-frame/dispatch
[::category-states-loaded community-id hashes %])}})) [::category-states-loaded community-id hashes %])}}))
(fx/defn navigate-to-community (rf/defn navigate-to-community
{:events [:communities/navigate-to-community]} {:events [:communities/navigate-to-community]}
[cofx community-id] [cofx community-id]
(fx/merge cofx (rf/merge cofx
(navigation/pop-to-root-tab :shell-stack) (navigation/pop-to-root-tab :shell-stack)
(navigation/navigate-to-nav2 :community community-id true))) (navigation/navigate-to-nav2 :community community-id true)))
(fx/defn member-role-updated (rf/defn member-role-updated
{:events [:community.member/role-updated]} {:events [:community.member/role-updated]}
[cofx response-js] [cofx response-js]
(fx/merge cofx (rf/merge cofx
(bottom-sheet/hide-bottom-sheet) (bottom-sheet/hide-bottom-sheet)
(handle-response response-js))) (handle-response response-js)))
(fx/defn add-role-to-member (rf/defn add-role-to-member
{:events [:community.member/add-role]} {:events [:community.member/add-role]}
[cofx community-id public-key role-id] [cofx community-id public-key role-id]
{::json-rpc/call [{:method "wakuext_addRoleToMember" {::json-rpc/call [{:method "wakuext_addRoleToMember"
@@ -702,7 +747,7 @@
:public-key public-key :public-key public-key
:role-id role-id})}]}) :role-id role-id})}]})
(fx/defn remove-role-from-member (rf/defn remove-role-from-member
{:events [:community.member/remove-role]} {:events [:community.member/remove-role]}
[_ community-id public-key role-id] [_ community-id public-key role-id]
{::json-rpc/call [{:method "wakuext_removeRoleFromMember" {::json-rpc/call [{:method "wakuext_removeRoleFromMember"
@@ -710,8 +755,9 @@
:user public-key :user public-key
:role role-id}]} :role role-id}]}
:on-success #(re-frame/dispatch [:community.member/role-updated %]) :on-success #(re-frame/dispatch [:community.member/role-updated %])
:on-error #(log/error "failed to remove role from member" :on-error
{:error % #(log/error "failed to remove role from member"
:community-id community-id {:error %
:public-key public-key :community-id community-id
:role-id role-id})]}) :public-key public-key
:role-id role-id})]})
+36 -33
View File
@@ -4,12 +4,12 @@
[status-im.contact.db :as contact.db] [status-im.contact.db :as contact.db]
[status-im.data-store.chats :as chats-store] [status-im.data-store.chats :as chats-store]
[status-im.data-store.contacts :as contacts-store] [status-im.data-store.contacts :as contacts-store]
[status-im2.navigation.events :as navigation]
[status-im.utils.types :as types] [status-im.utils.types :as types]
[status-im2.contexts.activity-center.events :as activity-center] [status-im2.contexts.activity-center.events :as activity-center]
[status-im.utils.fx :as fx])) [status-im2.navigation.events :as navigation]
[utils.re-frame :as rf]))
(fx/defn clean-up-chat (rf/defn clean-up-chat
[{:keys [db] :as cofx} [{:keys [db] :as cofx}
public-key public-key
{:keys [chat-id {:keys [chat-id
@@ -21,60 +21,63 @@
(when (= from public-key) (when (= from public-key)
message-id)) message-id))
(get-in db [:messages chat-id])) (get-in db [:messages chat-id]))
db (-> db db (-> db
;; remove messages ;; remove messages
(update-in [:messages chat-id] (update-in [:messages chat-id]
#(apply dissoc % removed-messages-ids)) #(apply dissoc % removed-messages-ids))
(update-in [:chats chat-id] (update-in [:chats chat-id]
assoc assoc
:unviewed-messages-count unviewed-messages-count :unviewed-messages-count unviewed-messages-count
:unviewed-mentions-count unviewed-mentions-count :unviewed-mentions-count unviewed-mentions-count
:last-message last-message))] :last-message last-message))]
{:db (assoc-in db [:message-lists chat-id] {:db (assoc-in db
(message-list/add-many nil (vals (get-in db [:messages chat-id]))))})) [:message-lists chat-id]
(message-list/add-many nil (vals (get-in db [:messages chat-id]))))}))
(fx/defn contact-blocked (rf/defn contact-blocked
{:events [::contact-blocked]} {:events [::contact-blocked]}
[{:keys [db] :as cofx} {:keys [public-key]} chats] [{:keys [db] :as cofx} {:keys [public-key]} chats]
(let [fxs (when chats (let [fxs (when chats
(map #(->> (chats-store/<-rpc %) (map #(->> (chats-store/<-rpc %)
(clean-up-chat public-key)) (types/js->clj chats)))] (clean-up-chat public-key))
(apply fx/merge (types/js->clj chats)))]
(apply rf/merge
cofx cofx
{:db (-> db {:db (-> db
(update :chats dissoc public-key) (update :chats dissoc public-key)
(update :chats-home-list disj public-key) (update :chats-home-list disj public-key)
(assoc-in [:contacts/contacts public-key :added] false)) (assoc-in [:contacts/contacts public-key :added] false))
:clear-message-notifications :clear-message-notifications
[[public-key] (get-in db [:multiaccount :remote-push-notifications-enabled?])]} [[public-key] (get-in db [:multiaccount :remote-push-notifications-enabled?])]}
(activity-center/notifications-fetch-unread-count) (activity-center/notifications-fetch-unread-count)
fxs))) fxs)))
(fx/defn block-contact (rf/defn block-contact
{:events [:contact.ui/block-contact-confirmed]} {:events [:contact.ui/block-contact-confirmed]}
[{:keys [db] :as cofx} public-key] [{:keys [db] :as cofx} public-key]
(let [contact (-> (contact.db/public-key->contact (let [contact (-> (contact.db/public-key->contact
(:contacts/contacts db) (:contacts/contacts db)
public-key) public-key)
(assoc :blocked true (assoc :blocked true
:added false)) :added false))
from-one-to-one-chat? (not (get-in db [:chats (:current-chat-id db) :group-chat]))] from-one-to-one-chat? (not (get-in db [:chats (:current-chat-id db) :group-chat]))]
(fx/merge cofx (rf/merge cofx
{:db (-> db {:db (-> db
;; add the contact to blocked contacts ;; add the contact to blocked contacts
(update :contacts/blocked (fnil conj #{}) public-key) (update :contacts/blocked (fnil conj #{}) public-key)
;; update the contact in contacts list ;; update the contact in contacts list
(assoc-in [:contacts/contacts public-key] contact))} (assoc-in [:contacts/contacts public-key] contact))}
(contacts-store/block (contacts-store/block
public-key #(do (re-frame/dispatch [::contact-blocked contact (.-chats %)]) public-key
(re-frame/dispatch [:sanitize-messages-and-process-response %]) #(do (re-frame/dispatch [::contact-blocked contact (.-chats %)])
(re-frame/dispatch [:hide-popover]))) (re-frame/dispatch [:sanitize-messages-and-process-response %])
(re-frame/dispatch [:hide-popover])))
;; reset navigation to avoid going back to non existing one to one chat ;; reset navigation to avoid going back to non existing one to one chat
(if from-one-to-one-chat? (if from-one-to-one-chat?
(navigation/pop-to-root-tab :chat-stack) (navigation/pop-to-root-tab :chat-stack)
(navigation/navigate-back))))) (navigation/navigate-back)))))
(fx/defn contact-unblocked (rf/defn contact-unblocked
{:events [::contact-unblocked]} {:events [::contact-unblocked]}
[{:keys [db]} contact-id] [{:keys [db]} contact-id]
(let [contact (-> (get-in db [:contacts/contacts contact-id]) (let [contact (-> (get-in db [:contacts/contacts contact-id])
@@ -83,7 +86,7 @@
(update :contacts/blocked disj contact-id) (update :contacts/blocked disj contact-id)
(assoc-in [:contacts/contacts contact-id] contact))})) (assoc-in [:contacts/contacts contact-id] contact))}))
(fx/defn unblock-contact (rf/defn unblock-contact
{:events [:contact.ui/unblock-contact-pressed]} {:events [:contact.ui/unblock-contact-pressed]}
[cofx contact-id] [cofx contact-id]
(contacts-store/unblock (contacts-store/unblock
+8 -8
View File
@@ -1,29 +1,29 @@
(ns status-im.contact.chat (ns status-im.contact.chat
(:require [re-frame.core :as re-frame] (:require [re-frame.core :as re-frame]
[status-im2.navigation.events :as navigation]
[status-im.utils.fx :as fx]
[status-im.chat.models :as chat] [status-im.chat.models :as chat]
[status-im.contact.core :as contact])) [status-im.contact.core :as contact]
[status-im2.navigation.events :as navigation]
[utils.re-frame :as rf]))
(fx/defn send-message-pressed (rf/defn send-message-pressed
{:events [:contact.ui/send-message-pressed] {:events [:contact.ui/send-message-pressed]
:interceptors [(re-frame/inject-cofx :random-id-generator)]} :interceptors [(re-frame/inject-cofx :random-id-generator)]}
[cofx {:keys [public-key ens-name]}] [cofx {:keys [public-key ens-name]}]
(chat/start-chat cofx public-key ens-name)) (chat/start-chat cofx public-key ens-name))
(fx/defn contact-code-submitted (rf/defn contact-code-submitted
{:events [:contact.ui/contact-code-submitted] {:events [:contact.ui/contact-code-submitted]
:interceptors [(re-frame/inject-cofx :random-id-generator)]} :interceptors [(re-frame/inject-cofx :random-id-generator)]}
[{{:contacts/keys [new-identity]} :db :as cofx} new-contact? nickname] [{{:contacts/keys [new-identity]} :db :as cofx} new-contact? nickname]
(let [{:keys [public-key ens-name]} new-identity] (let [{:keys [public-key ens-name]} new-identity]
(fx/merge cofx (rf/merge cofx
#(if new-contact? #(if new-contact?
(contact/add-contact % public-key nickname ens-name) (contact/add-contact % public-key nickname ens-name)
(chat/start-chat % public-key ens-name)) (chat/start-chat % public-key ens-name))
#(when new-contact? #(when new-contact?
(navigation/navigate-back %))))) (navigation/navigate-back %)))))
(fx/defn pinned-messages-pressed (rf/defn pinned-messages-pressed
{:events [:contact.ui/pinned-messages-pressed]} {:events [:contact.ui/pinned-messages-pressed]}
[cofx public-key] [cofx public-key]
(chat/navigate-to-user-pinned-messages cofx public-key)) (chat/navigate-to-user-pinned-messages cofx public-key))
+50 -45
View File
@@ -1,42 +1,44 @@
(ns status-im.contact.core (ns status-im.contact.core
(:require [re-frame.core :as re-frame] (:require [re-frame.core :as re-frame]
[status-im.constants :as constants]
[status-im.contact.block :as contact.block]
[status-im.contact.db :as contact.db] [status-im.contact.db :as contact.db]
[status-im.data-store.contacts :as contacts-store] [status-im.data-store.contacts :as contacts-store]
[status-im.ethereum.json-rpc :as json-rpc] [status-im.ethereum.json-rpc :as json-rpc]
[status-im2.navigation.events :as navigation]
[status-im.multiaccounts.update.core :as multiaccounts.update] [status-im.multiaccounts.update.core :as multiaccounts.update]
[status-im.utils.fx :as fx] [status-im2.navigation.events :as navigation]
[taoensso.timbre :as log] [taoensso.timbre :as log]
[status-im.constants :as constants] [utils.re-frame :as rf]))
[status-im.contact.block :as contact.block]))
(fx/defn load-contacts (rf/defn load-contacts
{:events [::contacts-loaded]} {:events [::contacts-loaded]}
[{:keys [db] :as cofx} all-contacts] [{:keys [db] :as cofx} all-contacts]
(let [contacts-list (map #(vector (:public-key %) (if (empty? (:address %)) (let [contacts-list (map #(vector (:public-key %)
(dissoc % :address) (if (empty? (:address %))
%)) (dissoc % :address)
%))
all-contacts) all-contacts)
contacts (into {} contacts-list)] contacts (into {} contacts-list)]
{:db (cond-> (-> db {:db (cond-> (-> db
(update :contacts/contacts #(merge contacts %)) (update :contacts/contacts #(merge contacts %))
(assoc :contacts/blocked (contact.db/get-blocked-contacts all-contacts))))})) (assoc :contacts/blocked (contact.db/get-blocked-contacts all-contacts))))}))
(defn build-contact (defn build-contact
[{{:keys [multiaccount] [{{:keys [multiaccount]
:contacts/keys [contacts]} :db} public-key] :contacts/keys [contacts]}
:db} public-key]
(cond-> (contact.db/public-key->contact contacts public-key) (cond-> (contact.db/public-key->contact contacts public-key)
(= public-key (:public-key multiaccount)) (= public-key (:public-key multiaccount))
(assoc :name (:name multiaccount)))) (assoc :name (:name multiaccount))))
(fx/defn ensure-contacts (rf/defn ensure-contacts
[{:keys [db]} contacts chats] [{:keys [db]} contacts chats]
(let [events (let [events
(reduce (reduce
(fn [acc {:keys [public-key] :as contact}] (fn [acc {:keys [public-key] :as contact}]
(let [added (:added contact) (let [added (:added contact)
was-added (contact.db/added? db public-key) was-added (contact.db/added? db public-key)
blocked (:blocked contact) blocked (:blocked contact)
was-blocked (contact.db/blocked? db public-key)] was-blocked (contact.db/blocked? db public-key)]
(cond-> acc (cond-> acc
(and added (not was-added)) (and added (not was-added))
@@ -51,7 +53,8 @@
[:activity-center.notifications/fetch-unread-count]] [:activity-center.notifications/fetch-unread-count]]
contacts)] contacts)]
(merge (merge
{:db (update db :contacts/contacts {:db (update db
:contacts/contacts
#(reduce (fn [acc {:keys [public-key] :as contact}] #(reduce (fn [acc {:keys [public-key] :as contact}]
(-> acc (-> acc
(update public-key merge contact) (update public-key merge contact)
@@ -68,15 +71,15 @@
:profile-image identicon :profile-image identicon
:address address})) :address address}))
(fx/defn send-contact-request (rf/defn send-contact-request
{:events [::send-contact-request]} {:events [::send-contact-request]}
[{:keys [db] :as cofx} public-key] [{:keys [db] :as cofx} public-key]
(let [{:keys [name profile-image]} (own-info db)] (let [{:keys [name profile-image]} (own-info db)]
{::json-rpc/call [{:method "wakuext_sendContactUpdate" {::json-rpc/call [{:method "wakuext_sendContactUpdate"
:params [public-key name profile-image] :params [public-key name profile-image]
:on-success #(log/debug "contact request sent" public-key)}]})) :on-success #(log/debug "contact request sent" public-key)}]}))
(fx/defn add-contact (rf/defn add-contact
"Add a contact and set pending to false" "Add a contact and set pending to false"
{:events [:contact.ui/add-to-contact-pressed]} {:events [:contact.ui/add-to-contact-pressed]}
[{:keys [db] :as cofx} public-key nickname ens-name] [{:keys [db] :as cofx} public-key nickname ens-name]
@@ -90,60 +93,62 @@
(re-frame/dispatch [:sanitize-messages-and-process-response %]) (re-frame/dispatch [:sanitize-messages-and-process-response %])
(re-frame/dispatch [:offload-messages constants/timeline-chat-id]))))) (re-frame/dispatch [:offload-messages constants/timeline-chat-id])))))
(fx/defn remove-contact (rf/defn remove-contact
"Remove a contact from current account's contact list" "Remove a contact from current account's contact list"
{:events [:contact.ui/remove-contact-pressed]} {:events [:contact.ui/remove-contact-pressed]}
[{:keys [db]} {:keys [public-key]}] [{:keys [db]} {:keys [public-key]}]
{:db (-> db {:db (-> db
(assoc-in [:contacts/contacts public-key :added] false) (assoc-in [:contacts/contacts public-key :added] false)
(assoc-in [:contacts/contacts public-key :contact-request-state] constants/contact-request-state-none)) (assoc-in [:contacts/contacts public-key :contact-request-state]
::json-rpc/call [{:method "wakuext_removeContact" constants/contact-request-state-none))
:params [public-key] ::json-rpc/call [{:method "wakuext_removeContact"
:params [public-key]
:on-success #(log/debug "contact removed successfully")} :on-success #(log/debug "contact removed successfully")}
{:method "wakuext_retractContactRequest" {:method "wakuext_retractContactRequest"
:params [{:contactId public-key}] :params [{:contactId public-key}]
:on-success #(log/debug "contact removed successfully")}] :on-success #(log/debug "contact removed successfully")}]
:dispatch [:offload-messages constants/timeline-chat-id]}) :dispatch [:offload-messages constants/timeline-chat-id]})
(fx/defn accept-contact-request (rf/defn accept-contact-request
{:events [:contact-requests.ui/accept-request]} {:events [:contact-requests.ui/accept-request]}
[{:keys [db]} id] [{:keys [db]} id]
{::json-rpc/call [{:method "wakuext_acceptContactRequest" {::json-rpc/call [{:method "wakuext_acceptContactRequest"
:params [{:id id}] :params [{:id id}]
:js-response true :js-response true
:on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %])}]}) :on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %])}]})
(fx/defn decline-contact-request (rf/defn decline-contact-request
{:events [:contact-requests.ui/decline-request]} {:events [:contact-requests.ui/decline-request]}
[{:keys [db]} id] [{:keys [db]} id]
{::json-rpc/call [{:method "wakuext_dismissContactRequest" {::json-rpc/call [{:method "wakuext_dismissContactRequest"
:params [{:id id}] :params [{:id id}]
:js-response true :js-response true
:on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %])}]}) :on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %])}]})
(fx/defn initialize-contacts [cofx] (rf/defn initialize-contacts
[cofx]
(contacts-store/fetch-contacts-rpc cofx #(re-frame/dispatch [::contacts-loaded %]))) (contacts-store/fetch-contacts-rpc cofx #(re-frame/dispatch [::contacts-loaded %])))
(fx/defn open-contact-toggle-list (rf/defn open-contact-toggle-list
{:events [:contact.ui/start-group-chat-pressed]} {:events [:contact.ui/start-group-chat-pressed]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(fx/merge cofx (rf/merge cofx
{:db (assoc db {:db (assoc db
:group/selected-contacts #{} :group/selected-contacts #{}
:new-chat-name "")} :new-chat-name "")}
(navigation/navigate-to-cofx :contact-toggle-list nil))) (navigation/navigate-to-cofx :contact-toggle-list nil)))
(fx/defn update-nickname (rf/defn update-nickname
{:events [:contacts/update-nickname]} {:events [:contacts/update-nickname]}
[{:keys [db] :as cofx} public-key nickname] [{:keys [db] :as cofx} public-key nickname]
(fx/merge cofx (rf/merge cofx
(contacts-store/set-nickname (contacts-store/set-nickname
public-key public-key
nickname nickname
#(re-frame/dispatch [:sanitize-messages-and-process-response %])) #(re-frame/dispatch [:sanitize-messages-and-process-response %]))
(navigation/navigate-back))) (navigation/navigate-back)))
(fx/defn switch-mutual-contact-requests-enabled (rf/defn switch-mutual-contact-requests-enabled
{:events [:multiaccounts.ui/switch-mutual-contact-requests-enabled]} {:events [:multiaccounts.ui/switch-mutual-contact-requests-enabled]}
[cofx enabled?] [cofx enabled?]
(multiaccounts.update/multiaccount-update (multiaccounts.update/multiaccount-update
@@ -152,7 +157,7 @@
enabled? enabled?
nil)) nil))
(fx/defn set-search-query (rf/defn set-search-query
{:events [:contacts/set-search-query]} {:events [:contacts/set-search-query]}
[{:keys [db] :as cofx} value] [{:keys [db] :as cofx} value]
{:db (assoc db :contacts/search-query value)}) {:db (assoc db :contacts/search-query value)})
+8 -6
View File
@@ -1,16 +1,18 @@
(ns status-im.currency.core (ns status-im.currency.core
(:require [status-im.multiaccounts.update.core :as multiaccounts.update] (:require [status-im.multiaccounts.update.core :as multiaccounts.update]
[status-im.utils.fx :as fx] [status-im.wallet.prices :as prices]
[status-im.wallet.prices :as prices])) [utils.re-frame :as rf]))
(defn get-currency [db] (defn get-currency
[db]
(get-in db [:multiaccount :currency] :usd)) (get-in db [:multiaccount :currency] :usd))
(fx/defn set-currency (rf/defn set-currency
{:events [:wallet.settings.ui/currency-selected]} {:events [:wallet.settings.ui/currency-selected]}
[{:keys [db] :as cofx} currency] [{:keys [db] :as cofx} currency]
(fx/merge cofx (rf/merge cofx
(multiaccounts.update/multiaccount-update (multiaccounts.update/multiaccount-update
:currency currency :currency
currency
{}) {})
(prices/update-prices))) (prices/update-prices)))
+48 -41
View File
@@ -1,31 +1,34 @@
(ns status-im.data-store.chats (ns status-im.data-store.chats
(:require [clojure.set :as clojure.set] (:require [clojure.set :as clojure.set]
[status-im.constants :as constants]
[status-im.data-store.messages :as messages] [status-im.data-store.messages :as messages]
[status-im.ethereum.json-rpc :as json-rpc] [status-im.ethereum.json-rpc :as json-rpc]
[status-im.constants :as constants] [status-im.utils.types :as types]
[status-im.utils.fx :as fx]
[taoensso.timbre :as log] [taoensso.timbre :as log]
[status-im.utils.types :as types])) [utils.re-frame :as rf]))
(defn rpc->type [{:keys [chat-type name] :as chat}] (defn rpc->type
[{:keys [chat-type name] :as chat}]
(cond (cond
(or (= constants/public-chat-type chat-type) (or (= constants/public-chat-type chat-type)
(= constants/profile-chat-type chat-type) (= constants/profile-chat-type chat-type)
(= constants/timeline-chat-type chat-type)) (assoc chat (= constants/timeline-chat-type chat-type))
:chat-name (str "#" name) (assoc chat
:public? true :chat-name (str "#" name)
:group-chat true :public? true
:timeline? (= constants/timeline-chat-type chat-type)) :group-chat true
:timeline? (= constants/timeline-chat-type chat-type))
(= constants/community-chat-type chat-type) (assoc chat (= constants/community-chat-type chat-type) (assoc chat
:chat-name name :chat-name name
:group-chat true) :group-chat true)
(= constants/private-group-chat-type chat-type) (assoc chat (= constants/private-group-chat-type chat-type) (assoc chat
:chat-name name :chat-name name
:public? false :public? false
:group-chat true) :group-chat true)
:else (assoc chat :public? false :group-chat false))) :else (assoc chat :public? false :group-chat false)))
(defn members-reducer [acc member] (defn members-reducer
[acc member]
(cond-> acc (cond-> acc
(:admin member) (:admin member)
(update :admins conj (:id member)) (update :admins conj (:id member))
@@ -34,45 +37,48 @@
:always :always
(update :contacts conj (:id member)))) (update :contacts conj (:id member))))
(defn- unmarshal-members [{:keys [members chat-type] :as chat}] (defn- unmarshal-members
[{:keys [members chat-type] :as chat}]
(cond (cond
(= constants/public-chat-type chat-type) (assoc chat (= constants/public-chat-type chat-type) (assoc chat
:contacts #{} :contacts #{}
:admins #{} :admins #{}
:members-joined #{}) :members-joined #{})
(= constants/private-group-chat-type chat-type) (merge chat (= constants/private-group-chat-type chat-type) (merge chat
(reduce members-reducer (reduce members-reducer
{:admins #{} {:admins #{}
:members-joined #{} :members-joined #{}
:contacts #{}} :contacts #{}}
members)) members))
:else :else
(assoc chat (assoc chat
:contacts #{(:id chat)} :contacts #{(:id chat)}
:admins #{} :admins #{}
:members-joined #{}))) :members-joined #{})))
(defn <-rpc [chat] (defn <-rpc
[chat]
(-> chat (-> chat
(clojure.set/rename-keys {:id :chat-id (clojure.set/rename-keys {:id :chat-id
:communityId :community-id :communityId :community-id
:syncedFrom :synced-from :syncedFrom :synced-from
:syncedTo :synced-to :syncedTo :synced-to
:membershipUpdateEvents :membership-update-events :membershipUpdateEvents :membership-update-events
:deletedAtClockValue :deleted-at-clock-value :deletedAtClockValue :deleted-at-clock-value
:chatType :chat-type :chatType :chat-type
:unviewedMessagesCount :unviewed-messages-count :unviewedMessagesCount :unviewed-messages-count
:unviewedMentionsCount :unviewed-mentions-count :unviewedMentionsCount :unviewed-mentions-count
:lastMessage :last-message :lastMessage :last-message
:lastClockValue :last-clock-value :lastClockValue :last-clock-value
:invitationAdmin :invitation-admin :invitationAdmin :invitation-admin
:profile :profile-public-key}) :profile :profile-public-key})
rpc->type rpc->type
unmarshal-members unmarshal-members
(update :last-message #(when % (messages/<-rpc %))) (update :last-message #(when % (messages/<-rpc %)))
(dissoc :members))) (dissoc :members)))
(defn <-rpc-js [^js chat] (defn <-rpc-js
[^js chat]
(-> {:name (.-name chat) (-> {:name (.-name chat)
:description (.-description chat) :description (.-description chat)
:color (.-color chat) :color (.-color chat)
@@ -103,9 +109,10 @@
rpc->type rpc->type
unmarshal-members)) unmarshal-members))
(fx/defn fetch-chats-rpc [_ {:keys [on-success]}] (rf/defn fetch-chats-rpc
{::json-rpc/call [{:method "wakuext_chatsPreview" [_ {:keys [on-success]}]
:params [] {::json-rpc/call [{:method "wakuext_chatsPreview"
:params []
:js-response true :js-response true
:on-success #(on-success ^js %) :on-success #(on-success ^js %)
:on-error #(log/error "failed to fetch chats" 0 -1 %)}]}) :on-error #(log/error "failed to fetch chats" 0 -1 %)}]})
+42 -35
View File
@@ -1,10 +1,11 @@
(ns status-im.data-store.contacts (ns status-im.data-store.contacts
(:require [clojure.set :as clojure.set] (:require [clojure.set :as clojure.set]
[status-im.ethereum.json-rpc :as json-rpc] [status-im.ethereum.json-rpc :as json-rpc]
[status-im.utils.fx :as fx] [taoensso.timbre :as log]
[taoensso.timbre :as log])) [utils.re-frame :as rf]))
(defn <-rpc [contact] (defn <-rpc
[contact]
(-> contact (-> contact
(clojure.set/rename-keys (clojure.set/rename-keys
{:id :public-key {:id :public-key
@@ -17,48 +18,54 @@
:lastENSClockValue :last-ens-clock-value :lastENSClockValue :last-ens-clock-value
:lastUpdated :last-updated :lastUpdated :last-updated
:localNickname :nickname}) :localNickname :nickname})
(assoc :mutual? (and (:added contact) (assoc :mutual?
(:hasAddedUs contact))))) (and (:added contact)
(:hasAddedUs contact)))))
(fx/defn fetch-contacts-rpc (rf/defn fetch-contacts-rpc
[_ on-success] [_ on-success]
{::json-rpc/call [{:method "wakuext_contacts" {::json-rpc/call [{:method "wakuext_contacts"
:params [] :params []
:on-success #(on-success (map <-rpc %)) :on-success #(on-success (map <-rpc %))
:on-error #(log/error "failed to fetch contacts" %)}]}) :on-error #(log/error "failed to fetch contacts" %)}]})
(fx/defn add (rf/defn add
[_ public-key nickname ens-name on-success] [_ public-key nickname ens-name on-success]
{::json-rpc/call [{:method "wakuext_addContact" {::json-rpc/call [{:method "wakuext_addContact"
:params [{:id public-key :nickname nickname :ensName ens-name}] :params [{:id public-key :nickname nickname :ensName ens-name}]
:js-response true :js-response true
:on-success #(do :on-success #(do
(log/info "saved contact" public-key "successfuly") (log/info "saved contact" public-key "successfuly")
(when on-success (when on-success
(on-success %))) (on-success %)))
:on-error #(log/error "failed to add contact" public-key %)}]}) :on-error #(log/error "failed to add contact" public-key %)}]})
(fx/defn set-nickname (rf/defn set-nickname
[_ public-key nickname on-success] [_ public-key nickname on-success]
{::json-rpc/call [{:method "wakuext_setContactLocalNickname" {::json-rpc/call [{:method "wakuext_setContactLocalNickname"
:params [{:id public-key :nickname nickname}] :params [{:id public-key :nickname nickname}]
:js-response true :js-response true
:on-success #(do :on-success #(do
(log/debug "set contact nickname" public-key "successfuly" nickname) (log/debug "set contact nickname" public-key "successfuly" nickname)
(when on-success (when on-success
(on-success %))) (on-success %)))
:on-error #(log/error "failed to set contact nickname " public-key nickname %)}]}) :on-error #(log/error "failed to set contact nickname "
public-key
nickname
%)}]})
(fx/defn block [_ contact-id on-success] (rf/defn block
{::json-rpc/call [{:method "wakuext_blockContact" [_ contact-id on-success]
:params [contact-id] {::json-rpc/call [{:method "wakuext_blockContact"
:params [contact-id]
:js-response true :js-response true
:on-success on-success :on-success on-success
:on-error #(log/error "failed to block contact" % contact-id)}]}) :on-error #(log/error "failed to block contact" % contact-id)}]})
(fx/defn unblock [_ contact-id on-success] (rf/defn unblock
{::json-rpc/call [{:method "wakuext_unblockContact" [_ contact-id on-success]
:params [contact-id] {::json-rpc/call [{:method "wakuext_unblockContact"
:on-success on-success :params [contact-id]
:on-success on-success
:js-response true :js-response true
:on-error #(log/error "failed to unblock contact" % contact-id)}]}) :on-error #(log/error "failed to unblock contact" % contact-id)}]})
+79 -64
View File
@@ -1,103 +1,118 @@
(ns status-im.data-store.messages (ns status-im.data-store.messages
(:require [clojure.set :as clojure.set] (:require [clojure.set :as clojure.set]
[status-im.ethereum.json-rpc :as json-rpc] [status-im.ethereum.json-rpc :as json-rpc]
[status-im.utils.fx :as fx] [taoensso.timbre :as log]
[taoensso.timbre :as log])) [utils.re-frame :as rf]))
(defn ->rpc [{:keys [content] :as message}] (defn ->rpc
[{:keys [content] :as message}]
(cond-> message (cond-> message
content content
(assoc :text (:text content) (assoc :text (:text content)
:sticker (:sticker content)) :sticker (:sticker content))
:always :always
(clojure.set/rename-keys {:chat-id :chat_id (clojure.set/rename-keys {:chat-id :chat_id
:whisper-timestamp :whisperTimestamp :whisper-timestamp :whisperTimestamp
:community-id :communityId :community-id :communityId
:clock-value :clock}))) :clock-value :clock})))
(defn <-rpc [message] (defn <-rpc
[message]
(-> message (-> message
(clojure.set/rename-keys {:id :message-id (clojure.set/rename-keys {:id :message-id
:whisperTimestamp :whisper-timestamp :whisperTimestamp :whisper-timestamp
:editedAt :edited-at :editedAt :edited-at
:contactVerificationState :contact-verification-state :contactVerificationState :contact-verification-state
:contactRequestState :contact-request-state :contactRequestState :contact-request-state
:commandParameters :command-parameters :commandParameters :command-parameters
:gapParameters :gap-parameters :gapParameters :gap-parameters
:messageType :message-type :messageType :message-type
:localChatId :chat-id :localChatId :chat-id
:communityId :community-id :communityId :community-id
:contentType :content-type :contentType :content-type
:clock :clock-value :clock :clock-value
:quotedMessage :quoted-message :quotedMessage :quoted-message
:outgoingStatus :outgoing-status :outgoingStatus :outgoing-status
:audioDurationMs :audio-duration-ms :audioDurationMs :audio-duration-ms
:deleted :deleted? :deleted :deleted?
:deletedForMe :deleted-for-me? :deletedForMe :deleted-for-me?
:new :new?}) :new :new?})
(update :quoted-message clojure.set/rename-keys {:parsedText :parsed-text :communityId :community-id}) (update :quoted-message
clojure.set/rename-keys
{:parsedText :parsed-text :communityId :community-id})
(update :outgoing-status keyword) (update :outgoing-status keyword)
(update :command-parameters clojure.set/rename-keys {:transactionHash :transaction-hash (update :command-parameters
:commandState :command-state}) clojure.set/rename-keys
(assoc :content {:chat-id (:chatId message) {:transactionHash :transaction-hash
:text (:text message) :commandState :command-state})
:image (:image message) (assoc :content {:chat-id (:chatId message)
:sticker (:sticker message) :text (:text message)
:ens-name (:ensName message) :image (:image message)
:line-count (:lineCount message) :sticker (:sticker message)
:parsed-text (:parsedText message) :ens-name (:ensName message)
:links (:links message) :line-count (:lineCount message)
:rtl? (:rtl message) :parsed-text (:parsedText message)
:response-to (:responseTo message)} :links (:links message)
:rtl? (:rtl message)
:response-to (:responseTo message)}
:outgoing (boolean (:outgoingStatus message))) :outgoing (boolean (:outgoingStatus message)))
(dissoc :ensName :chatId :text :rtl :responseTo :image :sticker :lineCount :parsedText :links))) (dissoc :ensName :chatId :text :rtl :responseTo :image :sticker :lineCount :parsedText :links)))
(defn messages-by-chat-id-rpc [chat-id (defn messages-by-chat-id-rpc
cursor [chat-id
limit cursor
on-success limit
on-error] on-success
on-error]
{::json-rpc/call [{:method "wakuext_chatMessages" {::json-rpc/call [{:method "wakuext_chatMessages"
:params [chat-id cursor limit] :params [chat-id cursor limit]
:on-success (fn [result] :on-success (fn [result]
(on-success (update result :messages #(map <-rpc %)))) (on-success (update result :messages #(map <-rpc %))))
:on-error on-error}]}) :on-error on-error}]})
(defn mark-seen-rpc [chat-id ids on-success] (defn mark-seen-rpc
{::json-rpc/call [{:method "wakuext_markMessagesSeen" [chat-id ids on-success]
:params [chat-id ids] {::json-rpc/call [{:method "wakuext_markMessagesSeen"
:params [chat-id ids]
:on-success #(do :on-success #(do
(log/debug "successfully marked as seen" %) (log/debug "successfully marked as seen" %)
(when on-success (on-success chat-id ids %))) (when on-success (on-success chat-id ids %)))
:on-error #(log/error "failed to get messages" %)}]}) :on-error #(log/error "failed to get messages" %)}]})
(defn delete-message-rpc [id] (defn delete-message-rpc
{::json-rpc/call [{:method "wakuext_deleteMessage" [id]
:params [id] {::json-rpc/call [{:method "wakuext_deleteMessage"
:params [id]
:on-success #(log/debug "successfully deleted message" id) :on-success #(log/debug "successfully deleted message" id)
:on-error #(log/error "failed to delete message" % id)}]}) :on-error #(log/error "failed to delete message" % id)}]})
(defn delete-messages-from-rpc [author] (defn delete-messages-from-rpc
{::json-rpc/call [{:method "wakuext_deleteMessagesFrom" [author]
:params [author] {::json-rpc/call [{:method "wakuext_deleteMessagesFrom"
:params [author]
:on-success #(log/debug "successfully deleted messages from" author) :on-success #(log/debug "successfully deleted messages from" author)
:on-error #(log/error "failed to delete messages from" % author)}]}) :on-error #(log/error "failed to delete messages from" % author)}]})
(defn delete-messages-by-chat-id-rpc [chat-id] (defn delete-messages-by-chat-id-rpc
{::json-rpc/call [{:method "wakuext_deleteMessagesByChatID" [chat-id]
:params [chat-id] {::json-rpc/call [{:method "wakuext_deleteMessagesByChatID"
:params [chat-id]
:on-success #(log/debug "successfully deleted messages by chat-id" chat-id) :on-success #(log/debug "successfully deleted messages by chat-id" chat-id)
:on-error #(log/error "failed to delete messages by chat-id" % chat-id)}]}) :on-error #(log/error "failed to delete messages by chat-id" % chat-id)}]})
(fx/defn delete-message [cofx id] (rf/defn delete-message
[cofx id]
(delete-message-rpc id)) (delete-message-rpc id))
(fx/defn delete-messages-from [cofx author] (rf/defn delete-messages-from
[cofx author]
(delete-messages-from-rpc author)) (delete-messages-from-rpc author))
(fx/defn mark-messages-seen [cofx chat-id ids on-success] (rf/defn mark-messages-seen
[cofx chat-id ids on-success]
(mark-seen-rpc chat-id ids on-success)) (mark-seen-rpc chat-id ids on-success))
(fx/defn delete-messages-by-chat-id [cofx chat-id] (rf/defn delete-messages-by-chat-id
[cofx chat-id]
(delete-messages-by-chat-id-rpc chat-id)) (delete-messages-by-chat-id-rpc chat-id))
+19 -14
View File
@@ -1,31 +1,36 @@
(ns status-im.data-store.pin-messages (ns status-im.data-store.pin-messages
(:require [clojure.set :as clojure.set] (:require [clojure.set :as clojure.set]
[status-im.data-store.messages :as messages]
[status-im.ethereum.json-rpc :as json-rpc] [status-im.ethereum.json-rpc :as json-rpc]
[status-im.utils.fx :as fx]
[taoensso.timbre :as log] [taoensso.timbre :as log]
[status-im.data-store.messages :as messages])) [utils.re-frame :as rf]))
(defn <-rpc [message] (defn <-rpc
[message]
(-> message (-> message
(merge (messages/<-rpc (message :message))) (merge (messages/<-rpc (message :message)))
(clojure.set/rename-keys {:pinnedAt :pinned-at (clojure.set/rename-keys {:pinnedAt :pinned-at
:pinnedBy :pinned-by}) :pinnedBy :pinned-by})
(dissoc :message))) (dissoc :message)))
(defn pinned-message-by-chat-id-rpc [chat-id (defn pinned-message-by-chat-id-rpc
cursor [chat-id
limit cursor
on-success limit
on-error] on-success
on-error]
{::json-rpc/call [{:method "wakuext_chatPinnedMessages" {::json-rpc/call [{:method "wakuext_chatPinnedMessages"
:params [chat-id cursor limit] :params [chat-id cursor limit]
:on-success (fn [result] :on-success (fn [result]
(let [result (clojure.set/rename-keys result {:pinnedMessages :pinned-messages})] (let [result (clojure.set/rename-keys result
{:pinnedMessages
:pinned-messages})]
(on-success (update result :pinned-messages #(map <-rpc %))))) (on-success (update result :pinned-messages #(map <-rpc %)))))
:on-error on-error}]}) :on-error on-error}]})
(fx/defn send-pin-message [cofx pin-message] (rf/defn send-pin-message
{::json-rpc/call [{:method "wakuext_sendPinMessage" [cofx pin-message]
:params [(messages/->rpc pin-message)] {::json-rpc/call [{:method "wakuext_sendPinMessage"
:params [(messages/->rpc pin-message)]
:on-success #(log/debug "successfully pinned message" pin-message) :on-success #(log/debug "successfully pinned message" pin-message)
:on-error #(log/error "failed to pin message" % pin-message)}]}) :on-error #(log/error "failed to pin message" % pin-message)}]})
@@ -2,23 +2,28 @@
(:require [clojure.set :as clojure.set] (:require [clojure.set :as clojure.set]
[re-frame.core :as re-frame] [re-frame.core :as re-frame]
[status-im.ethereum.json-rpc :as json-rpc] [status-im.ethereum.json-rpc :as json-rpc]
[status-im.utils.fx :as fx] [taoensso.timbre :as log]
[taoensso.timbre :as log])) [utils.re-frame :as rf]))
(defn <-rpc [visibility-status-update] (defn <-rpc
(clojure.set/rename-keys visibility-status-update {:publicKey :public-key [visibility-status-update]
:statusType :status-type})) (clojure.set/rename-keys visibility-status-update
(defn <-rpc-settings [settings] {:publicKey :public-key
:statusType :status-type}))
(defn <-rpc-settings
[settings]
(-> settings (-> settings
(clojure.set/rename-keys (clojure.set/rename-keys
{:current-user-status :current-user-visibility-status}) {:current-user-status :current-user-visibility-status})
(update :current-user-visibility-status <-rpc))) (update :current-user-visibility-status <-rpc)))
(fx/defn fetch-visibility-status-updates-rpc [_] (rf/defn fetch-visibility-status-updates-rpc
[_]
{::json-rpc/call [{:method "wakuext_statusUpdates" {::json-rpc/call [{:method "wakuext_statusUpdates"
:params [] :params []
:on-success #(re-frame/dispatch :on-success #(re-frame/dispatch
[:visibility-status-updates/visibility-status-updates-loaded [:visibility-status-updates/visibility-status-updates-loaded
(:statusUpdates ^js %)]) (:statusUpdates ^js %)])
:on-error #(log/error :on-error #(log/error
"failed to fetch visibility-status-updates" %)}]}) "failed to fetch visibility-status-updates"
%)}]})
+104 -74
View File
@@ -2,18 +2,19 @@
(:refer-clojure :exclude [name]) (:refer-clojure :exclude [name])
(:require [clojure.string :as string] (:require [clojure.string :as string]
[re-frame.core :as re-frame] [re-frame.core :as re-frame]
[status-im.bottom-sheet.core :as bottom-sheet]
[status-im.ethereum.core :as ethereum] [status-im.ethereum.core :as ethereum]
[status-im.ethereum.eip55 :as eip55] [status-im.ethereum.eip55 :as eip55]
[status-im.ethereum.ens :as ens] [status-im.ethereum.ens :as ens]
[status-im.ethereum.stateofus :as stateofus] [status-im.ethereum.stateofus :as stateofus]
[status-im.multiaccounts.update.core :as multiaccounts.update] [status-im.multiaccounts.update.core :as multiaccounts.update]
[status-im2.navigation.events :as navigation]
[status-im.utils.datetime :as datetime] [status-im.utils.datetime :as datetime]
[status-im.utils.fx :as fx]
[status-im.utils.random :as random] [status-im.utils.random :as random]
[status-im.bottom-sheet.core :as bottom-sheet])) [status-im2.navigation.events :as navigation]
[utils.re-frame :as rf]))
(defn fullname [custom-domain? username] (defn fullname
[custom-domain? username]
(if custom-domain? (if custom-domain?
username username
(stateofus/subdomain username))) (stateofus/subdomain username)))
@@ -38,14 +39,16 @@
(fn [[chain-id name cb]] (fn [[chain-id name cb]]
(ens/expire-at chain-id name cb))) (ens/expire-at chain-id name cb)))
(fx/defn update-ens-tx-state (rf/defn update-ens-tx-state
{:events [:update-ens-tx-state]} {:events [:update-ens-tx-state]}
[{:keys [db]} new-state username custom-domain? tx-hash] [{:keys [db]} new-state username custom-domain? tx-hash]
{:db (assoc-in db [:ens/registrations tx-hash] {:state new-state {:db (assoc-in db
:username username [:ens/registrations tx-hash]
:custom-domain? custom-domain?})}) {:state new-state
:username username
:custom-domain? custom-domain?})})
(fx/defn redirect-to-ens-summary (rf/defn redirect-to-ens-summary
{:events [::redirect-to-ens-summary]} {:events [::redirect-to-ens-summary]}
[cofx] [cofx]
;; we reset navigation so that navigate back doesn't return ;; we reset navigation so that navigate back doesn't return
@@ -55,19 +58,19 @@
[:my-profile [:my-profile
:ens-confirmation])) :ens-confirmation]))
(fx/defn update-ens-tx-state-and-redirect (rf/defn update-ens-tx-state-and-redirect
{:events [:update-ens-tx-state-and-redirect]} {:events [:update-ens-tx-state-and-redirect]}
[cofx new-state username custom-domain? tx-hash] [cofx new-state username custom-domain? tx-hash]
(fx/merge cofx (rf/merge cofx
(update-ens-tx-state new-state username custom-domain? tx-hash) (update-ens-tx-state new-state username custom-domain? tx-hash)
(redirect-to-ens-summary))) (redirect-to-ens-summary)))
(fx/defn clear-ens-registration (rf/defn clear-ens-registration
{:events [:clear-ens-registration]} {:events [:clear-ens-registration]}
[{:keys [db]} tx-hash] [{:keys [db]} tx-hash]
{:db (update db :ens/registrations dissoc tx-hash)}) {:db (update db :ens/registrations dissoc tx-hash)})
(fx/defn set-state (rf/defn set-state
{:events [::name-resolved]} {:events [::name-resolved]}
[{:keys [db]} username state address] [{:keys [db]} username state address]
(when (= username (when (= username
@@ -76,37 +79,43 @@
(assoc-in [:ens/registration :state] state) (assoc-in [:ens/registration :state] state)
(assoc-in [:ens/registration :address] address))})) (assoc-in [:ens/registration :address] address))}))
(fx/defn save-username (rf/defn save-username
{:events [::save-username]} {:events [::save-username]}
[{:keys [db] :as cofx} custom-domain? username redirectToSummary] [{:keys [db] :as cofx} custom-domain? username redirectToSummary]
(let [name (fullname custom-domain? username) (let [name (fullname custom-domain? username)
names (get-in db [:multiaccount :usernames] []) names (get-in db [:multiaccount :usernames] [])
new-names (conj names name)] new-names (conj names name)]
(fx/merge cofx (rf/merge cofx
(multiaccounts.update/multiaccount-update (multiaccounts.update/multiaccount-update
:usernames new-names :usernames
new-names
(when redirectToSummary (when redirectToSummary
{:on-success #(re-frame/dispatch [::redirect-to-ens-summary])})) {:on-success #(re-frame/dispatch [::redirect-to-ens-summary])}))
(when (empty? names) (when (empty? names)
(multiaccounts.update/multiaccount-update (multiaccounts.update/multiaccount-update
:preferred-name name {}))))) :preferred-name
name
{})))))
(fx/defn set-pub-key (rf/defn set-pub-key
{:events [::set-pub-key]} {:events [::set-pub-key]}
[{:keys [db]}] [{:keys [db]}]
(let [{:keys [username address custom-domain?]} (:ens/registration db) (let [{:keys [username address custom-domain?]} (:ens/registration db)
address (or address (ethereum/default-address db)) address (or address (ethereum/default-address db))
{:keys [public-key]} (:multiaccount db) {:keys [public-key]} (:multiaccount db)
chain-id (ethereum/chain-id db) chain-id (ethereum/chain-id db)
username (fullname custom-domain? username)] username (fullname custom-domain? username)]
(ens/set-pub-key-prepare-tx (ens/set-pub-key-prepare-tx
chain-id address username public-key chain-id
address
username
public-key
#(re-frame/dispatch [:signing.ui/sign #(re-frame/dispatch [:signing.ui/sign
{:tx-obj % {:tx-obj %
:on-result [::save-username custom-domain? username true] :on-result [::save-username custom-domain? username true]
:on-error [::on-registration-failure]}])))) :on-error [::on-registration-failure]}]))))
(fx/defn on-input-submitted (rf/defn on-input-submitted
{:events [::input-submitted]} {:events [::input-submitted]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(let [{:keys [state username custom-domain?]} (:ens/registration db)] (let [{:keys [state username custom-domain?]} (:ens/registration db)]
@@ -135,14 +144,16 @@
(ens/pubkey (ens/pubkey
chain-id chain-id
(fullname custom-domain? username) (fullname custom-domain? username)
#(re-frame/dispatch [::name-resolved username #(re-frame/dispatch
(cond [::name-resolved username
(not public-key) :owned (cond
(and (string/ends-with? % "0000000000000000000000000000000000000000000000000000000000000000") (not custom-domain?)) (not public-key) :owned
:invalid-ens (and (string/ends-with? % "0000000000000000000000000000000000000000000000000000000000000000")
(= % public-key) :connected (not custom-domain?))
:else :connected-with-different-key) :invalid-ens
(eip55/address->checksum response)])) (= % public-key) :connected
:else :connected-with-different-key)
(eip55/address->checksum response)]))
:else :else
(re-frame/dispatch [::name-resolved username :taken])))) (re-frame/dispatch [::name-resolved username :taken]))))
@@ -153,33 +164,40 @@
5 10 5 10
1 10)) 1 10))
(fx/defn register-name (rf/defn register-name
{:events [::register-name-pressed]} {:events [::register-name-pressed]}
[{:keys [db]} address] [{:keys [db]} address]
(let [{:keys [username]} (let [{:keys [username]}
(:ens/registration db) (:ens/registration db)
{:keys [public-key]} (:multiaccount db) {:keys [public-key]} (:multiaccount db)
chain-id (ethereum/chain-id db)] chain-id (ethereum/chain-id db)]
(ens/register-prepare-tx (ens/register-prepare-tx
chain-id address username public-key chain-id
address
username
public-key
#(re-frame/dispatch [:signing.ui/sign #(re-frame/dispatch [:signing.ui/sign
{:tx-obj % {:tx-obj %
:on-result [:update-ens-tx-state-and-redirect :submitted username false] :on-result [:update-ens-tx-state-and-redirect :submitted username false]
:on-error [::on-registration-failure]}])))) :on-error [::on-registration-failure]}]))))
(defn- valid-custom-domain? [username] (defn- valid-custom-domain?
[username]
(and (ens/is-valid-eth-name? username) (and (ens/is-valid-eth-name? username)
(stateofus/lower-case? username))) (stateofus/lower-case? username)))
(defn- valid-username? [custom-domain? username] (defn- valid-username?
[custom-domain? username]
(if custom-domain? (if custom-domain?
(valid-custom-domain? username) (valid-custom-domain? username)
(stateofus/valid-username? username))) (stateofus/valid-username? username)))
(defn- state [custom-domain? username usernames] (defn- state
[custom-domain? username usernames]
(cond (cond
(or (string/blank? username) (or (string/blank? username)
(> 4 (count username))) :too-short (> 4 (count username)))
:too-short
(valid-username? custom-domain? username) (valid-username? custom-domain? username)
(if (usernames (fullname custom-domain? username)) (if (usernames (fullname custom-domain? username))
:already-added :already-added
@@ -189,79 +207,88 @@
;;NOTE we want to handle only last resolve ;;NOTE we want to handle only last resolve
(def resolve-last-id (atom nil)) (def resolve-last-id (atom nil))
(fx/defn set-username-candidate (rf/defn set-username-candidate
{:events [::set-username-candidate]} {:events [::set-username-candidate]}
[{:keys [db]} username] [{:keys [db]} username]
(let [{:keys [custom-domain?]} (:ens/registration db) (let [{:keys [custom-domain?]} (:ens/registration db)
usernames (into #{} (get-in db [:multiaccount :usernames])) usernames (into #{} (get-in db [:multiaccount :usernames]))
state (state custom-domain? username usernames)] state (state custom-domain? username usernames)]
(reset! resolve-last-id (random/id)) (reset! resolve-last-id (random/id))
(merge (merge
{:db (update db :ens/registration assoc {:db (update db
:username username :ens/registration assoc
:state state)} :username username
:state state)}
(when (= state :searching) (when (= state :searching)
(let [{:keys [multiaccount]} db (let [{:keys [multiaccount]} db
{:keys [public-key]} multiaccount {:keys [public-key]} multiaccount
addresses (ethereum/addresses-without-watch db) addresses (ethereum/addresses-without-watch db)
chain-id (ethereum/chain-id db)] chain-id (ethereum/chain-id db)]
{::resolve-owner [chain-id {::resolve-owner [chain-id
(fullname custom-domain? username) (fullname custom-domain? username)
#(on-resolve-owner #(on-resolve-owner
chain-id custom-domain? username addresses public-key % chain-id
resolve-last-id @resolve-last-id)]}))))) custom-domain?
username
addresses
public-key
%
resolve-last-id
@resolve-last-id)]})))))
(fx/defn return-to-ens-main-screen (rf/defn return-to-ens-main-screen
{:events [::got-it-pressed ::cancel-pressed]} {:events [::got-it-pressed ::cancel-pressed]}
[{:keys [db] :as cofx} _] [{:keys [db] :as cofx} _]
(fx/merge cofx (rf/merge cofx
;; clear registration data ;; clear registration data
{:db (dissoc db :ens/registration)} {:db (dissoc db :ens/registration)}
;; we reset navigation so that navigate back doesn't return ;; we reset navigation so that navigate back doesn't return
;; into the registration flow ;; into the registration flow
(navigation/set-stack-root :profile-stack [:my-profile (navigation/set-stack-root :profile-stack
:ens-main]))) [:my-profile
:ens-main])))
(fx/defn switch-domain-type (rf/defn switch-domain-type
{:events [::switch-domain-type]} {:events [::switch-domain-type]}
[{:keys [db] :as cofx} _] [{:keys [db] :as cofx} _]
(fx/merge cofx (rf/merge cofx
{:db (-> db {:db (-> db
(update :ens/registration dissoc :username :state) (update :ens/registration dissoc :username :state)
(update-in [:ens/registration :custom-domain?] not))})) (update-in [:ens/registration :custom-domain?] not))}))
(fx/defn change-address (rf/defn change-address
{:events [::change-address]} {:events [::change-address]}
[{:keys [db] :as cofx} _ {:keys [address]}] [{:keys [db] :as cofx} _ {:keys [address]}]
(fx/merge cofx (rf/merge cofx
{:db (assoc-in db [:ens/registration :address] address)} {:db (assoc-in db [:ens/registration :address] address)}
(bottom-sheet/hide-bottom-sheet))) (bottom-sheet/hide-bottom-sheet)))
(fx/defn save-preferred-name (rf/defn save-preferred-name
{:events [::save-preferred-name]} {:events [::save-preferred-name]}
[cofx name] [cofx name]
(multiaccounts.update/multiaccount-update cofx (multiaccounts.update/multiaccount-update cofx
:preferred-name name :preferred-name
name
{})) {}))
(fx/defn on-registration-failure (rf/defn on-registration-failure
"TODO not sure there is actually anything to do here "TODO not sure there is actually anything to do here
it should only be called if the user cancels the signing it should only be called if the user cancels the signing
Actual registration failure has not been implemented properly" Actual registration failure has not been implemented properly"
{:events [::on-registration-failure]} {:events [::on-registration-failure]}
[_ _]) [_ _])
(fx/defn store-name-address (rf/defn store-name-address
{:events [::address-resolved]} {:events [::address-resolved]}
[{:keys [db]} username address] [{:keys [db]} username address]
{:db (assoc-in db [:ens/names username :address] address)}) {:db (assoc-in db [:ens/names username :address] address)})
(fx/defn store-name-public-key (rf/defn store-name-public-key
{:events [::public-key-resolved]} {:events [::public-key-resolved]}
[{:keys [db]} username public-key] [{:keys [db]} username public-key]
{:db (assoc-in db [:ens/names username :public-key] public-key)}) {:db (assoc-in db [:ens/names username :public-key] public-key)})
(fx/defn store-expiration-date (rf/defn store-expiration-date
{:events [::get-expiration-time-success]} {:events [::get-expiration-time-success]}
[{:keys [now db]} username timestamp] [{:keys [now db]} username timestamp]
{:db (-> db {:db (-> db
@@ -269,11 +296,11 @@
(datetime/timestamp->year-month-day-date timestamp)) (datetime/timestamp->year-month-day-date timestamp))
(assoc-in [:ens/names username :releasable?] (<= timestamp now)))}) (assoc-in [:ens/names username :releasable?] (<= timestamp now)))})
(fx/defn navigate-to-name (rf/defn navigate-to-name
{:events [::navigate-to-name]} {:events [::navigate-to-name]}
[{:keys [db] :as cofx} username] [{:keys [db] :as cofx} username]
(let [chain-id (ethereum/chain-id db)] (let [chain-id (ethereum/chain-id db)]
(fx/merge cofx (rf/merge cofx
{::get-expiration-time {::get-expiration-time
[chain-id [chain-id
(stateofus/username username) (stateofus/username username)
@@ -286,24 +313,27 @@
#(re-frame/dispatch [::public-key-resolved username %])]} #(re-frame/dispatch [::public-key-resolved username %])]}
(navigation/navigate-to-cofx :ens-name-details username)))) (navigation/navigate-to-cofx :ens-name-details username))))
(fx/defn start-registration (rf/defn start-registration
{:events [::add-username-pressed ::get-started-pressed]} {:events [::add-username-pressed ::get-started-pressed]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(fx/merge cofx (rf/merge cofx
(set-username-candidate (get-in db [:ens/registration :username] "")) (set-username-candidate (get-in db [:ens/registration :username] ""))
(navigation/navigate-to-cofx :ens-search {}))) (navigation/navigate-to-cofx :ens-search {})))
(fx/defn remove-username (rf/defn remove-username
{:events [::remove-username]} {:events [::remove-username]}
[{:keys [db] :as cofx} name] [{:keys [db] :as cofx} name]
(let [names (get-in db [:multiaccount :usernames] []) (let [names (get-in db [:multiaccount :usernames] [])
preferred-name (get-in db [:multiaccount :preferred-name]) preferred-name (get-in db [:multiaccount :preferred-name])
new-names (remove #(= name %) names)] new-names (remove #(= name %) names)]
(fx/merge cofx (rf/merge cofx
(multiaccounts.update/multiaccount-update (multiaccounts.update/multiaccount-update
:usernames new-names :usernames
new-names
{}) {})
(when (= name preferred-name) (when (= name preferred-name)
(multiaccounts.update/multiaccount-update (multiaccounts.update/multiaccount-update
:preferred-name (first new-names) {})) :preferred-name
(first new-names)
{}))
(navigation/navigate-back)))) (navigation/navigate-back))))
+32 -30
View File
@@ -1,22 +1,23 @@
(ns status-im.ethereum.subscriptions (ns status-im.ethereum.subscriptions
(:require [status-im.ethereum.eip55 :as eip55] (:require [status-im.ethereum.eip55 :as eip55]
[status-im.wallet.db :as wallet]
[status-im.wallet.core :as wallet.core]
[status-im.ethereum.transactions.core :as transactions] [status-im.ethereum.transactions.core :as transactions]
[status-im.utils.fx :as fx] [status-im.wallet.core :as wallet.core]
[taoensso.timbre :as log])) [status-im.wallet.db :as wallet]
[taoensso.timbre :as log]
[utils.re-frame :as rf]))
(fx/defn new-transfers (rf/defn new-transfers
[cofx block-number accounts] [cofx block-number accounts]
(log/debug "[wallet-subs] new-transfers" (log/debug "[wallet-subs] new-transfers"
"accounts" accounts "accounts" accounts
"block" block-number) "block" block-number)
(transactions/check-watched-transactions cofx)) (transactions/check-watched-transactions cofx))
(fx/defn recent-history-fetching-started (rf/defn recent-history-fetching-started
[{:keys [db]} accounts] [{:keys [db]} accounts]
(log/debug "[wallet-subs] recent-history-fetching-started" (log/debug "[wallet-subs] recent-history-fetching-started"
"accounts" accounts) "accounts"
accounts)
(let [event (get db :wallet/on-recent-history-fetching)] (let [event (get db :wallet/on-recent-history-fetching)]
(cond-> {:db (-> db (cond-> {:db (-> db
(transactions/update-fetching-status accounts :recent? true) (transactions/update-fetching-status accounts :recent? true)
@@ -25,20 +26,21 @@
event event
(assoc :dispatch event)))) (assoc :dispatch event))))
(fx/defn recent-history-fetching-ended (rf/defn recent-history-fetching-ended
[{:keys [db]} {:keys [accounts blockNumber]}] [{:keys [db]} {:keys [accounts blockNumber]}]
(log/debug "[wallet-subs] recent-history-fetching-ended" (log/debug "[wallet-subs] recent-history-fetching-ended"
"accounts" accounts "accounts" accounts
"block" blockNumber) "block" blockNumber)
{:db (-> db {:db (-> db
(assoc :ethereum/current-block blockNumber) (assoc :ethereum/current-block blockNumber)
(update-in [:wallet :accounts] (update-in [:wallet :accounts]
wallet/remove-transactions-since-block blockNumber) wallet/remove-transactions-since-block
(transactions/update-fetching-status accounts :recent? false) blockNumber)
(dissoc :wallet/waiting-for-recent-history? (transactions/update-fetching-status accounts :recent? false)
:wallet/refreshing-history? (dissoc :wallet/waiting-for-recent-history?
:wallet/fetching-error :wallet/refreshing-history?
:wallet/recent-history-fetching-started?)) :wallet/fetching-error
:wallet/recent-history-fetching-started?))
:transactions/get-transfers :transactions/get-transfers
{:chain-tokens (:wallet/all-tokens db) {:chain-tokens (:wallet/all-tokens db)
:addresses (reduce :addresses (reduce
@@ -53,27 +55,27 @@
:before-block blockNumber :before-block blockNumber
:limit 20}}) :limit 20}})
(fx/defn fetching-error (rf/defn fetching-error
[{:keys [db] :as cofx} {:keys [message]}] [{:keys [db] :as cofx} {:keys [message]}]
(fx/merge (rf/merge
cofx cofx
{:db (assoc db :wallet/fetching-error message)} {:db (assoc db :wallet/fetching-error message)}
(wallet.core/after-checking-history))) (wallet.core/after-checking-history)))
(fx/defn non-archival-node-detected (rf/defn non-archival-node-detected
[{:keys [db]} _] [{:keys [db]} _]
{:db (assoc db :wallet/non-archival-node true)}) {:db (assoc db :wallet/non-archival-node true)})
(fx/defn new-wallet-event (rf/defn new-wallet-event
[cofx {:keys [type blockNumber accounts] :as event}] [cofx {:keys [type blockNumber accounts] :as event}]
(log/info "[wallet-subs] new-wallet-event" (log/info "[wallet-subs] new-wallet-event"
"event-type" type "event-type" type
"blockNumber" blockNumber "blockNumber" blockNumber
"accounts" accounts) "accounts" accounts)
(case type (case type
"new-transfers" (new-transfers cofx blockNumber accounts) "new-transfers" (new-transfers cofx blockNumber accounts)
"recent-history-fetching" (recent-history-fetching-started cofx accounts) "recent-history-fetching" (recent-history-fetching-started cofx accounts)
"recent-history-ready" (recent-history-fetching-ended cofx event) "recent-history-ready" (recent-history-fetching-ended cofx event)
"fetching-history-error" (fetching-error cofx event) "fetching-history-error" (fetching-error cofx event)
"non-archival-node-detected" (non-archival-node-detected cofx event) "non-archival-node-detected" (non-archival-node-detected cofx event)
(log/warn ::unknown-wallet-event :type type :event event))) (log/warn ::unknown-wallet-event :type type :event event)))
+118 -98
View File
@@ -7,10 +7,10 @@
[status-im.ethereum.eip55 :as eip55] [status-im.ethereum.eip55 :as eip55]
[status-im.ethereum.encode :as encode] [status-im.ethereum.encode :as encode]
[status-im.ethereum.json-rpc :as json-rpc] [status-im.ethereum.json-rpc :as json-rpc]
[status-im.utils.fx :as fx]
[status-im.utils.mobile-sync :as utils.mobile-sync] [status-im.utils.mobile-sync :as utils.mobile-sync]
[status-im.wallet.core :as wallet] [status-im.wallet.core :as wallet]
[taoensso.timbre :as log])) [taoensso.timbre :as log]
[utils.re-frame :as rf]))
(def confirmations-count-threshold 12) (def confirmations-count-threshold 12)
@@ -23,8 +23,9 @@
(def network->subdomain {5 "goerli"}) (def network->subdomain {5 "goerli"})
(defn get-transaction-details-url [chain-id hash] (defn get-transaction-details-url
{:pre [(number? chain-id) (string? hash)] [chain-id hash]
{:pre [(number? chain-id) (string? hash)]
:post [(or (nil? %) (string? %))]} :post [(or (nil? %) (string? %))]}
(cond (cond
(etherscan-supported? chain-id) (etherscan-supported? chain-id)
@@ -51,15 +52,16 @@
(defn- parse-token-transfer (defn- parse-token-transfer
[chain-tokens contract] [chain-tokens contract]
(let [{:keys [symbol] :as token} (get chain-tokens contract (let [{:keys [symbol] :as token} (get chain-tokens
contract
default-erc20-token)] default-erc20-token)]
{:symbol symbol {:symbol symbol
:token token :token token
;; NOTE(goranjovic) - just a flag we need when we merge this entry ;; NOTE(goranjovic) - just a flag we need when we merge this entry
;; with the existing entry in the app, e.g. transaction info with ;; with the existing entry in the app, e.g. transaction info with
;; gas details, or a previous transfer entry with old confirmations ;; gas details, or a previous transfer entry with old confirmations
;; count. ;; count.
:transfer true})) :transfer true}))
(defn enrich-transfer (defn enrich-transfer
[chain-tokens [chain-tokens
@@ -105,7 +107,7 @@
(def default-transfers-limit 20) (def default-transfers-limit 20)
(fx/defn watch-transaction (rf/defn watch-transaction
"Set a watch for the given transaction "Set a watch for the given transaction
`watch-params` needs to contain a `trigger-fn` and `on-trigger` functions `watch-params` needs to contain a `trigger-fn` and `on-trigger` functions
`trigger-fn` is a function that returns true if the watch has been triggered `trigger-fn` is a function that returns true if the watch has been triggered
@@ -114,10 +116,11 @@
[{:keys [db]} transaction-id {:keys [trigger-fn on-trigger] :as watch-params}] [{:keys [db]} transaction-id {:keys [trigger-fn on-trigger] :as watch-params}]
(when (and (fn? trigger-fn) (when (and (fn? trigger-fn)
(fn? on-trigger)) (fn? on-trigger))
{:db (assoc-in db [:ethereum/watched-transactions transaction-id] {:db (assoc-in db
watch-params)})) [:ethereum/watched-transactions transaction-id]
watch-params)}))
(fx/defn check-transaction (rf/defn check-transaction
"Check if the transaction has been triggered and applies the effects returned "Check if the transaction has been triggered and applies the effects returned
by `on-trigger` if that is the case" by `on-trigger` if that is the case"
[{:keys [db] :as cofx} {:keys [hash] :as transaction}] [{:keys [db] :as cofx} {:keys [hash] :as transaction}]
@@ -125,12 +128,14 @@
(get-in db [:ethereum/watched-transactions hash])] (get-in db [:ethereum/watched-transactions hash])]
(let [{:keys [trigger-fn on-trigger]} watch-params] (let [{:keys [trigger-fn on-trigger]} watch-params]
(when (trigger-fn db transaction) (when (trigger-fn db transaction)
(fx/merge cofx (rf/merge cofx
{:db (update db :ethereum/watched-transactions {:db (update db
dissoc hash)} :ethereum/watched-transactions
dissoc
hash)}
(on-trigger transaction)))))) (on-trigger transaction))))))
(fx/defn check-watched-transactions (rf/defn check-watched-transactions
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(let [watched-transactions (let [watched-transactions
(reduce-kv (fn [acc _ {:keys [transactions]}] (reduce-kv (fn [acc _ {:keys [transactions]}]
@@ -139,13 +144,13 @@
(keys (get db :ethereum/watched-transactions))))) (keys (get db :ethereum/watched-transactions)))))
{} {}
(get-in db [:wallet :accounts]))] (get-in db [:wallet :accounts]))]
(apply fx/merge (apply rf/merge
cofx cofx
(map (fn [[_ transaction]] (map (fn [[_ transaction]]
(check-transaction transaction)) (check-transaction transaction))
watched-transactions)))) watched-transactions))))
(fx/defn add-transfer (rf/defn add-transfer
"We determine a unique id for the transfer before adding it because some "We determine a unique id for the transfer before adding it because some
transaction can contain multiple transfers and they would overwrite each other transaction can contain multiple transfers and they would overwrite each other
in the transfer map if identified by hash" in the transfer map if identified by hash"
@@ -157,25 +162,30 @@
(:type transfer-by-hash)))) (:type transfer-by-hash))))
id id
hash))] hash))]
(fx/merge cofx (rf/merge cofx
{:db (assoc-in db [:wallet :accounts address :transactions unique-id] {:db (assoc-in db
(assoc transfer :hash unique-id))} [:wallet :accounts address :transactions unique-id]
(assoc transfer :hash unique-id))}
(check-transaction transfer))))) (check-transaction transfer)))))
(defn get-min-known-block [db address] (defn get-min-known-block
[db address]
(get-in db [:wallet :accounts (eip55/address->checksum address) :min-block])) (get-in db [:wallet :accounts (eip55/address->checksum address) :min-block]))
(defn get-max-block-with-transfers [db address] (defn get-max-block-with-transfers
[db address]
(get-in db [:wallet :accounts (eip55/address->checksum address) :max-block])) (get-in db [:wallet :accounts (eip55/address->checksum address) :max-block]))
(defn min-block-transfers-count [db address] (defn min-block-transfers-count
(get-in db [:wallet :accounts [db address]
(eip55/address->checksum address) (get-in db
:min-block-transfers-count])) [:wallet :accounts
(eip55/address->checksum address)
:min-block-transfers-count]))
(fx/defn set-lowest-fetched-block (rf/defn set-lowest-fetched-block
[{:keys [db]} address transfers] [{:keys [db]} address transfers]
(let [checksum (eip55/address->checksum address) (let [checksum (eip55/address->checksum address)
{:keys [min-block min-block-transfers-count]} {:keys [min-block min-block-transfers-count]}
(reduce (reduce
(fn [{:keys [min-block] :as acc} (fn [{:keys [min-block] :as acc}
@@ -198,43 +208,47 @@
(min-block-transfers-count db address)} (min-block-transfers-count db address)}
transfers)] transfers)]
(log/debug "[transactions] set-lowest-fetched-block" (log/debug "[transactions] set-lowest-fetched-block"
"address" address "address" address
"min-block" min-block "min-block" min-block
"min-block-transfers-count" min-block-transfers-count) "min-block-transfers-count" min-block-transfers-count)
{:db (update-in db [:wallet :accounts checksum] assoc {:db (update-in db
:min-block min-block [:wallet :accounts checksum]
assoc
:min-block min-block
:min-block-transfers-count min-block-transfers-count)})) :min-block-transfers-count min-block-transfers-count)}))
(defn update-fetching-status (defn update-fetching-status
[db addresses fetching-type state] [db addresses fetching-type state]
(update-in (update-in
db [:wallet :fetching] db
[:wallet :fetching]
(fn [accounts] (fn [accounts]
(reduce (reduce
(fn [accounts address] (fn [accounts address]
(assoc-in accounts (assoc-in accounts
[(eip55/address->checksum address) fetching-type] [(eip55/address->checksum address) fetching-type]
state)) state))
accounts accounts
addresses)))) addresses))))
(fx/defn tx-fetching-in-progress (rf/defn tx-fetching-in-progress
[{:keys [db]} addresses] [{:keys [db]} addresses]
{:db (update-fetching-status db addresses :history? true)}) {:db (update-fetching-status db addresses :history? true)})
(fx/defn tx-fetching-ended (rf/defn tx-fetching-ended
[{:keys [db]} addresses] [{:keys [db]} addresses]
{:db (update-fetching-status db addresses :history? false)}) {:db (update-fetching-status db addresses :history? false)})
(fx/defn tx-history-end-reached (rf/defn tx-history-end-reached
[{:keys [db] :as cofx} address] [{:keys [db] :as cofx} address]
(let [syncing-allowed? (utils.mobile-sync/syncing-allowed? cofx)] (let [syncing-allowed? (utils.mobile-sync/syncing-allowed? cofx)]
{:db (assoc-in db [:wallet :fetching address :all-fetched?] {:db (assoc-in db
(if syncing-allowed? [:wallet :fetching address :all-fetched?]
:all (if syncing-allowed?
:all-preloaded))})) :all
:all-preloaded))}))
(fx/defn delete-pending-transactions (rf/defn delete-pending-transactions
[{:keys [db]} address transactions] [{:keys [db]} address transactions]
(let [all-transactions (let [all-transactions
(get-in db [:wallet :accounts (eip55/address->checksum address) :transactions]) (get-in db [:wallet :accounts (eip55/address->checksum address) :transactions])
@@ -247,38 +261,38 @@
transactions)] transactions)]
{:wallet/delete-pending-transactions pending-tx-hashes})) {:wallet/delete-pending-transactions pending-tx-hashes}))
(fx/defn handle-new-transfer (rf/defn handle-new-transfer
[{:keys [db] :as cofx} transfers {:keys [address limit]}] [{:keys [db] :as cofx} transfers {:keys [address limit]}]
(log/debug "[transfers] new-transfers" (log/debug "[transfers] new-transfers"
"address" address "address" address
"count" (count transfers) "count" (count transfers)
"limit" limit) "limit" limit)
(let [checksum (eip55/address->checksum address) (let [checksum (eip55/address->checksum address)
max-known-block (get-max-block-with-transfers db address) max-known-block (get-max-block-with-transfers db address)
effects (cond-> [(when (seq transfers) effects (cond-> [(when (seq transfers)
(set-lowest-fetched-block checksum transfers)) (set-lowest-fetched-block checksum transfers))
(wallet/set-max-block-with-transfers checksum transfers)] (wallet/set-max-block-with-transfers checksum transfers)]
(seq transfers) (seq transfers)
(concat (concat
[(delete-pending-transactions address transfers)] [(delete-pending-transactions address transfers)]
(mapv add-transfer transfers)) (mapv add-transfer transfers))
(and max-known-block (and max-known-block
(some #(> (:block %) max-known-block) transfers)) (some #(> (:block %) max-known-block) transfers))
(conj (wallet/update-balances (conj (wallet/update-balances
[address] [address]
(zero? max-known-block))) (zero? max-known-block)))
(and (zero? max-known-block) (and (zero? max-known-block)
(empty? transfers)) (empty? transfers))
(conj (wallet/set-zero-balances {:address address})) (conj (wallet/set-zero-balances {:address address}))
(< (count transfers) limit) (< (count transfers) limit)
(conj (tx-history-end-reached checksum)))] (conj (tx-history-end-reached checksum)))]
(apply fx/merge cofx (tx-fetching-ended [checksum]) effects))) (apply rf/merge cofx (tx-fetching-ended [checksum]) effects)))
(fx/defn check-ens-transactions (rf/defn check-ens-transactions
[{:keys [db] :as cofx} transfers] [{:keys [db] :as cofx} transfers]
(let [set-of-transactions-hash (reduce (fn [acc {:keys [hash]}] (conj acc hash)) #{} transfers) (let [set-of-transactions-hash (reduce (fn [acc {:keys [hash]}] (conj acc hash)) #{} transfers)
registrations (filter registrations (filter
@@ -288,12 +302,15 @@
(contains? set-of-transactions-hash hash))) (contains? set-of-transactions-hash hash)))
(get db :ens/registrations)) (get db :ens/registrations))
fxs (map (fn [[hash {:keys [username custom-domain?]}]] fxs (map (fn [[hash {:keys [username custom-domain?]}]]
(let [transfer (first (filter (fn [transfer] (let [transfer-hash (get transfer :hash)] (= transfer-hash hash))) transfers)) (let [transfer (first (filter (fn [transfer]
type (get transfer :type) (let [transfer-hash (get transfer :hash)]
(= transfer-hash hash)))
transfers))
type (get transfer :type)
transaction-success (get transfer :transfer)] transaction-success (get transfer :transfer)]
(cond (cond
(= transaction-success true) (= transaction-success true)
(fx/merge cofx (rf/merge cofx
(ens/clear-ens-registration hash) (ens/clear-ens-registration hash)
(ens/save-username custom-domain? username false)) (ens/save-username custom-domain? username false))
(= type :failed) (= type :failed)
@@ -301,69 +318,72 @@
:else :else
nil))) nil)))
registrations)] registrations)]
(apply fx/merge cofx fxs))) (apply rf/merge cofx fxs)))
(fx/defn new-transfers (rf/defn new-transfers
{:events [::new-transfers]} {:events [::new-transfers]}
[cofx transfers params] [cofx transfers params]
(fx/merge cofx (rf/merge cofx
(handle-new-transfer transfers params) (handle-new-transfer transfers params)
(wallet/stop-fetching-on-empty-tx-history transfers) (wallet/stop-fetching-on-empty-tx-history transfers)
(check-ens-transactions transfers))) (check-ens-transactions transfers)))
(fx/defn tx-fetching-failed (rf/defn tx-fetching-failed
{:events [::tx-fetching-failed]} {:events [::tx-fetching-failed]}
[cofx error address] [cofx error address]
(log/debug "[transactions] tx-fetching-failed" (log/debug "[transactions] tx-fetching-failed"
"address" address "address" address
"error" error) "error" error)
(tx-fetching-ended cofx [address])) (tx-fetching-ended cofx [address]))
(re-frame/reg-fx (re-frame/reg-fx
:transactions/get-transfers :transactions/get-transfers
(fn [{:keys [chain-tokens addresses before-block limit (fn
limit-per-address fetch-more?] [{:keys [chain-tokens addresses before-block limit
:as params limit-per-address fetch-more?]
:or {limit default-transfers-limit :as params
:or {limit default-transfers-limit
fetch-more? true}}] fetch-more? true}}]
{:pre [(spec/valid? {:pre [(spec/valid?
(spec/coll-of string?) (spec/coll-of string?)
addresses)]} addresses)]}
(log/debug "[transactions] get-transfers" (log/debug "[transactions] get-transfers"
"addresses" addresses "addresses" addresses
"block" before-block "block" before-block
"limit" limit "limit" limit
"limit-per-address" limit-per-address "limit-per-address" limit-per-address
"fetch-more?" fetch-more?) "fetch-more?" fetch-more?)
(doseq [address addresses] (doseq [address addresses]
(let [limit (or (get limit-per-address address) (let [limit (or (get limit-per-address address)
limit)] limit)]
(json-rpc/call (json-rpc/call
{:method "wallet_getTransfersByAddress" {:method "wallet_getTransfersByAddress"
:params [address (encode/uint before-block) (encode/uint limit) fetch-more?] :params [address (encode/uint before-block) (encode/uint limit) fetch-more?]
:on-success #(re-frame/dispatch :on-success #(re-frame/dispatch
[::new-transfers [::new-transfers
(enrich-transfers chain-tokens %) (enrich-transfers chain-tokens %)
(assoc params :address address (assoc params
:limit limit)]) :address address
:on-error #(re-frame/dispatch [::tx-fetching-failed address])}))))) :limit limit)])
:on-error #(re-frame/dispatch [::tx-fetching-failed address])})))))
(defn some-transactions-loaded? [db address] (defn some-transactions-loaded?
[db address]
(not-empty (get-in db [:wallet :accounts address :transactions]))) (not-empty (get-in db [:wallet :accounts address :transactions])))
(fx/defn fetch-more-tx (rf/defn fetch-more-tx
{:events [:transactions/fetch-more]} {:events [:transactions/fetch-more]}
[{:keys [db] :as cofx} address] [{:keys [db] :as cofx} address]
(let [min-known-block (or (get-min-known-block db address) (let [min-known-block (or (get-min-known-block db address)
(:ethereum/current-block db)) (:ethereum/current-block db))
min-block-transfers-count (or (min-block-transfers-count db address) 0)] min-block-transfers-count (or (min-block-transfers-count db address) 0)]
(fx/merge (rf/merge
cofx cofx
{:transactions/get-transfers {:transactions/get-transfers
{:chain-tokens (:wallet/all-tokens db) {:chain-tokens (:wallet/all-tokens db)
:addresses [address] :addresses [address]
:before-block min-known-block :before-block min-known-block
:fetch-more? (utils.mobile-sync/syncing-allowed? cofx) :fetch-more? (utils.mobile-sync/syncing-allowed? cofx)
;; Transfers are requested before and including `min-known-block` because ;; Transfers are requested before and including `min-known-block` because
;; there is no guarantee that all transfers from that block are shown ;; there is no guarantee that all transfers from that block are shown
;; already. To make sure that we fetch the whole `default-transfers-limit` ;; already. To make sure that we fetch the whole `default-transfers-limit`
@@ -373,7 +393,7 @@
min-block-transfers-count)}}} min-block-transfers-count)}}}
(tx-fetching-in-progress [address])))) (tx-fetching-in-progress [address]))))
(fx/defn get-fetched-transfers (rf/defn get-fetched-transfers
{:events [:transaction/get-fetched-transfers]} {:events [:transaction/get-fetched-transfers]}
[{:keys [db]}] [{:keys [db]}]
{:transactions/get-transfers {:transactions/get-transfers
+137 -122
View File
@@ -1,64 +1,65 @@
(ns status-im.events (ns status-im.events
(:require clojure.set (:require
[re-frame.core :as re-frame] clojure.set
status-im.add-new.core [re-frame.core :as re-frame]
[status-im.async-storage.core :as async-storage] status-im.add-new.core
status-im.backup.core [status-im.async-storage.core :as async-storage]
status-im.bootnodes.core status-im.backup.core
status-im.browser.core status-im.bootnodes.core
status-im.browser.permissions status-im.browser.core
[status-im.chat.models :as chat] status-im.browser.permissions
status-im.chat.models.images [status-im.chat.models :as chat]
status-im.chat.models.input status-im.chat.models.images
status-im.chat.models.loading status-im.chat.models.input
status-im.chat.models.transport status-im.chat.models.loading
[status-im.constants :as constants] status-im.chat.models.transport
status-im.contact.block [status-im.constants :as constants]
status-im.contact.chat status-im.contact.block
status-im.contact.core status-im.contact.chat
status-im.currency.core status-im.contact.core
[status-im.ethereum.json-rpc :as json-rpc] status-im.currency.core
status-im.ethereum.subscriptions [status-im.ethereum.json-rpc :as json-rpc]
status-im.fleet.core status-im.ethereum.subscriptions
status-im.http.core status-im.fleet.core
[status-im.i18n.i18n :as i18n] status-im.http.core
[status-im.keycard.core :as keycard] [status-im.i18n.i18n :as i18n]
status-im.log-level.core [status-im.keycard.core :as keycard]
status-im.mailserver.constants status-im.log-level.core
[status-im.mailserver.core :as mailserver] status-im.mailserver.constants
[status-im.multiaccounts.biometric.core :as biometric] [status-im.mailserver.core :as mailserver]
status-im.multiaccounts.login.core [status-im.multiaccounts.biometric.core :as biometric]
status-im.multiaccounts.logout.core status-im.multiaccounts.login.core
status-im.multiaccounts.update.core status-im.multiaccounts.logout.core
[status-im.native-module.core :as status] [status-im.multiaccounts.model :as multiaccounts.model]
[status-im2.navigation.events :as navigation] status-im.multiaccounts.update.core
status-im2.contexts.activity-center.events [status-im.native-module.core :as status]
status-im.pairing.core status-im.network.net-info
status-im.profile.core status-im.pairing.core
status-im.search.core status-im.profile.core
status-im.signals.core status-im.search.core
status-im.stickers.core status-im.signals.core
status-im.transport.core status-im.stickers.core
[status-im.ui.components.permissions :as permissions] status-im.transport.core
[status-im.ui.components.react :as react] [status-im.ui.components.permissions :as permissions]
status-im.ui.screens.privacy-and-security-settings.events [status-im.ui.components.react :as react]
[status-im.utils.dimensions :as dimensions] status-im.ui.screens.privacy-and-security-settings.events
[status-im.utils.fx :as fx] [status-im.utils.dimensions :as dimensions]
status-im.utils.logging.core status-im.utils.logging.core
[status-im.utils.universal-links.core :as universal-links] [status-im.utils.universal-links.core :as universal-links]
[status-im.utils.utils :as utils] [status-im.utils.utils :as utils]
status-im.visibility-status-updates.core status-im.visibility-status-popover.core
status-im.waku.core status-im.visibility-status-updates.core
status-im.wallet.accounts.core status-im.waku.core
status-im.wallet.choose-recipient.core status-im.wallet-connect-legacy.core
[status-im.wallet.core :as wallet] status-im.wallet-connect.core
status-im.wallet.custom-tokens.core status-im.wallet.accounts.core
status-im.wallet-connect.core status-im.wallet.choose-recipient.core
status-im.wallet-connect-legacy.core [status-im.wallet.core :as wallet]
status-im.network.net-info status-im.wallet.custom-tokens.core
status-im.visibility-status-popover.core status-im2.contexts.activity-center.events
status-im2.contexts.shell.events status-im2.contexts.shell.events
[status-im.multiaccounts.model :as multiaccounts.model])) [status-im2.navigation.events :as navigation]
[utils.re-frame :as rf]))
(re-frame/reg-fx (re-frame/reg-fx
:dismiss-keyboard :dismiss-keyboard
@@ -95,53 +96,58 @@
(fn [] (fn []
(dimensions/add-event-listener))) (dimensions/add-event-listener)))
(fx/defn dismiss-keyboard (rf/defn dismiss-keyboard
{:events [:dismiss-keyboard]} {:events [:dismiss-keyboard]}
[_] [_]
{:dismiss-keyboard nil}) {:dismiss-keyboard nil})
(fx/defn identicon-generated (rf/defn identicon-generated
{:events [:identicon-generated]} {:events [:identicon-generated]}
[{:keys [db]} path identicon] [{:keys [db]} path identicon]
{:db (assoc-in db path identicon)}) {:db (assoc-in db path identicon)})
(fx/defn gfycat-generated (rf/defn gfycat-generated
{:events [:gfycat-generated]} {:events [:gfycat-generated]}
[{:keys [db]} path gfycat] [{:keys [db]} path gfycat]
{:db (assoc-in db path gfycat)}) {:db (assoc-in db path gfycat)})
(fx/defn system-theme-mode-changed (rf/defn system-theme-mode-changed
{:events [:system-theme-mode-changed]} {:events [:system-theme-mode-changed]}
[cofx _] [cofx _]
(when (multiaccounts.model/logged-in? cofx) (when (multiaccounts.model/logged-in? cofx)
{:multiaccounts.ui/switch-theme (get-in cofx [:db :multiaccount :appearance]) {:multiaccounts.ui/switch-theme (get-in cofx [:db :multiaccount :appearance])
:dispatch [:reload-new-ui]})) :dispatch [:reload-new-ui]}))
(def authentication-options (def authentication-options
{:reason (i18n/label :t/biometric-auth-reason-login)}) {:reason (i18n/label :t/biometric-auth-reason-login)})
(defn- on-biometric-auth-result [{:keys [bioauth-success bioauth-code bioauth-message]}] (defn- on-biometric-auth-result
[{:keys [bioauth-success bioauth-code bioauth-message]}]
(when-not bioauth-success (when-not bioauth-success
(if (= bioauth-code "USER_FALLBACK") (if (= bioauth-code "USER_FALLBACK")
(re-frame/dispatch [:multiaccounts.logout.ui/logout-confirmed]) (re-frame/dispatch [:multiaccounts.logout.ui/logout-confirmed])
(utils/show-confirmation {:title (i18n/label :t/biometric-auth-confirm-title) (utils/show-confirmation
:content (or bioauth-message (i18n/label :t/biometric-auth-confirm-message)) {:title (i18n/label :t/biometric-auth-confirm-title)
:confirm-button-text (i18n/label :t/biometric-auth-confirm-try-again) :content (or bioauth-message (i18n/label :t/biometric-auth-confirm-message))
:cancel-button-text (i18n/label :t/biometric-auth-confirm-logout) :confirm-button-text (i18n/label :t/biometric-auth-confirm-try-again)
:on-accept #(biometric/authenticate nil on-biometric-auth-result authentication-options) :cancel-button-text (i18n/label :t/biometric-auth-confirm-logout)
:on-cancel #(re-frame/dispatch [:multiaccounts.logout.ui/logout-confirmed])})))) :on-accept #(biometric/authenticate nil
on-biometric-auth-result
authentication-options)
:on-cancel #(re-frame/dispatch [:multiaccounts.logout.ui/logout-confirmed])}))))
(fx/defn on-return-from-background [{:keys [db now] :as cofx}] (rf/defn on-return-from-background
[{:keys [db now] :as cofx}]
(let [app-in-background-since (get db :app-in-background-since) (let [app-in-background-since (get db :app-in-background-since)
signed-up? (get-in db [:multiaccount :signed-up?]) signed-up? (get-in db [:multiaccount :signed-up?])
biometric-auth? (= (:auth-method db) "biometric") biometric-auth? (= (:auth-method db) "biometric")
requires-bio-auth (and requires-bio-auth (and
signed-up? signed-up?
biometric-auth? biometric-auth?
(some? app-in-background-since) (some? app-in-background-since)
(>= (- now app-in-background-since) (>= (- now app-in-background-since)
constants/ms-in-bg-for-require-bioauth))] constants/ms-in-bg-for-require-bioauth))]
(fx/merge cofx (rf/merge cofx
{:db (dissoc db :app-in-background-since)} {:db (dissoc db :app-in-background-since)}
(mailserver/process-next-messages-request) (mailserver/process-next-messages-request)
(wallet/restart-wallet-service-after-background app-in-background-since) (wallet/restart-wallet-service-after-background app-in-background-since)
@@ -151,17 +157,17 @@
#(when requires-bio-auth #(when requires-bio-auth
(biometric/authenticate % on-biometric-auth-result authentication-options))))) (biometric/authenticate % on-biometric-auth-result authentication-options)))))
(fx/defn on-going-in-background (rf/defn on-going-in-background
[{:keys [db now]}] [{:keys [db now]}]
{:db (assoc db :app-in-background-since now) {:db (assoc db :app-in-background-since now)
:dispatch-n [[:audio-recorder/on-background] [:audio-message/on-background]]}) :dispatch-n [[:audio-recorder/on-background] [:audio-message/on-background]]})
(fx/defn app-state-change (rf/defn app-state-change
{:events [:app-state-change]} {:events [:app-state-change]}
[{:keys [db] :as cofx} state] [{:keys [db] :as cofx} state]
(let [app-coming-from-background? (= state "active") (let [app-coming-from-background? (= state "active")
app-going-in-background? (= state "background")] app-going-in-background? (= state "background")]
(fx/merge cofx (rf/merge cofx
{::app-state-change-fx state {::app-state-change-fx state
:db (assoc db :app-state state)} :db (assoc db :app-state state)}
#(when app-coming-from-background? #(when app-coming-from-background?
@@ -169,31 +175,31 @@
#(when app-going-in-background? #(when app-going-in-background?
(on-going-in-background %))))) (on-going-in-background %)))))
(fx/defn request-permissions (rf/defn request-permissions
{:events [:request-permissions]} {:events [:request-permissions]}
[_ options] [_ options]
{:request-permissions-fx options}) {:request-permissions-fx options})
(fx/defn update-window-dimensions (rf/defn update-window-dimensions
{:events [:update-window-dimensions]} {:events [:update-window-dimensions]}
[{:keys [db]} dimensions] [{:keys [db]} dimensions]
{:db (assoc db :dimensions/window (dimensions/window dimensions))}) {:db (assoc db :dimensions/window (dimensions/window dimensions))})
(fx/defn init-timeline-chat (rf/defn init-timeline-chat
{:events [:init-timeline-chat]} {:events [:init-timeline-chat]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(when-not (get-in db [:pagination-info constants/timeline-chat-id :messages-initialized?]) (when-not (get-in db [:pagination-info constants/timeline-chat-id :messages-initialized?])
(chat/preload-chat-data cofx constants/timeline-chat-id))) (chat/preload-chat-data cofx constants/timeline-chat-id)))
(fx/defn on-will-focus (rf/defn on-will-focus
{:events [:screens/on-will-focus]} {:events [:screens/on-will-focus]}
[{:keys [db] :as cofx} view-id] [{:keys [db] :as cofx} view-id]
(fx/merge cofx (rf/merge cofx
(cond (cond
(= :chat view-id) (= :chat view-id)
{::async-storage/set! {:chat-id (get-in cofx [:db :current-chat-id]) {::async-storage/set! {:chat-id (get-in cofx [:db :current-chat-id])
:key-uid (get-in cofx [:db :multiaccount :key-uid])} :key-uid (get-in cofx [:db :multiaccount :key-uid])}
:db (assoc db :screens/was-focused-once? true)} :db (assoc db :screens/was-focused-once? true)}
(= :login view-id) (= :login view-id)
{} {}
@@ -204,47 +210,49 @@
:else :else
{::async-storage/set! {:chat-id nil {::async-storage/set! {:chat-id nil
:key-uid nil} :key-uid nil}
:db (assoc db :screens/was-focused-once? true)}) :db (assoc db :screens/was-focused-once? true)})
#(case view-id #(case view-id
:keycard-settings (keycard/settings-screen-did-load %) :keycard-settings (keycard/settings-screen-did-load %)
:reset-card (keycard/reset-card-screen-did-load %) :reset-card (keycard/reset-card-screen-did-load %)
:enter-pin-settings (keycard/enter-pin-screen-did-load %) :enter-pin-settings (keycard/enter-pin-screen-did-load %)
:keycard-login-pin (keycard/login-pin-screen-did-load %) :keycard-login-pin (keycard/login-pin-screen-did-load %)
:add-new-account-pin (keycard/enter-pin-screen-did-load %) :add-new-account-pin (keycard/enter-pin-screen-did-load %)
:keycard-authentication-method (keycard/authentication-method-screen-did-load %) :keycard-authentication-method (keycard/authentication-method-screen-did-load %)
:multiaccounts (keycard/multiaccounts-screen-did-load %) :multiaccounts (keycard/multiaccounts-screen-did-load %)
:wallet (wallet/wallet-will-focus %) :wallet (wallet/wallet-will-focus %)
nil))) nil)))
;;TODO :replace by named events ;;TODO :replace by named events
(fx/defn set-event (rf/defn set-event
{:events [:set]} {:events [:set]}
[{:keys [db]} k v] [{:keys [db]} k v]
{:db (assoc db k v)}) {:db (assoc db k v)})
(fx/defn set-view-id (rf/defn set-view-id
{:events [:set-view-id]} {:events [:set-view-id]}
[{:keys [db]} view-id] [{:keys [db]} view-id]
{:db (assoc db :view-id view-id)}) {:db (assoc db :view-id view-id)})
;;TODO :replace by named events ;;TODO :replace by named events
(fx/defn set-once-event (rf/defn set-once-event
{:events [:set-once]} {:events [:set-once]}
[{:keys [db]} k v] [{:keys [db]} k v]
(when-not (get db k) (when-not (get db k)
{:db (assoc db k v)})) {:db (assoc db k v)}))
;;TODO :replace by named events ;;TODO :replace by named events
(fx/defn set-in-event (rf/defn set-in-event
{:events [:set-in]} {:events [:set-in]}
[{:keys [db]} path v] [{:keys [db]} path v]
{:db (assoc-in db path v)}) {:db (assoc-in db path v)})
(defn on-ramp<-rpc [on-ramp] (defn on-ramp<-rpc
(clojure.set/rename-keys on-ramp {:logoUrl :logo-url [on-ramp]
:siteUrl :site-url})) (clojure.set/rename-keys on-ramp
{:logoUrl :logo-url
:siteUrl :site-url}))
(fx/defn crypto-loaded-event (rf/defn crypto-loaded-event
{:events [::crypto-loaded]} {:events [::crypto-loaded]}
[{:keys [db]} on-ramps] [{:keys [db]} on-ramps]
{:db (assoc {:db (assoc
@@ -252,7 +260,7 @@
:buy-crypto/on-ramps :buy-crypto/on-ramps
(map on-ramp<-rpc on-ramps))}) (map on-ramp<-rpc on-ramps))})
(fx/defn buy-crypto-ui-loaded (rf/defn buy-crypto-ui-loaded
{:events [:buy-crypto.ui/loaded]} {:events [:buy-crypto.ui/loaded]}
[_] [_]
{::json-rpc/call [{:method "wallet_getCryptoOnRamps" {::json-rpc/call [{:method "wallet_getCryptoOnRamps"
@@ -260,10 +268,10 @@
:on-success (fn [on-ramps] :on-success (fn [on-ramps]
(re-frame/dispatch [::crypto-loaded on-ramps]))}]}) (re-frame/dispatch [::crypto-loaded on-ramps]))}]})
(fx/defn open-buy-crypto-screen (rf/defn open-buy-crypto-screen
{:events [:buy-crypto.ui/open-screen]} {:events [:buy-crypto.ui/open-screen]}
[cofx] [cofx]
(fx/merge (rf/merge
cofx cofx
(navigation/open-modal :buy-crypto nil) (navigation/open-modal :buy-crypto nil)
(wallet/keep-watching-history))) (wallet/keep-watching-history)))
@@ -275,38 +283,45 @@
:global? true/false (close information box across all profiles)}]" :global? true/false (close information box across all profiles)}]"
[]) [])
(defn information-box-id-hash [id public-key global?] (defn information-box-id-hash
[id public-key global?]
(if global? (if global?
(hash id) (hash id)
(hash (str public-key id)))) (hash (str public-key id))))
(fx/defn close-information-box (rf/defn close-information-box
{:events [:close-information-box]} {:events [:close-information-box]}
[{:keys [db]} id global?] [{:keys [db]} id global?]
(let [public-key (get-in db [:multiaccount :public-key]) (let [public-key (get-in db [:multiaccount :public-key])
hash (information-box-id-hash id public-key global?)] hash (information-box-id-hash id public-key global?)]
{::async-storage/set! {hash true} {::async-storage/set! {hash true}
:db (assoc-in db [:information-box-states id] true)})) :db (assoc-in db [:information-box-states id] true)}))
(fx/defn information-box-states-loaded (rf/defn information-box-states-loaded
{:events [:information-box-states-loaded]} {:events [:information-box-states-loaded]}
[{:keys [db]} hashes states] [{:keys [db]} hashes states]
{:db (assoc db :information-box-states (reduce {:db (assoc db
(fn [acc [id hash]] :information-box-states
(assoc acc id (get states hash))) (reduce
{} hashes))}) (fn [acc [id hash]]
(assoc acc id (get states hash)))
{}
hashes))})
(fx/defn load-information-box-states (rf/defn load-information-box-states
{:events [:load-information-box-states]} {:events [:load-information-box-states]}
[{:keys [db]}] [{:keys [db]}]
(let [public-key (get-in db [:multiaccount :public-key]) (let [public-key (get-in db [:multiaccount :public-key])
{:keys [keys hashes]} (reduce (fn [acc {:keys [id global?]}] {:keys [keys hashes]} (reduce (fn [acc {:keys [id global?]}]
(let [hash (information-box-id-hash (let [hash (information-box-id-hash
id public-key global?)] id
public-key
global?)]
(-> acc (-> acc
(assoc-in [:hashes id] hash) (assoc-in [:hashes id] hash)
(update :keys #(conj % hash))))) (update :keys #(conj % hash)))))
{} closable-information-boxes)] {}
closable-information-boxes)]
{::async-storage/get {:keys keys {::async-storage/get {:keys keys
:cb #(re-frame/dispatch :cb #(re-frame/dispatch
[:information-box-states-loaded hashes %])}})) [:information-box-states-loaded hashes %])}}))
+24 -20
View File
@@ -5,7 +5,7 @@
[status-im.multiaccounts.update.core :as multiaccounts.update] [status-im.multiaccounts.update.core :as multiaccounts.update]
[status-im.node.core :as node] [status-im.node.core :as node]
[status-im.utils.config :as config] [status-im.utils.config :as config]
[status-im.utils.fx :as fx])) [utils.re-frame :as rf]))
(defn current-fleet-sub (defn current-fleet-sub
[multiaccount] [multiaccount]
@@ -14,10 +14,10 @@
(defn format-mailserver (defn format-mailserver
[mailserver address] [mailserver address]
{:id mailserver {:id mailserver
:name (name mailserver) :name (name mailserver)
:password constants/mailserver-password :password constants/mailserver-password
:address address}) :address address})
(defn format-mailservers (defn format-mailservers
[mailservers] [mailservers]
@@ -26,13 +26,14 @@
{} {}
mailservers)) mailservers))
(defn default-mailservers [db] (defn default-mailservers
[db]
(reduce (fn [acc [fleet node-by-type]] (reduce (fn [acc [fleet node-by-type]]
(assoc acc fleet (format-mailservers (:mail node-by-type)))) (assoc acc fleet (format-mailservers (:mail node-by-type))))
{} {}
(node/fleets db))) (node/fleets db)))
(fx/defn show-save-confirmation (rf/defn show-save-confirmation
{:events [:fleet.ui/fleet-selected]} {:events [:fleet.ui/fleet-selected]}
[_ fleet] [_ fleet]
{:ui/show-confirmation {:ui/show-confirmation
@@ -44,7 +45,8 @@
#(re-frame/dispatch [:fleet.ui/save-fleet-confirmed (keyword fleet)]) #(re-frame/dispatch [:fleet.ui/save-fleet-confirmed (keyword fleet)])
:on-cancel nil}}) :on-cancel nil}})
(defn nodes->fleet [nodes] (defn nodes->fleet
[nodes]
(letfn [(format-nodes [nodes] (letfn [(format-nodes [nodes]
(reduce (fn [acc n] (reduce (fn [acc n]
(assoc acc (assoc acc
@@ -52,28 +54,30 @@
n)) n))
{} {}
nodes))] nodes))]
{:boot (format-nodes nodes) {:boot (format-nodes nodes)
:mail (format-nodes nodes) :mail (format-nodes nodes)
:whisper (format-nodes nodes)})) :whisper (format-nodes nodes)}))
(fx/defn set-nodes [{:keys [db]} fleet nodes] (rf/defn set-nodes
[{:keys [db]} fleet nodes]
{:db (-> db {:db (-> db
(assoc-in [:custom-fleets fleet] (nodes->fleet nodes)) (assoc-in [:custom-fleets fleet] (nodes->fleet nodes))
(assoc-in [:mailserver/mailservers fleet] (format-mailservers (assoc-in [:mailserver/mailservers fleet]
(reduce (format-mailservers
(fn [acc e] (reduce
(assoc acc (fn [acc e]
(keyword e) (assoc acc
e)) (keyword e)
{} e))
nodes))))}) {}
nodes))))})
(fx/defn save (rf/defn save
{:events [:fleet.ui/save-fleet-confirmed]} {:events [:fleet.ui/save-fleet-confirmed]}
[{:keys [db now] :as cofx} fleet] [{:keys [db now] :as cofx} fleet]
(let [old-fleet (get-in db [:multiaccount :fleet])] (let [old-fleet (get-in db [:multiaccount :fleet])]
(when (not= fleet old-fleet) (when (not= fleet old-fleet)
(fx/merge (rf/merge
cofx cofx
(multiaccounts.update/multiaccount-update :fleet fleet {}) (multiaccounts.update/multiaccount-update :fleet fleet {})
(node/prepare-new-config (node/prepare-new-config
+45 -40
View File
@@ -4,35 +4,36 @@
[clojure.string :as string] [clojure.string :as string]
[re-frame.core :as re-frame] [re-frame.core :as re-frame]
[status-im.chat.models :as models.chat] [status-im.chat.models :as models.chat]
[status-im.ethereum.json-rpc :as json-rpc]
[status-im2.navigation.events :as navigation]
[status-im.utils.fx :as fx]
[status-im.constants :as constants] [status-im.constants :as constants]
[status-im.ethereum.json-rpc :as json-rpc]
[status-im.i18n.i18n :as i18n] [status-im.i18n.i18n :as i18n]
[status-im2.contexts.activity-center.events :as activity-center])) [status-im2.contexts.activity-center.events :as activity-center]
[status-im2.navigation.events :as navigation]
[utils.re-frame :as rf]))
(fx/defn navigate-chat-updated (rf/defn navigate-chat-updated
{:events [:navigate-chat-updated]} {:events [:navigate-chat-updated]}
[cofx chat-id] [cofx chat-id]
(when (get-in cofx [:db :chats chat-id]) (when (get-in cofx [:db :chats chat-id])
(models.chat/navigate-to-chat cofx chat-id))) (models.chat/navigate-to-chat cofx chat-id)))
(fx/defn handle-chat-removed (rf/defn handle-chat-removed
{:events [:chat-removed]} {:events [:chat-removed]}
[cofx response] [cofx response]
(fx/merge cofx (rf/merge cofx
{:db (dissoc (:db cofx) :current-chat-id) {:db (dissoc (:db cofx) :current-chat-id)
:dispatch-n [[:sanitize-messages-and-process-response response] :dispatch-n [[:sanitize-messages-and-process-response response]
[:pop-to-root-tab :chat-stack]]} [:pop-to-root-tab :chat-stack]]}
(activity-center/notifications-fetch-unread-count))) (activity-center/notifications-fetch-unread-count)))
(fx/defn handle-chat-update (rf/defn handle-chat-update
{:events [:chat-updated]} {:events [:chat-updated]}
[_ response do-not-navigate?] [_ response do-not-navigate?]
{:dispatch-n [[:sanitize-messages-and-process-response response] {:dispatch-n [[:sanitize-messages-and-process-response response]
(when-not do-not-navigate? [:navigate-chat-updated (.-id (aget (.-chats response) 0))])]}) (when-not do-not-navigate?
[:navigate-chat-updated (.-id (aget (.-chats response) 0))])]})
(fx/defn remove-member (rf/defn remove-member
"Format group update message and sign membership" "Format group update message and sign membership"
{:events [:group-chats.ui/remove-member-pressed]} {:events [:group-chats.ui/remove-member-pressed]}
[_ chat-id member do-not-navigate?] [_ chat-id member do-not-navigate?]
@@ -41,7 +42,7 @@
:js-response true :js-response true
:on-success #(re-frame/dispatch [:chat-updated % do-not-navigate?])}]}) :on-success #(re-frame/dispatch [:chat-updated % do-not-navigate?])}]})
(fx/defn join-chat (rf/defn join-chat
{:events [:group-chats.ui/join-pressed]} {:events [:group-chats.ui/join-pressed]}
[_ chat-id] [_ chat-id]
{::json-rpc/call [{:method "wakuext_confirmJoiningGroup" {::json-rpc/call [{:method "wakuext_confirmJoiningGroup"
@@ -49,7 +50,7 @@
:js-response true :js-response true
:on-success #(re-frame/dispatch [:chat-updated %])}]}) :on-success #(re-frame/dispatch [:chat-updated %])}]})
(fx/defn create (rf/defn create
{:events [:group-chats.ui/create-pressed] {:events [:group-chats.ui/create-pressed]
:interceptors [(re-frame/inject-cofx :random-guid-generator)]} :interceptors [(re-frame/inject-cofx :random-guid-generator)]}
[{:keys [db] :as cofx} group-name] [{:keys [db] :as cofx} group-name]
@@ -59,7 +60,7 @@
:js-response true :js-response true
:on-success #(re-frame/dispatch [:chat-updated %])}]})) :on-success #(re-frame/dispatch [:chat-updated %])}]}))
(fx/defn create-from-link (rf/defn create-from-link
[cofx {:keys [chat-id invitation-admin chat-name]}] [cofx {:keys [chat-id invitation-admin chat-name]}]
(if (get-in cofx [:db :chats chat-id]) (if (get-in cofx [:db :chats chat-id])
{:dispatch [:chat.ui/navigate-to-chat chat-id]} {:dispatch [:chat.ui/navigate-to-chat chat-id]}
@@ -68,7 +69,7 @@
:js-response true :js-response true
:on-success #(re-frame/dispatch [:chat-updated %])}]})) :on-success #(re-frame/dispatch [:chat-updated %])}]}))
(fx/defn make-admin (rf/defn make-admin
{:events [:group-chats.ui/make-admin-pressed]} {:events [:group-chats.ui/make-admin-pressed]}
[_ chat-id member] [_ chat-id member]
{::json-rpc/call [{:method "wakuext_addAdminsToGroupChat" {::json-rpc/call [{:method "wakuext_addAdminsToGroupChat"
@@ -76,7 +77,7 @@
:js-response true :js-response true
:on-success #(re-frame/dispatch [:chat-updated %])}]}) :on-success #(re-frame/dispatch [:chat-updated %])}]})
(fx/defn add-members (rf/defn add-members
"Add members to a group chat" "Add members to a group chat"
{:events [:group-chats.ui/add-members-pressed]} {:events [:group-chats.ui/add-members-pressed]}
[{{:keys [current-chat-id selected-participants]} :db :as cofx}] [{{:keys [current-chat-id selected-participants]} :db :as cofx}]
@@ -85,7 +86,7 @@
:js-response true :js-response true
:on-success #(re-frame/dispatch [:chat-updated %])}]}) :on-success #(re-frame/dispatch [:chat-updated %])}]})
(fx/defn add-members-from-invitation (rf/defn add-members-from-invitation
"Add members to a group chat" "Add members to a group chat"
{:events [:group-chats.ui/add-members-from-invitation]} {:events [:group-chats.ui/add-members-from-invitation]}
[{{:keys [current-chat-id] :as db} :db :as cofx} id participant] [{{:keys [current-chat-id] :as db} :db :as cofx} id participant]
@@ -95,7 +96,7 @@
:js-response true :js-response true
:on-success #(re-frame/dispatch [:chat-updated %])}]}) :on-success #(re-frame/dispatch [:chat-updated %])}]})
(fx/defn leave (rf/defn leave
"Leave chat" "Leave chat"
{:events [:group-chats.ui/leave-chat-confirmed]} {:events [:group-chats.ui/leave-chat-confirmed]}
[{:keys [db] :as cofx} chat-id] [{:keys [db] :as cofx} chat-id]
@@ -104,21 +105,22 @@
:js-response true :js-response true
:on-success #(re-frame/dispatch [:chat-removed %])}]}) :on-success #(re-frame/dispatch [:chat-removed %])}]})
(fx/defn remove (rf/defn remove
"Remove chat" "Remove chat"
{:events [:group-chats.ui/remove-chat-confirmed]} {:events [:group-chats.ui/remove-chat-confirmed]}
[cofx chat-id] [cofx chat-id]
(fx/merge cofx (rf/merge cofx
(models.chat/deactivate-chat chat-id) (models.chat/deactivate-chat chat-id)
(navigation/pop-to-root-tab :chat-stack))) (navigation/pop-to-root-tab :chat-stack)))
(def not-blank? (def not-blank?
(complement string/blank?)) (complement string/blank?))
(defn- valid-name? [name] (defn- valid-name?
[name]
(spec/valid? not-blank? name)) (spec/valid? not-blank? name))
(fx/defn name-changed (rf/defn name-changed
"Save chat from edited profile" "Save chat from edited profile"
{:events [:group-chats.ui/name-changed]} {:events [:group-chats.ui/name-changed]}
[{:keys [db] :as cofx} chat-id new-name] [{:keys [db] :as cofx} chat-id new-name]
@@ -129,29 +131,29 @@
:js-response true :js-response true
:on-success #(re-frame/dispatch [:chat-updated %])}]})) :on-success #(re-frame/dispatch [:chat-updated %])}]}))
(fx/defn membership-retry (rf/defn membership-retry
{:events [:group-chats.ui/membership-retry]} {:events [:group-chats.ui/membership-retry]}
[{{:keys [current-chat-id] :as db} :db}] [{{:keys [current-chat-id] :as db} :db}]
{:db (assoc-in db [:chat/memberships current-chat-id :retry?] true)}) {:db (assoc-in db [:chat/memberships current-chat-id :retry?] true)})
(fx/defn membership-message (rf/defn membership-message
{:events [:group-chats.ui/update-membership-message]} {:events [:group-chats.ui/update-membership-message]}
[{{:keys [current-chat-id] :as db} :db} message] [{{:keys [current-chat-id] :as db} :db} message]
{:db (assoc-in db [:chat/memberships current-chat-id :message] message)}) {:db (assoc-in db [:chat/memberships current-chat-id :message] message)})
(fx/defn send-group-chat-membership-request (rf/defn send-group-chat-membership-request
"Send group chat membership request" "Send group chat membership request"
{:events [:send-group-chat-membership-request]} {:events [:send-group-chat-membership-request]}
[{{:keys [current-chat-id chats] :as db} :db :as cofx}] [{{:keys [current-chat-id chats] :as db} :db :as cofx}]
(let [{:keys [invitation-admin]} (get chats current-chat-id) (let [{:keys [invitation-admin]} (get chats current-chat-id)
message (get-in db [:chat/memberships current-chat-id :message])] message (get-in db [:chat/memberships current-chat-id :message])]
{:db (assoc-in db [:chat/memberships current-chat-id] nil) {:db (assoc-in db [:chat/memberships current-chat-id] nil)
::json-rpc/call [{:method "wakuext_sendGroupChatInvitationRequest" ::json-rpc/call [{:method "wakuext_sendGroupChatInvitationRequest"
:params [nil current-chat-id invitation-admin message] :params [nil current-chat-id invitation-admin message]
:js-response true :js-response true
:on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %])}]})) :on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %])}]}))
(fx/defn send-group-chat-membership-rejection (rf/defn send-group-chat-membership-rejection
"Send group chat membership rejection" "Send group chat membership rejection"
{:events [:send-group-chat-membership-rejection]} {:events [:send-group-chat-membership-rejection]}
[cofx invitation-id] [cofx invitation-id]
@@ -160,14 +162,17 @@
:js-response true :js-response true
:on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %])}]}) :on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %])}]})
(fx/defn handle-invitations (rf/defn handle-invitations
[{db :db} invitations] [{db :db} invitations]
{:db (update db :group-chat/invitations #(reduce (fn [acc {:keys [id] :as inv}] {:db (update db
(assoc acc id inv)) :group-chat/invitations
% #(reduce (fn [acc {:keys [id] :as inv}]
invitations))}) (assoc acc id inv))
%
invitations))})
(defn member-removed? [{:keys [membership-update-events]} pk] (defn member-removed?
[{:keys [membership-update-events]} pk]
(->> membership-update-events (->> membership-update-events
(filter #(contains? (set (:members %)) pk)) (filter #(contains? (set (:members %)) pk))
(sort-by :clockValue >) (sort-by :clockValue >)
@@ -175,41 +180,41 @@
:type :type
(= constants/invitation-state-removed))) (= constants/invitation-state-removed)))
(fx/defn deselect-contact (rf/defn deselect-contact
{:events [:deselect-contact]} {:events [:deselect-contact]}
[{:keys [db]} id] [{:keys [db]} id]
{:db (update db :group/selected-contacts disj id)}) {:db (update db :group/selected-contacts disj id)})
(fx/defn select-contact (rf/defn select-contact
{:events [:select-contact]} {:events [:select-contact]}
[{:keys [db]} id] [{:keys [db]} id]
{:db (update db :group/selected-contacts conj id)}) {:db (update db :group/selected-contacts conj id)})
(fx/defn deselect-participant (rf/defn deselect-participant
{:events [:deselect-participant]} {:events [:deselect-participant]}
[{:keys [db]} id] [{:keys [db]} id]
{:db (update db :selected-participants disj id)}) {:db (update db :selected-participants disj id)})
(fx/defn select-participant (rf/defn select-participant
{:events [:select-participant]} {:events [:select-participant]}
[{:keys [db]} id] [{:keys [db]} id]
{:db (update db :selected-participants conj id)}) {:db (update db :selected-participants conj id)})
(fx/defn add-participants-toggle-list (rf/defn add-participants-toggle-list
{:events [:group/add-participants-toggle-list]} {:events [:group/add-participants-toggle-list]}
[{db :db}] [{db :db}]
{:db (assoc db :selected-participants #{})}) {:db (assoc db :selected-participants #{})})
(fx/defn show-group-chat-profile (rf/defn show-group-chat-profile
{:events [:show-group-chat-profile]} {:events [:show-group-chat-profile]}
[{:keys [db] :as cofx} chat-id] [{:keys [db] :as cofx} chat-id]
(fx/merge cofx (rf/merge cofx
{:db (-> db {:db (-> db
(assoc :new-chat-name (get-in db [:chats chat-id :name])) (assoc :new-chat-name (get-in db [:chats chat-id :name]))
(assoc :current-chat-id chat-id))} (assoc :current-chat-id chat-id))}
(navigation/navigate-to-cofx :group-chat-profile nil))) (navigation/navigate-to-cofx :group-chat-profile nil)))
(fx/defn ui-leave-chat-pressed (rf/defn ui-leave-chat-pressed
{:events [:group-chats.ui/leave-chat-pressed]} {:events [:group-chats.ui/leave-chat-pressed]}
[{:keys [db]} chat-id] [{:keys [db]} chat-id]
(let [chat-name (get-in db [:chats chat-id :name])] (let [chat-name (get-in db [:chats chat-id :name])]
+41 -38
View File
@@ -1,69 +1,72 @@
(ns status-im.keycard.backup-key (ns status-im.keycard.backup-key
(:require [status-im.utils.fx :as fx] (:require [re-frame.core :as re-frame]
[re-frame.core :as re-frame]
[status-im.utils.utils :as utils]
[status-im.i18n.i18n :as i18n]
[status-im.ethereum.mnemonic :as mnemonic] [status-im.ethereum.mnemonic :as mnemonic]
[status-im.i18n.i18n :as i18n]
[status-im.keycard.common :as common] [status-im.keycard.common :as common]
[status-im.multiaccounts.recover.core :as multiaccounts.recover] [status-im.multiaccounts.recover.core :as multiaccounts.recover]
[status-im2.navigation.events :as navigation]
[status-im.signing.core :as signing.core] [status-im.signing.core :as signing.core]
[taoensso.timbre :as log])) [status-im.utils.utils :as utils]
[status-im2.navigation.events :as navigation]
[taoensso.timbre :as log]
[utils.re-frame :as rf]))
(fx/defn backup-card-pressed (rf/defn backup-card-pressed
{:events [:keycard-settings.ui/backup-card-pressed]} {:events [:keycard-settings.ui/backup-card-pressed]}
[{:keys [db] :as cofx} backup-type] [{:keys [db] :as cofx} backup-type]
(log/debug "[keycard] start backup") (log/debug "[keycard] start backup")
(fx/merge cofx (rf/merge cofx
{:db (-> db {:db (-> db
(assoc-in [:keycard :creating-backup?] backup-type))} (assoc-in [:keycard :creating-backup?] backup-type))}
(when (:multiaccount db) (when (:multiaccount db)
(navigation/change-tab :profile)) (navigation/change-tab :profile))
(navigation/navigate-to-cofx :seed-phrase nil))) (navigation/navigate-to-cofx :seed-phrase nil)))
(fx/defn recovery-card-pressed (rf/defn recovery-card-pressed
{:events [:keycard-settings.ui/recovery-card-pressed]} {:events [:keycard-settings.ui/recovery-card-pressed]}
[{:keys [db] :as cofx} show-warning] [{:keys [db] :as cofx} show-warning]
(fx/merge cofx (rf/merge cofx
{:db (-> db {:db (-> db
;setting pin-retry-counter is a workaround for the way the PIN view decides if it should accept PUK or PIN ;setting pin-retry-counter is a workaround for the way the PIN view
(assoc-in [:keycard :application-info :pin-retry-counter] 3) ;decides if it should accept PUK or PIN
(assoc-in [:keycard :factory-reset-card?] true) (assoc-in [:keycard :application-info :pin-retry-counter] 3)
(dissoc :popover/popover)) (assoc-in [:keycard :factory-reset-card?] true)
(dissoc :popover/popover))
:hide-popover nil} :hide-popover nil}
(signing.core/discard) (signing.core/discard)
(if show-warning (if show-warning
(utils/show-confirmation {:title (i18n/label :t/keycard-recover-title) (utils/show-confirmation
:content (i18n/label :t/keycard-recover-text) {:title (i18n/label :t/keycard-recover-title)
:confirm-button-text (i18n/label :t/yes) :content (i18n/label :t/keycard-recover-text)
:cancel-button-text (i18n/label :t/no) :confirm-button-text (i18n/label :t/yes)
:on-accept #(re-frame/dispatch [:keycard-settings.ui/backup-card-pressed :recovery-card]) :cancel-button-text (i18n/label :t/no)
:on-cancel #()}) :on-accept #(re-frame/dispatch [:keycard-settings.ui/backup-card-pressed
:recovery-card])
:on-cancel #()})
(backup-card-pressed :recovery-card)))) (backup-card-pressed :recovery-card))))
(fx/defn start-keycard-backup (rf/defn start-keycard-backup
{:events [::start-keycard-backup]} {:events [::start-keycard-backup]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
{::multiaccounts.recover/import-multiaccount {:passphrase (-> db {::multiaccounts.recover/import-multiaccount {:passphrase (-> db
:multiaccounts/key-storage :multiaccounts/key-storage
:seed-phrase :seed-phrase
mnemonic/sanitize-passphrase) mnemonic/sanitize-passphrase)
:password nil :password nil
:success-event ::create-backup-card}}) :success-event ::create-backup-card}})
(fx/defn create-backup-card (rf/defn create-backup-card
{:events [::create-backup-card]} {:events [::create-backup-card]}
[{:keys [db] :as cofx} root-data derived-data] [{:keys [db] :as cofx} root-data derived-data]
(fx/merge cofx (rf/merge cofx
{:db (-> db {:db (-> db
(update :intro-wizard (update :intro-wizard
assoc assoc
:root-key root-data :root-key root-data
:derived derived-data :derived derived-data
:recovering? true :recovering? true
:selected-storage-type :advanced) :selected-storage-type :advanced)
(assoc-in [:keycard :flow] :recovery) (assoc-in [:keycard :flow] :recovery)
(update :multiaccounts/key-storage dissoc :seed-phrase)) (update :multiaccounts/key-storage dissoc :seed-phrase))
:dismiss-keyboard nil} :dismiss-keyboard nil}
(common/listen-to-hardware-back-button) (common/listen-to-hardware-back-button)
(navigation/navigate-to-cofx :keycard-onboarding-intro nil))) (navigation/navigate-to-cofx :keycard-onboarding-intro nil)))
+87 -76
View File
@@ -1,71 +1,74 @@
(ns status-im.keycard.change-pin (ns status-im.keycard.change-pin
(:require [status-im.i18n.i18n :as i18n] (:require [status-im.i18n.i18n :as i18n]
[status-im2.navigation.events :as navigation]
[status-im.keycard.onboarding :as onboarding]
[status-im.utils.fx :as fx]
[taoensso.timbre :as log]
[status-im.keycard.common :as common] [status-im.keycard.common :as common]
[status-im.keycard.onboarding :as onboarding]
[status-im2.navigation.events :as navigation]
[taoensso.timbre :as log]
[utils.re-frame :as rf]
[utils.security.core :as security])) [utils.security.core :as security]))
(fx/defn change-credentials-pressed (rf/defn change-credentials-pressed
{:events [:keycard-settings.ui/change-credentials-pressed]} {:events [:keycard-settings.ui/change-credentials-pressed]}
[{:keys [db] :as cofx} changing] [{:keys [db] :as cofx} changing]
(fx/merge cofx (rf/merge cofx
{:db {:db
(assoc-in db [:keycard :pin] {:enter-step :current (assoc-in db
:current [] [:keycard :pin]
:puk [] {:enter-step :current
:original [] :current []
:confirmation [] :puk []
:puk-original [] :original []
:puk-confirmation [] :confirmation []
:status nil :puk-original []
:error-label nil :puk-confirmation []
:on-verified (case changing :status nil
:pin :keycard/proceed-to-change-pin :error-label nil
:puk :keycard/proceed-to-change-puk :on-verified (case changing
:pairing :keycard/proceed-to-change-pairing)})} :pin :keycard/proceed-to-change-pin
:puk :keycard/proceed-to-change-puk
:pairing :keycard/proceed-to-change-pairing)})}
(common/navigate-to-enter-pin-screen))) (common/navigate-to-enter-pin-screen)))
(fx/defn proceed-to-change-pin (rf/defn proceed-to-change-pin
{:events [:keycard/proceed-to-change-pin]} {:events [:keycard/proceed-to-change-pin]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(fx/merge cofx (rf/merge cofx
{:db (-> db {:db (-> db
(assoc-in [:keycard :pin :enter-step] :original) (assoc-in [:keycard :pin :enter-step] :original)
(assoc-in [:keycard :pin :status] nil))} (assoc-in [:keycard :pin :status] nil))}
(navigation/navigate-replace :enter-pin-settings nil))) (navigation/navigate-replace :enter-pin-settings nil)))
(fx/defn proceed-to-change-puk (rf/defn proceed-to-change-puk
{:events [:keycard/proceed-to-change-puk]} {:events [:keycard/proceed-to-change-puk]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(fx/merge cofx (rf/merge cofx
{:db (-> db {:db (-> db
(assoc-in [:keycard :pin :enter-step] :puk-original) (assoc-in [:keycard :pin :enter-step] :puk-original)
(assoc-in [:keycard :pin :status] nil))} (assoc-in [:keycard :pin :status] nil))}
(navigation/navigate-replace :enter-pin-settings nil))) (navigation/navigate-replace :enter-pin-settings nil)))
(fx/defn proceed-to-change-pairing (rf/defn proceed-to-change-pairing
{:events [:keycard/proceed-to-change-pairing]} {:events [:keycard/proceed-to-change-pairing]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(navigation/navigate-replace cofx :change-pairing-code nil)) (navigation/navigate-replace cofx :change-pairing-code nil))
(fx/defn discard-pin-change (rf/defn discard-pin-change
{:events [::on-cancel]} {:events [::on-cancel]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(fx/merge cofx (rf/merge cofx
(common/clear-pin) (common/clear-pin)
(common/hide-connection-sheet) (common/hide-connection-sheet)
(if (get-in db [:keycard :pin :puk-restore?]) (if (get-in db [:keycard :pin :puk-restore?])
(navigation/navigate-to-cofx :multiaccounts nil) (navigation/navigate-to-cofx :multiaccounts nil)
(navigation/set-stack-root :profile-stack [:my-profile :keycard-settings])))) (navigation/set-stack-root :profile-stack [:my-profile :keycard-settings]))))
(fx/defn change-pin (rf/defn change-pin
{:events [:keycard/change-pin]} {:events [:keycard/change-pin]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(let [setup-step (get-in db [:keycard :setup-step])] (let [setup-step (get-in db [:keycard :setup-step])]
(log/debug "[keycard] change-pin" (log/debug "[keycard] change-pin"
"setup-step" setup-step) "setup-step"
setup-step)
(if (= setup-step :pin) (if (= setup-step :pin)
(onboarding/load-preparing-screen cofx) (onboarding/load-preparing-screen cofx)
(common/show-connection-sheet (common/show-connection-sheet
@@ -78,15 +81,15 @@
(get-in db [:keycard :pin :original])) (get-in db [:keycard :pin :original]))
current-pin (common/vector->string current-pin (common/vector->string
(get-in db [:keycard :pin :current]))] (get-in db [:keycard :pin :current]))]
(fx/merge (rf/merge
cofx cofx
{:db (assoc-in db [:keycard :pin :status] :verifying) {:db (assoc-in db [:keycard :pin :status] :verifying)
:keycard/change-pin :keycard/change-pin
{:new-pin new-pin {:new-pin new-pin
:current-pin current-pin}})))})))) :current-pin current-pin}})))}))))
(fx/defn change-puk (rf/defn change-puk
{:events [:keycard/change-puk]} {:events [:keycard/change-puk]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(common/show-connection-sheet (common/show-connection-sheet
@@ -99,13 +102,13 @@
(get-in db [:keycard :pin :puk-original])) (get-in db [:keycard :pin :puk-original]))
pin (common/vector->string pin (common/vector->string
(get-in db [:keycard :pin :current]))] (get-in db [:keycard :pin :current]))]
(fx/merge (rf/merge
cofx cofx
{:db (assoc-in db [:keycard :pin :status] :verifying) {:db (assoc-in db [:keycard :pin :status] :verifying)
:keycard/change-puk {:puk puk :keycard/change-puk {:puk puk
:pin pin}})))})) :pin pin}})))}))
(fx/defn change-pairing (rf/defn change-pairing
{:events [:keycard/change-pairing]} {:events [:keycard/change-pairing]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(common/show-connection-sheet (common/show-connection-sheet
@@ -117,30 +120,32 @@
(let [pairing (get-in db [:keycard :pin :pairing-code]) (let [pairing (get-in db [:keycard :pin :pairing-code])
pin (common/vector->string pin (common/vector->string
(get-in db [:keycard :pin :current]))] (get-in db [:keycard :pin :current]))]
(fx/merge (rf/merge
cofx cofx
{:db (assoc-in db [:keycard :pin :status] :verifying) {:db (assoc-in db [:keycard :pin :status] :verifying)
:keycard/change-pairing {:pairing pairing :keycard/change-pairing {:pairing pairing
:pin pin}})))})) :pin pin}})))}))
(fx/defn change-pairing-code (rf/defn change-pairing-code
{:events [:keycard/change-pairing-code]} {:events [:keycard/change-pairing-code]}
[{:keys [db] :as cofx} pairing] [{:keys [db] :as cofx} pairing]
(fx/merge (rf/merge
cofx cofx
{:db (assoc-in db [:keycard :pin :pairing-code] (security/unmask pairing))} {:db (assoc-in db [:keycard :pin :pairing-code] (security/unmask pairing))}
(change-pairing))) (change-pairing)))
(fx/defn on-change-pin-success (rf/defn on-change-pin-success
{:events [:keycard.callback/on-change-pin-success]} {:events [:keycard.callback/on-change-pin-success]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(let [pin (get-in db [:keycard :pin :original]) (let [pin (get-in db [:keycard :pin :original])
puk-restore? (get-in db [:keycard :pin :puk-restore?])] puk-restore? (get-in db [:keycard :pin :puk-restore?])]
(fx/merge cofx (rf/merge cofx
{:db (assoc-in db [:keycard :pin] {:status nil {:db (assoc-in db
:login pin [:keycard :pin]
:confirmation [] {:status nil
:error-label nil}) :login pin
:confirmation []
:error-label nil})
:utils/show-popup {:title "" :utils/show-popup {:title ""
:content (i18n/label :t/pin-changed)}} :content (i18n/label :t/pin-changed)}}
(common/hide-connection-sheet) (common/hide-connection-sheet)
@@ -150,55 +155,61 @@
(when (:multiaccounts/login db) (when (:multiaccounts/login db)
(common/get-keys-from-keycard))))) (common/get-keys-from-keycard)))))
(fx/defn on-change-puk-success (rf/defn on-change-puk-success
{:events [:keycard.callback/on-change-puk-success]} {:events [:keycard.callback/on-change-puk-success]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(fx/merge cofx (rf/merge cofx
{:db (assoc-in db [:keycard :pin] {:status nil {:db (assoc-in db
:puk-original [] [:keycard :pin]
:puk-confirmation [] {:status nil
:error-label nil}) :puk-original []
:puk-confirmation []
:error-label nil})
:utils/show-popup {:title "" :utils/show-popup {:title ""
:content (i18n/label :t/puk-changed)}} :content (i18n/label :t/puk-changed)}}
(common/hide-connection-sheet) (common/hide-connection-sheet)
(navigation/set-stack-root :profile-stack [:my-profile :keycard-settings]))) (navigation/set-stack-root :profile-stack [:my-profile :keycard-settings])))
(fx/defn on-change-pairing-success (rf/defn on-change-pairing-success
{:events [:keycard.callback/on-change-pairing-success]} {:events [:keycard.callback/on-change-pairing-success]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(fx/merge cofx (rf/merge cofx
{:db (assoc-in db [:keycard :pin] {:status nil {:db (assoc-in db
:pairing-code nil [:keycard :pin]
:error-label nil}) {:status nil
:pairing-code nil
:error-label nil})
:utils/show-popup {:title "" :utils/show-popup {:title ""
:content (i18n/label :t/pairing-changed)}} :content (i18n/label :t/pairing-changed)}}
(common/hide-connection-sheet) (common/hide-connection-sheet)
(navigation/set-stack-root :profile-stack [:my-profile :keycard-settings]))) (navigation/set-stack-root :profile-stack [:my-profile :keycard-settings])))
(fx/defn on-change-pin-error (rf/defn on-change-pin-error
{:events [:keycard.callback/on-change-pin-error]} {:events [:keycard.callback/on-change-pin-error]}
[{:keys [db] :as cofx} error] [{:keys [db] :as cofx} error]
(log/debug "[keycard] change pin error" error) (log/debug "[keycard] change pin error" error)
(let [tag-was-lost? (common/tag-lost? (:error error)) (let [tag-was-lost? (common/tag-lost? (:error error))
pin-retries (common/pin-retries (:error error))] pin-retries (common/pin-retries (:error error))]
(fx/merge cofx (rf/merge
(if tag-was-lost? cofx
(fx/merge cofx (if tag-was-lost?
{:db (assoc-in db [:keycard :pin :status] nil)} (rf/merge cofx
(common/set-on-card-connected :keycard/change-pin)) {:db (assoc-in db [:keycard :pin :status] nil)}
(if-not (nil? pin-retries) (common/set-on-card-connected :keycard/change-pin))
(fx/merge cofx (if-not (nil? pin-retries)
{:db (-> db (rf/merge cofx
(assoc-in [:keycard :application-info :pin-retry-counter] pin-retries) {:db (-> db
(update-in [:keycard :pin] assoc (assoc-in [:keycard :application-info :pin-retry-counter] pin-retries)
:status :error (update-in [:keycard :pin]
:enter-step :current assoc
:puk [] :status :error
:current [] :enter-step :current
:original [] :puk []
:confirmation [] :current []
:sign [] :original []
:error-label :t/pin-mismatch))} :confirmation []
(when (zero? pin-retries) (common/frozen-keycard-popup)) :sign []
(navigation/navigate-to-cofx :enter-pin-settings nil)) :error-label :t/pin-mismatch))}
(common/show-wrong-keycard-alert)))))) (when (zero? pin-retries) (common/frozen-keycard-popup))
(navigation/navigate-to-cofx :enter-pin-settings nil))
(common/show-wrong-keycard-alert))))))
+147 -129
View File
@@ -1,28 +1,30 @@
(ns status-im.keycard.common (ns status-im.keycard.common
(:require [clojure.string :as string] (:require [clojure.string :as string]
[re-frame.core :as re-frame] [re-frame.core :as re-frame]
[status-im.ethereum.core :as ethereum]
[status-im.keycard.nfc :as nfc]
[status-im.i18n.i18n :as i18n]
[status-im2.navigation.events :as navigation]
[status-im.ui.screens.keycard.keycard-interaction :as keycard-sheet]
[status-im.utils.fx :as fx]
[status-im.utils.keychain.core :as keychain]
[status-im.utils.types :as types]
[taoensso.timbre :as log]
[status-im.utils.datetime :as utils.datetime]
[status-im.bottom-sheet.core :as bottom-sheet] [status-im.bottom-sheet.core :as bottom-sheet]
[status-im.ethereum.core :as ethereum]
[status-im.i18n.i18n :as i18n]
[status-im.keycard.nfc :as nfc]
[status-im.popover.core :as popover]
[status-im.ui.screens.keycard.keycard-interaction :as keycard-sheet]
[status-im.utils.datetime :as utils.datetime]
[status-im.utils.keychain.core :as keychain]
[status-im.utils.platform :as platform] [status-im.utils.platform :as platform]
[status-im.popover.core :as popover])) [status-im.utils.types :as types]
[status-im2.navigation.events :as navigation]
[taoensso.timbre :as log]
[utils.re-frame :as rf]))
(def default-pin "000000") (def default-pin "000000")
(def pin-mismatch-error #"Unexpected error SW, 0x63C(\d+)|wrongPIN\(retryCounter: (\d+)\)") (def pin-mismatch-error #"Unexpected error SW, 0x63C(\d+)|wrongPIN\(retryCounter: (\d+)\)")
(defn pin-retries [error] (defn pin-retries
(when-let [matched-error (re-matches pin-mismatch-error error)] (js/parseInt (second (filter some? matched-error))))) [error]
(when-let [matched-error (re-matches pin-mismatch-error error)]
(js/parseInt (second (filter some? matched-error)))))
(fx/defn dispatch-event (rf/defn dispatch-event
[_ event] [_ event]
{:dispatch [event]}) {:dispatch [event]})
@@ -56,7 +58,8 @@
(zero? free-pairing-slots)) (zero? free-pairing-slots))
:no-pairing-slots)) :no-pairing-slots))
(defn tag-lost? [error] (defn tag-lost?
[error]
(or (or
(= error "Tag was lost.") (= error "Tag was lost.")
(= error "NFCError:100") (= error "NFCError:100")
@@ -94,24 +97,24 @@
(fn [supported?] (fn [supported?]
(nfc/set-nfc-supported? supported?))) (nfc/set-nfc-supported? supported?)))
(fx/defn listen-to-hardware-back-button (rf/defn listen-to-hardware-back-button
[{:keys [db]}] [{:keys [db]}]
(when-not (get-in db [:keycard :back-button-listener]) (when-not (get-in db [:keycard :back-button-listener])
{:keycard/listen-to-hardware-back-button nil})) {:keycard/listen-to-hardware-back-button nil}))
(fx/defn remove-listener-to-hardware-back-button (rf/defn remove-listener-to-hardware-back-button
[{:keys [db]}] [{:keys [db]}]
(when-let [listener (get-in db [:keycard :back-button-listener])] (when-let [listener (get-in db [:keycard :back-button-listener])]
{:keycard/remove-listener-to-hardware-back-button listener})) {:keycard/remove-listener-to-hardware-back-button listener}))
(fx/defn set-on-card-connected (rf/defn set-on-card-connected
[{:keys [db]} on-connect] [{:keys [db]} on-connect]
(log/debug "[keycard] set-on-card-connected" on-connect) (log/debug "[keycard] set-on-card-connected" on-connect)
{:db (-> db {:db (-> db
(assoc-in [:keycard :on-card-connected] on-connect) (assoc-in [:keycard :on-card-connected] on-connect)
(assoc-in [:keycard :last-on-card-connected] nil))}) (assoc-in [:keycard :last-on-card-connected] nil))})
(fx/defn stash-on-card-connected (rf/defn stash-on-card-connected
[{:keys [db]}] [{:keys [db]}]
(let [on-connect (get-in db [:keycard :on-card-connected])] (let [on-connect (get-in db [:keycard :on-card-connected])]
(log/debug "[keycard] stash-on-card-connected" on-connect) (log/debug "[keycard] stash-on-card-connected" on-connect)
@@ -119,7 +122,7 @@
(assoc-in [:keycard :last-on-card-connected] on-connect) (assoc-in [:keycard :last-on-card-connected] on-connect)
(assoc-in [:keycard :on-card-connected] nil))})) (assoc-in [:keycard :on-card-connected] nil))}))
(fx/defn restore-on-card-connected (rf/defn restore-on-card-connected
[{:keys [db]}] [{:keys [db]}]
(let [on-connect (or (let [on-connect (or
(get-in db [:keycard :on-card-connected]) (get-in db [:keycard :on-card-connected])
@@ -129,21 +132,21 @@
(assoc-in [:keycard :on-card-connected] on-connect) (assoc-in [:keycard :on-card-connected] on-connect)
(assoc-in [:keycard :last-on-card-connect] nil))})) (assoc-in [:keycard :last-on-card-connect] nil))}))
(fx/defn clear-on-card-connected (rf/defn clear-on-card-connected
[{:keys [db]}] [{:keys [db]}]
(log/debug "[keycard] clear-on-card-connected") (log/debug "[keycard] clear-on-card-connected")
{:db (-> db {:db (-> db
(assoc-in [:keycard :on-card-connected] nil) (assoc-in [:keycard :on-card-connected] nil)
(assoc-in [:keycard :last-on-card-connected] nil))}) (assoc-in [:keycard :last-on-card-connected] nil))})
(fx/defn set-on-card-read (rf/defn set-on-card-read
[{:keys [db]} on-connect] [{:keys [db]} on-connect]
(log/debug "[keycard] set-on-card-read" on-connect) (log/debug "[keycard] set-on-card-read" on-connect)
{:db (-> db {:db (-> db
(assoc-in [:keycard :on-card-read] on-connect) (assoc-in [:keycard :on-card-read] on-connect)
(assoc-in [:keycard :last-on-card-read] nil))}) (assoc-in [:keycard :last-on-card-read] nil))})
(fx/defn stash-on-card-read (rf/defn stash-on-card-read
[{:keys [db]}] [{:keys [db]}]
(let [on-connect (get-in db [:keycard :on-card-read])] (let [on-connect (get-in db [:keycard :on-card-read])]
(log/debug "[keycard] stash-on-card-read" on-connect) (log/debug "[keycard] stash-on-card-read" on-connect)
@@ -151,7 +154,7 @@
(assoc-in [:keycard :last-on-card-read] on-connect) (assoc-in [:keycard :last-on-card-read] on-connect)
(assoc-in [:keycard :on-card-read] nil))})) (assoc-in [:keycard :on-card-read] nil))}))
(fx/defn restore-on-card-read (rf/defn restore-on-card-read
[{:keys [db]}] [{:keys [db]}]
(let [on-connect (or (let [on-connect (or
(get-in db [:keycard :on-card-read]) (get-in db [:keycard :on-card-read])
@@ -161,14 +164,15 @@
(assoc-in [:keycard :on-card-read] on-connect) (assoc-in [:keycard :on-card-read] on-connect)
(assoc-in [:keycard :last-on-card-connect] nil))})) (assoc-in [:keycard :last-on-card-connect] nil))}))
(fx/defn clear-on-card-read (rf/defn clear-on-card-read
[{:keys [db]}] [{:keys [db]}]
(log/debug "[keycard] clear-on-card-read") (log/debug "[keycard] clear-on-card-read")
{:db (-> db {:db (-> db
(assoc-in [:keycard :on-card-read] nil) (assoc-in [:keycard :on-card-read] nil)
(assoc-in [:keycard :last-on-card-read] nil))}) (assoc-in [:keycard :last-on-card-read] nil))})
(defn keycard-sheet-content [on-cancel connected? params] (defn keycard-sheet-content
[on-cancel connected? params]
(fn [] (fn []
[keycard-sheet/connect-keycard [keycard-sheet/connect-keycard
{:on-cancel #(re-frame/dispatch on-cancel) {:on-cancel #(re-frame/dispatch on-cancel)
@@ -177,17 +181,19 @@
:on-connect ::on-card-connected :on-connect ::on-card-connected
:on-disconnect ::on-card-disconnected}])) :on-disconnect ::on-card-disconnected}]))
(fx/defn show-connection-sheet-component (rf/defn show-connection-sheet-component
[{:keys [db] :as cofx} {:keys [on-card-connected on-card-read handler] [{:keys [db] :as cofx}
{:keys [on-cancel] {:keys [on-card-connected on-card-read handler]
:or {on-cancel [::cancel-sheet-confirm]}} {:keys [on-cancel]
:sheet-options}] :or {on-cancel [::cancel-sheet-confirm]}}
:sheet-options}]
(assert (keyword? on-card-connected)) (assert (keyword? on-card-connected))
(assert (fn? handler)) (assert (fn? handler))
(let [connected? (get-in db [:keycard :card-connected?])] (let [connected? (get-in db [:keycard :card-connected?])]
(log/debug "[keycard] show-sheet-with-connection-check" (log/debug "[keycard] show-sheet-with-connection-check"
"card-connected?" connected?) "card-connected?"
(fx/merge connected?)
(rf/merge
cofx cofx
{:dismiss-keyboard true} {:dismiss-keyboard true}
(bottom-sheet/show-bottom-sheet (bottom-sheet/show-bottom-sheet
@@ -205,7 +211,7 @@
(when connected? (when connected?
handler)))) handler))))
(fx/defn show-connection-sheet (rf/defn show-connection-sheet
[{:keys [db] :as cofx} args] [{:keys [db] :as cofx} args]
(let [nfc-running? (get-in db [:keycard :nfc-running?])] (let [nfc-running? (get-in db [:keycard :nfc-running?])]
(log/debug "show connection; already running?" nfc-running?) (log/debug "show connection; already running?" nfc-running?)
@@ -213,59 +219,59 @@
(show-connection-sheet-component cofx args) (show-connection-sheet-component cofx args)
{:keycard/start-nfc-and-show-connection-sheet args}))) {:keycard/start-nfc-and-show-connection-sheet args})))
(fx/defn on-nfc-ready-for-sheet (rf/defn on-nfc-ready-for-sheet
{:events [:keycard.callback/show-connection-sheet]} {:events [:keycard.callback/show-connection-sheet]}
[cofx args] [cofx args]
(log/debug "on-nfc-ready-for-sheet") (log/debug "on-nfc-ready-for-sheet")
(show-connection-sheet-component cofx args)) (show-connection-sheet-component cofx args))
(fx/defn hide-connection-sheet-component (rf/defn hide-connection-sheet-component
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(fx/merge cofx (rf/merge cofx
{:db (assoc-in db [:keycard :card-read-in-progress?] false)} {:db (assoc-in db [:keycard :card-read-in-progress?] false)}
(restore-on-card-connected) (restore-on-card-connected)
(restore-on-card-read) (restore-on-card-read)
(bottom-sheet/hide-bottom-sheet))) (bottom-sheet/hide-bottom-sheet)))
(fx/defn hide-connection-sheet (rf/defn hide-connection-sheet
[cofx] [cofx]
(log/debug "hide-connection-sheet") (log/debug "hide-connection-sheet")
{:keycard/stop-nfc-and-hide-connection-sheet nil}) {:keycard/stop-nfc-and-hide-connection-sheet nil})
(fx/defn on-nfc-ready-to-close-sheet (rf/defn on-nfc-ready-to-close-sheet
{:events [:keycard.callback/hide-connection-sheet]} {:events [:keycard.callback/hide-connection-sheet]}
[cofx] [cofx]
(log/debug "on-nfc-ready-to-close-sheet") (log/debug "on-nfc-ready-to-close-sheet")
(hide-connection-sheet-component cofx)) (hide-connection-sheet-component cofx))
(fx/defn clear-pin (rf/defn clear-pin
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(fx/merge (rf/merge
cofx cofx
{:db (assoc-in db {:db (assoc-in db
[:keycard :pin] [:keycard :pin]
{:status nil {:status nil
:login (get-in db [:keycard :pin :original]) :login (get-in db [:keycard :pin :original])
:export-key [] :export-key []
:sign [] :sign []
:puk [] :puk []
:current [] :current []
:original [] :original []
:confirmation [] :confirmation []
:error-label nil :error-label nil
:on-verified (get-in db [:keycard :pin :on-verified]) :on-verified (get-in db [:keycard :pin :on-verified])
:on-verified-failure (get-in db [:keycard :pin :on-verified])})})) :on-verified-failure (get-in db [:keycard :pin :on-verified])})}))
(fx/defn cancel-sheet-confirm (rf/defn cancel-sheet-confirm
{:events [::cancel-sheet-confirm {:events [::cancel-sheet-confirm
:keycard/back-button-pressed]} :keycard/back-button-pressed]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(when-not (get-in db [:keycard :card-connected?]) (when-not (get-in db [:keycard :card-connected?])
(fx/merge cofx (rf/merge cofx
(hide-connection-sheet) (hide-connection-sheet)
(clear-pin)))) (clear-pin))))
(fx/defn cancel-sheet (rf/defn cancel-sheet
{:events [::cancel-sheet]} {:events [::cancel-sheet]}
[_] [_]
{:ui/show-confirmation {:title (i18n/label :t/keycard-cancel-setup-title) {:ui/show-confirmation {:title (i18n/label :t/keycard-cancel-setup-title)
@@ -275,7 +281,7 @@
:on-accept #(re-frame/dispatch [::cancel-sheet-confirm]) :on-accept #(re-frame/dispatch [::cancel-sheet-confirm])
:on-cancel #()}}) :on-cancel #()}})
(fx/defn on-add-listener-to-hardware-back-button (rf/defn on-add-listener-to-hardware-back-button
"Adds listener to hardware back button on Android. "Adds listener to hardware back button on Android.
During keycard setup we show user a warning that setup will be cancelled During keycard setup we show user a warning that setup will be cancelled
when back button pressed. This prevents user from going back during setup when back button pressed. This prevents user from going back during setup
@@ -284,120 +290,126 @@
[{:keys [db]} listener] [{:keys [db]} listener]
{:db (assoc-in db [:keycard :back-button-listener] listener)}) {:db (assoc-in db [:keycard :back-button-listener] listener)})
(fx/defn show-wrong-keycard-alert (rf/defn show-wrong-keycard-alert
[_] [_]
(log/debug "show-wrong-keycard-alert") (log/debug "show-wrong-keycard-alert")
{:utils/show-popup {:title (i18n/label :t/wrong-card) {:utils/show-popup {:title (i18n/label :t/wrong-card)
:content (i18n/label :t/wrong-card-text)}}) :content (i18n/label :t/wrong-card-text)}})
(fx/defn unauthorized-operation (rf/defn unauthorized-operation
[cofx] [cofx]
(fx/merge cofx (rf/merge cofx
{:utils/show-popup {:title "" {:utils/show-popup {:title ""
:content (i18n/label :t/keycard-unauthorized-operation)}} :content (i18n/label :t/keycard-unauthorized-operation)}}
(clear-on-card-connected) (clear-on-card-connected)
(navigation/set-stack-root :profile-stack [:my-profile :keycard-settings]))) (navigation/set-stack-root :profile-stack [:my-profile :keycard-settings])))
(fx/defn navigate-to-enter-pin-screen (rf/defn navigate-to-enter-pin-screen
{:events [:keycard/navigate-to-enter-pin-screen]} {:events [:keycard/navigate-to-enter-pin-screen]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(fx/merge cofx (rf/merge cofx
{:db (assoc-in db [:keycard :pin :current] [])} {:db (assoc-in db [:keycard :pin :current] [])}
(navigation/navigate-to-cofx :enter-pin-settings nil))) (navigation/navigate-to-cofx :enter-pin-settings nil)))
(defn- tag-lost-exception? [code error] (defn- tag-lost-exception?
[code error]
(or (or
(= code "android.nfc.TagLostException") (= code "android.nfc.TagLostException")
(= error "Tag was lost.") (= error "Tag was lost.")
(= error "NFCError:100"))) (= error "NFCError:100")))
(fx/defn process-error [{:keys [db]} code error] (rf/defn process-error
[{:keys [db]} code error]
(when-not (tag-lost-exception? code error) (when-not (tag-lost-exception? code error)
{:db (assoc-in db [:keycard :setup-step] :error)})) {:db (assoc-in db [:keycard :setup-step] :error)}))
(fx/defn get-keys-from-keycard (rf/defn get-keys-from-keycard
[{:keys [db]}] [{:keys [db]}]
(let [key-uid (get-in db [:multiaccounts/login :key-uid]) (let [key-uid (get-in db [:multiaccounts/login :key-uid])
pin (string/join (get-in db [:keycard :pin :login]))] pin (string/join (get-in db [:keycard :pin :login]))]
(log/debug "[keycard] get-keys-from-keycard" (log/debug "[keycard] get-keys-from-keycard"
", not empty pin:" (boolean (seq pin))) ", not empty pin:"
(boolean (seq pin)))
(when (seq pin) (when (seq pin)
{:db (assoc-in db [:keycard :pin :status] :verifying) {:db (assoc-in db [:keycard :pin :status] :verifying)
:keycard/get-keys {:pin pin}}))) :keycard/get-keys {:pin pin}})))
(fx/defn on-get-keys-success (rf/defn on-get-keys-success
{:events [:keycard.callback/on-get-keys-success]} {:events [:keycard.callback/on-get-keys-success]}
[{:keys [db] :as cofx} data] [{:keys [db] :as cofx} data]
(let [{:keys [key-uid encryption-public-key whisper-private-key] (let [{:keys [key-uid encryption-public-key whisper-private-key]
:as account-data} (js->clj data :keywordize-keys true) :as account-data}
(js->clj data :keywordize-keys true)
{:keys [identicon name]} (get-in db [:multiaccounts/multiaccounts key-uid]) {:keys [identicon name]} (get-in db [:multiaccounts/multiaccounts key-uid])
key-uid (get-in db [:keycard :application-info :key-uid]) key-uid (get-in db [:keycard :application-info :key-uid])
multiaccount-data (types/clj->json {:name name multiaccount-data (types/clj->json {:name name
:key-uid key-uid :key-uid key-uid
:identicon identicon}) :identicon identicon})
save-keys? (get-in db [:multiaccounts/login :save-password?])] save-keys? (get-in db [:multiaccounts/login :save-password?])]
(fx/merge cofx (rf/merge cofx
{:db {:db
(-> db (-> db
(assoc-in [:keycard :pin :status] nil) (assoc-in [:keycard :pin :status] nil)
(assoc-in [:keycard :pin :login] []) (assoc-in [:keycard :pin :login] [])
(update-in [:keycard :application-info] assoc (update-in [:keycard :application-info]
assoc
:puk-retry-counter 5 :puk-retry-counter 5
:pin-retry-counter 3) :pin-retry-counter 3)
(assoc-in [:keycard :multiaccount] (assoc-in [:keycard :multiaccount]
(update account-data :whisper-public-key ethereum/normalized-hex)) (update account-data :whisper-public-key ethereum/normalized-hex))
(assoc-in [:keycard :flow] nil) (assoc-in [:keycard :flow] nil)
(update :multiaccounts/login assoc (update :multiaccounts/login assoc
:password encryption-public-key :password encryption-public-key
:key-uid key-uid :key-uid key-uid
:identicon identicon :identicon identicon
:name name)) :name name))
:keycard/login-with-keycard {:multiaccount-data multiaccount-data :keycard/login-with-keycard {:multiaccount-data multiaccount-data
:password encryption-public-key :password encryption-public-key
:chat-key whisper-private-key :chat-key whisper-private-key
:key-uid key-uid}} :key-uid key-uid}}
(when save-keys? (when save-keys?
(keychain/save-keycard-keys key-uid encryption-public-key whisper-private-key)) (keychain/save-keycard-keys key-uid encryption-public-key whisper-private-key))
(clear-on-card-connected) (clear-on-card-connected)
(clear-on-card-read) (clear-on-card-read)
(hide-connection-sheet)))) (hide-connection-sheet))))
(fx/defn blocked-or-frozen-keycard-popup (rf/defn blocked-or-frozen-keycard-popup
[{:keys [db] :as cofx} card-status] [{:keys [db] :as cofx} card-status]
(fx/merge (rf/merge
cofx cofx
{:db (assoc-in db [:keycard :pin :status] card-status)} {:db (assoc-in db [:keycard :pin :status] card-status)}
(hide-connection-sheet) (hide-connection-sheet)
; do not try to display the popover if it is already open or ; do not try to display the popover if it is already open or
; we are in the login interface (which has a different handling) ; we are in the login interface (which has a different handling)
(when-not (or (:multiaccounts/login db) (:popover/popover db)) (when-not (or (:multiaccounts/login db) (:popover/popover db))
(popover/show-popover {:view card-status})))) (popover/show-popover {:view card-status}))))
(fx/defn blocked-keycard-popup (rf/defn blocked-keycard-popup
[cofx] [cofx]
(blocked-or-frozen-keycard-popup cofx :blocked-card)) (blocked-or-frozen-keycard-popup cofx :blocked-card))
(fx/defn frozen-keycard-popup (rf/defn frozen-keycard-popup
[cofx] [cofx]
(blocked-or-frozen-keycard-popup cofx :frozen-card)) (blocked-or-frozen-keycard-popup cofx :frozen-card))
(fx/defn on-get-keys-error (rf/defn on-get-keys-error
{:events [:keycard.callback/on-get-keys-error]} {:events [:keycard.callback/on-get-keys-error]}
[{:keys [db] :as cofx} error] [{:keys [db] :as cofx} error]
(log/debug "[keycard] get keys error: " error) (log/debug "[keycard] get keys error: " error)
(let [tag-was-lost? (tag-lost? (:error error)) (let [tag-was-lost? (tag-lost? (:error error))
key-uid (get-in db [:keycard :application-info :key-uid]) key-uid (get-in db [:keycard :application-info :key-uid])
flow (get-in db [:keycard :flow]) flow (get-in db [:keycard :flow])
pin-retries-count (pin-retries (:error error))] pin-retries-count (pin-retries (:error error))]
(if tag-was-lost? (if tag-was-lost?
{:db (assoc-in db [:keycard :pin :status] nil)} {:db (assoc-in db [:keycard :pin :status] nil)}
(if-not (nil? pin-retries-count) (if-not (nil? pin-retries-count)
(fx/merge (rf/merge
cofx cofx
{:db (-> db {:db (-> db
(assoc-in [:keycard :application-info :pin-retry-counter] pin-retries-count) (assoc-in [:keycard :application-info :pin-retry-counter] pin-retries-count)
(update-in [:keycard :pin] assoc (update-in [:keycard :pin]
assoc
:status :error :status :error
:login [] :login []
:import-multiaccount [] :import-multiaccount []
@@ -406,42 +418,45 @@
(when (zero? pin-retries-count) (frozen-keycard-popup))) (when (zero? pin-retries-count) (frozen-keycard-popup)))
(show-wrong-keycard-alert))))) (show-wrong-keycard-alert)))))
(fx/defn factory-reset (rf/defn factory-reset
{:events [:keycard/factory-reset]} {:events [:keycard/factory-reset]}
[{:keys [db]} on-card-read] [{:keys [db]} on-card-read]
(log/debug "[keycard] factory-reset") (log/debug "[keycard] factory-reset")
{:db (update db :keycard dissoc :factory-reset-card?) {:db (update db :keycard dissoc :factory-reset-card?)
:keycard/factory-reset {:on-success on-card-read}}) :keycard/factory-reset {:on-success on-card-read}})
;; Get application info ;; Get application info
(fx/defn update-pairings (rf/defn update-pairings
[{:keys [db]} instance-uid pairing] [{:keys [db]} instance-uid pairing]
(let [paired-on (utils.datetime/timestamp) (let [paired-on (utils.datetime/timestamp)
pairings (-> (get-in db [:keycard :pairings]) pairings (-> (get-in db [:keycard :pairings])
(assoc instance-uid {:pairing pairing :paired-on paired-on}))] (assoc instance-uid {:pairing pairing :paired-on paired-on}))]
{:keycard/persist-pairings pairings {:keycard/persist-pairings pairings
:db (assoc-in db [:keycard :pairings] pairings)})) :db (assoc-in db [:keycard :pairings] pairings)}))
(fx/defn get-application-info (rf/defn get-application-info
{:events [:keycard/get-application-info]} {:events [:keycard/get-application-info]}
[{:keys [db]} on-card-read] [{:keys [db]} on-card-read]
(log/debug "[keycard] get-application-info") (log/debug "[keycard] get-application-info")
{:keycard/get-application-info {:on-success on-card-read}}) {:keycard/get-application-info {:on-success on-card-read}})
(fx/defn on-get-application-info-success (rf/defn on-get-application-info-success
{:events [:keycard.callback/on-get-application-info-success]} {:events [:keycard.callback/on-get-application-info-success]}
[{:keys [db] :as cofx} info on-success] [{:keys [db] :as cofx} info on-success]
(let [{:keys [pin-retry-counter puk-retry-counter instance-uid new-pairing]} info (let [{:keys [pin-retry-counter puk-retry-counter instance-uid new-pairing]} info
view-id (:view-id db) view-id (:view-id db)
{:keys [on-card-read]} (:keycard db) {:keys [on-card-read]} (:keycard db)
on-success' (or on-success on-card-read) on-success' (or on-success
enter-step (get-in db [:keycard :pin :enter-step])] on-card-read)
enter-step (get-in db
[:keycard :pin
:enter-step])]
(log/debug "[keycard] on-get-application-info-success" (log/debug "[keycard] on-get-application-info-success"
"on-success" on-success' "on-success" on-success'
"pin-retry-counter" pin-retry-counter "pin-retry-counter" pin-retry-counter
"puk-retry-counter" puk-retry-counter) "puk-retry-counter" puk-retry-counter)
(fx/merge (rf/merge
cofx cofx
{:db (-> db {:db (-> db
(assoc-in [:keycard :pin :enter-step] enter-step) (assoc-in [:keycard :pin :enter-step] enter-step)
@@ -461,7 +476,7 @@
(when on-success' (when on-success'
(dispatch-event cofx on-success'))))))) (dispatch-event cofx on-success')))))))
(fx/defn on-get-application-info-error (rf/defn on-get-application-info-error
{:events [:keycard.callback/on-get-application-info-error]} {:events [:keycard.callback/on-get-application-info-error]}
[{:keys [db] :as cofx} error] [{:keys [db] :as cofx} error]
(let [on-card-read (get-in db [:keycard :on-card-read]) (let [on-card-read (get-in db [:keycard :on-card-read])
@@ -475,10 +490,10 @@
last-on-card-connected) last-on-card-connected)
(when-not tag-was-lost? (when-not tag-was-lost?
(if login? (if login?
(fx/merge cofx (rf/merge cofx
(clear-on-card-read) (clear-on-card-read)
(navigation/navigate-to-cofx :not-keycard nil)) (navigation/navigate-to-cofx :not-keycard nil))
(fx/merge cofx (rf/merge cofx
{:db (assoc-in db [:keycard :application-info-error] error)} {:db (assoc-in db [:keycard :application-info-error] error)}
(when (contains? (when (contains?
@@ -489,7 +504,7 @@
(when on-card-read (when on-card-read
(dispatch-event on-card-read))))))) (dispatch-event on-card-read)))))))
(fx/defn on-card-connected (rf/defn on-card-connected
{:events [::on-card-connected]} {:events [::on-card-connected]}
[{:keys [db] :as cofx} _] [{:keys [db] :as cofx} _]
(let [instance-uid (get-in db [:keycard :application-info :instance-uid]) (let [instance-uid (get-in db [:keycard :application-info :instance-uid])
@@ -500,9 +515,9 @@
should-read-instance-uid? :keycard/get-application-info should-read-instance-uid? :keycard/get-application-info
:else (get-in db [:keycard :on-card-read]))] :else (get-in db [:keycard :on-card-read]))]
(log/debug "[keycard] on-card-connected" on-card-connected (log/debug "[keycard] on-card-connected" on-card-connected
"on-card-read" on-card-read) "on-card-read" on-card-read)
(when on-card-connected (when on-card-connected
(fx/merge cofx (rf/merge cofx
{:db (-> db {:db (-> db
(assoc-in [:keycard :card-read-in-progress?] (boolean on-card-read)))} (assoc-in [:keycard :card-read-in-progress?] (boolean on-card-read)))}
(when on-card-connected (when on-card-connected
@@ -512,20 +527,21 @@
(nil? on-card-connected)) (nil? on-card-connected))
(get-application-info on-card-read)))))) (get-application-info on-card-read))))))
(fx/defn on-card-disconnected (rf/defn on-card-disconnected
{:events [::on-card-disconnected]} {:events [::on-card-disconnected]}
[{:keys [db] :as cofx} _] [{:keys [db] :as cofx} _]
(log/debug "[keycard] card disconnected") (log/debug "[keycard] card disconnected")
(fx/merge cofx (rf/merge cofx
{:db (-> db {:db (-> db
(assoc-in [:keycard :card-read-in-progress?] false))} (assoc-in [:keycard :card-read-in-progress?] false))}
(restore-on-card-connected) (restore-on-card-connected)
(restore-on-card-read))) (restore-on-card-read)))
(defn keycard-multiaccount? [db] (defn keycard-multiaccount?
[db]
(boolean (get-in db [:multiaccount :keycard-pairing]))) (boolean (get-in db [:multiaccount :keycard-pairing])))
(fx/defn verify-pin (rf/defn verify-pin
{:events [:keycard/verify-pin]} {:events [:keycard/verify-pin]}
[{:keys [db] :as cofx} {:keys [pin-step on-card-connected on-failure on-success]}] [{:keys [db] :as cofx} {:keys [pin-step on-card-connected on-failure on-success]}]
(let [on-success (or on-success (let [on-success (or on-success
@@ -534,23 +550,25 @@
(get-in db [:keycard :pin :on-verified-failure])) (get-in db [:keycard :pin :on-verified-failure]))
pin-step (or pin-step pin-step (or pin-step
(get-in db [:keycard :pin :step]))] (get-in db [:keycard :pin :step]))]
(fx/merge (rf/merge
cofx cofx
{:db (update-in db [:keycard :pin] assoc {:db (update-in db
:step pin-step [:keycard :pin]
:on-verified on-success assoc
:step pin-step
:on-verified on-success
:on-verified-failure on-failure)} :on-verified-failure on-failure)}
(show-connection-sheet (show-connection-sheet
{:on-card-connected (or on-card-connected :keycard/verify-pin) {:on-card-connected (or on-card-connected :keycard/verify-pin)
:handler :handler
(fn [{:keys [db] :as cofx}] (fn [{:keys [db] :as cofx}]
(let [pin (vector->string (get-in db [:keycard :pin pin-step]))] (let [pin (vector->string (get-in db [:keycard :pin pin-step]))]
(fx/merge (rf/merge
cofx cofx
{:db (assoc-in db [:keycard :pin :status] :verifying) {:db (assoc-in db [:keycard :pin :status] :verifying)
:keycard/verify-pin {:pin pin}})))})))) :keycard/verify-pin {:pin pin}})))}))))
(fx/defn navigete-to-keycard-settings (rf/defn navigete-to-keycard-settings
{:events [::navigate-to-keycard-settings]} {:events [::navigate-to-keycard-settings]}
[cofx] [cofx]
(navigation/set-stack-root :profile-stack [:my-profile :keycard-settings])) (navigation/set-stack-root :profile-stack [:my-profile :keycard-settings]))
+186 -143
View File
@@ -1,70 +1,74 @@
(ns status-im.keycard.core (ns status-im.keycard.core
(:require [re-frame.db] (:require [re-frame.db]
[status-im.i18n.i18n :as i18n]
status-im.keycard.backup-key
[status-im.keycard.card :as card]
[status-im.keycard.change-pin :as change-pin] [status-im.keycard.change-pin :as change-pin]
[status-im.keycard.common :as common] [status-im.keycard.common :as common]
status-im.keycard.delete-key status-im.keycard.delete-key
status-im.keycard.export-key status-im.keycard.export-key
status-im.keycard.unpair
status-im.keycard.backup-key
[status-im.keycard.login :as login] [status-im.keycard.login :as login]
[status-im.keycard.mnemonic :as mnemonic] [status-im.keycard.mnemonic :as mnemonic]
[status-im.keycard.onboarding :as onboarding] [status-im.keycard.onboarding :as onboarding]
[status-im.keycard.recovery :as recovery] [status-im.keycard.recovery :as recovery]
[status-im.keycard.sign :as sign] [status-im.keycard.sign :as sign]
status-im.keycard.unpair
[status-im.keycard.wallet :as wallet] [status-im.keycard.wallet :as wallet]
[status-im.keycard.card :as card]
[status-im.i18n.i18n :as i18n]
[status-im.multiaccounts.recover.core :as multiaccounts.recover] [status-im.multiaccounts.recover.core :as multiaccounts.recover]
[status-im.multiaccounts.update.core :as multiaccounts.update] [status-im.multiaccounts.update.core :as multiaccounts.update]
[status-im2.navigation.events :as navigation]
[status-im.utils.datetime :as utils.datetime] [status-im.utils.datetime :as utils.datetime]
[status-im.utils.fx :as fx] [status-im2.navigation.events :as navigation]
[taoensso.timbre :as log])) [taoensso.timbre :as log]
[utils.re-frame :as rf]))
(fx/defn show-keycard-has-multiaccount-alert (rf/defn show-keycard-has-multiaccount-alert
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(fx/merge cofx (rf/merge cofx
{:db (assoc-in db [:keycard :setup-step] nil) {:db (assoc-in db [:keycard :setup-step] nil)
:utils/show-confirmation {:title nil :utils/show-confirmation {:title nil
:content (i18n/label :t/keycard-has-multiaccount-on-it) :content (i18n/label
:t/keycard-has-multiaccount-on-it)
:cancel-button-text "" :cancel-button-text ""
:confirm-button-text :t/okay}})) :confirm-button-text :t/okay}}))
(fx/defn load-pin-screen (rf/defn load-pin-screen
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(fx/merge cofx (rf/merge cofx
{:db (-> db {:db (-> db
(assoc-in [:keycard :setup-step] :pin) (assoc-in [:keycard :setup-step] :pin)
(assoc-in [:keycard :pin] {:enter-step :original (assoc-in [:keycard :pin]
:original [] {:enter-step :original
:confirmation []}))} :original []
:confirmation []}))}
(navigation/navigate-to-cofx :keycard-onboarding-pin nil))) (navigation/navigate-to-cofx :keycard-onboarding-pin nil)))
(fx/defn load-recovery-pin-screen (rf/defn load-recovery-pin-screen
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(fx/merge cofx (rf/merge cofx
{:db (-> db {:db (-> db
(assoc-in [:keycard :pin] {:enter-step :import-multiaccount (assoc-in [:keycard :pin]
:import-multiaccount [] {:enter-step :import-multiaccount
:current []}))} :import-multiaccount []
:current []}))}
(common/listen-to-hardware-back-button) (common/listen-to-hardware-back-button)
(navigation/navigate-replace :keycard-recovery-pin nil))) (navigation/navigate-replace :keycard-recovery-pin nil)))
(fx/defn load-pairing (rf/defn load-pairing
[{:keys [db]}] [{:keys [db]}]
(let [instance-uid (get-in db [:keycard :application-info :instance-uid]) (let [instance-uid (get-in db [:keycard :application-info :instance-uid])
pairing-data (or (get-in db [:keycard :pairings (keyword instance-uid)]) pairing-data (or (get-in db [:keycard :pairings (keyword instance-uid)])
(get-in db [:keycard :pairings instance-uid]))] (get-in db [:keycard :pairings instance-uid]))]
{:db (update-in db [:keycard :secrets] merge pairing-data)})) {:db (update-in db [:keycard :secrets] merge pairing-data)}))
(fx/defn proceed-setup-with-initialized-card (rf/defn proceed-setup-with-initialized-card
[{:keys [db] :as cofx} flow instance-uid paired?] [{:keys [db] :as cofx} flow instance-uid paired?]
(log/debug "[keycard] proceed-setup-with-initialized-card" (log/debug "[keycard] proceed-setup-with-initialized-card"
"instance-uid" instance-uid) "instance-uid"
instance-uid)
(if (= flow :import) (if (= flow :import)
(navigation/navigate-to-cofx cofx :keycard-recovery-no-key nil) (navigation/navigate-to-cofx cofx :keycard-recovery-no-key nil)
(if paired? (if paired?
(fx/merge cofx (rf/merge cofx
(common/listen-to-hardware-back-button) (common/listen-to-hardware-back-button)
(when (= flow :create) (when (= flow :create)
(mnemonic/set-mnemonic)) (mnemonic/set-mnemonic))
@@ -74,25 +78,25 @@
(onboarding/load-pairing-screen cofx) (onboarding/load-pairing-screen cofx)
(recovery/load-pair-screen cofx))))) (recovery/load-pair-screen cofx)))))
(fx/defn navigate-to-keycard-settings (rf/defn navigate-to-keycard-settings
{:events [:profile.ui/keycard-settings-button-pressed]} {:events [:profile.ui/keycard-settings-button-pressed]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(fx/merge cofx (rf/merge cofx
{:db (-> db {:db (-> db
(assoc-in [:keycard :pin :on-verified] nil) (assoc-in [:keycard :pin :on-verified] nil)
(assoc-in [:keycard :setup-step] nil))} (assoc-in [:keycard :setup-step] nil))}
(common/clear-on-card-connected) (common/clear-on-card-connected)
(navigation/navigate-to-cofx :keycard-settings nil))) (navigation/navigate-to-cofx :keycard-settings nil)))
(fx/defn password-option-pressed (rf/defn password-option-pressed
{:eevents [:keycard.ui/password-option-pressed]} {:eevents [:keycard.ui/password-option-pressed]}
[{:keys [db]}] [{:keys [db]}]
(when (= (get-in db [:keycard :flow]) :create) (when (= (get-in db [:keycard :flow]) :create)
#())) ;;TODO with v1 flow #())) ;;TODO with v1 flow
(fx/defn settings-screen-did-load (rf/defn settings-screen-did-load
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(fx/merge cofx (rf/merge cofx
{:db (-> db {:db (-> db
(assoc-in [:keycard :pin :on-verified] nil) (assoc-in [:keycard :pin :on-verified] nil)
(assoc-in [:keycard :setup-step] nil))} (assoc-in [:keycard :setup-step] nil))}
@@ -111,7 +115,7 @@
[{:keys [db]}] [{:keys [db]}]
(let [enter-step (get-in db [:keycard :pin :enter-step])] (let [enter-step (get-in db [:keycard :pin :enter-step])]
{:db (-> db {:db (-> db
(assoc-in [:keycard :pin enter-step] []) (assoc-in [:keycard :pin enter-step] [])
(dissoc :intro-wizard :recovered-account?))})) (dissoc :intro-wizard :recovered-account?))}))
(defn multiaccounts-screen-did-load (defn multiaccounts-screen-did-load
@@ -122,44 +126,51 @@
[{:keys [db]}] [{:keys [db]}]
{:db (assoc-in db [:keycard :setup-step] nil)}) {:db (assoc-in db [:keycard :setup-step] nil)})
(fx/defn set-nfc-supported (rf/defn set-nfc-supported
{:events [:keycard.callback/check-nfc-support-success]} {:events [:keycard.callback/check-nfc-support-success]}
[_ supported?] [_ supported?]
{:keycard/set-nfc-supported supported?}) {:keycard/set-nfc-supported supported?})
;; TODO: Should be listener and replace the view in bottom sheet to avoid this ;; TODO: Should be listener and replace the view in bottom sheet to avoid this
(fx/defn on-check-nfc-enabled-success (rf/defn on-check-nfc-enabled-success
{:events [:keycard.callback/check-nfc-enabled-success]} {:events [:keycard.callback/check-nfc-enabled-success]}
[{:keys [db]} nfc-enabled?] [{:keys [db]} nfc-enabled?]
(log/debug "[keycard] check-nfc-enabled-success" (log/debug "[keycard] check-nfc-enabled-success"
"nfc-enabled?" nfc-enabled?) "nfc-enabled?"
nfc-enabled?)
{:db (assoc-in db [:keycard :nfc-enabled?] nfc-enabled?)}) {:db (assoc-in db [:keycard :nfc-enabled?] nfc-enabled?)})
(defn- proceed-to-pin-confirmation [fx] (defn- proceed-to-pin-confirmation
[fx]
(assoc-in fx [:db :keycard :pin :enter-step] :confirmation)) (assoc-in fx [:db :keycard :pin :enter-step] :confirmation))
(defn- proceed-to-change-puk-confirmation [fx] (defn- proceed-to-change-puk-confirmation
[fx]
(assoc-in fx [:db :keycard :pin :enter-step] :puk-confirmation)) (assoc-in fx [:db :keycard :pin :enter-step] :puk-confirmation))
(defn- proceed-to-pin-reset-confirmation [fx] (defn- proceed-to-pin-reset-confirmation
[fx]
(-> fx (-> fx
(update-in [:db :keycard :pin] dissoc :reset-confirmation) (update-in [:db :keycard :pin] dissoc :reset-confirmation)
(assoc-in [:db :keycard :pin :enter-step] :reset-confirmation))) (assoc-in [:db :keycard :pin :enter-step] :reset-confirmation)))
(defn- proceed-to-puk-confirmation [fx] (defn- proceed-to-puk-confirmation
[fx]
(assoc-in fx [:db :keycard :pin :enter-step] :puk)) (assoc-in fx [:db :keycard :pin :enter-step] :puk))
(fx/defn on-unblock-pin-success (rf/defn on-unblock-pin-success
{:events [:keycard.callback/on-unblock-pin-success]} {:events [:keycard.callback/on-unblock-pin-success]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(let [reset-pin (get-in db [:keycard :pin :reset])] (let [reset-pin (get-in db [:keycard :pin :reset])]
(fx/merge cofx (rf/merge cofx
{:db {:db
(-> db (-> db
(update-in [:keycard :application-info] assoc (update-in [:keycard :application-info]
assoc
:puk-retry-counter 5 :puk-retry-counter 5
:pin-retry-counter 3) :pin-retry-counter 3)
(update-in [:keycard :pin] assoc (update-in [:keycard :pin]
assoc
:status :after-unblocking :status :after-unblocking
:enter-step :login :enter-step :login
:login reset-pin :login reset-pin
@@ -171,18 +182,19 @@
(common/clear-on-card-connected) (common/clear-on-card-connected)
(common/clear-on-card-read)))) (common/clear-on-card-read))))
(fx/defn on-unblock-pin-error (rf/defn on-unblock-pin-error
{:events [:keycard.callback/on-unblock-pin-error]} {:events [:keycard.callback/on-unblock-pin-error]}
[{:keys [db] :as cofx} error] [{:keys [db] :as cofx} error]
(let [tag-was-lost? (common/tag-lost? (:error error)) (let [tag-was-lost? (common/tag-lost? (:error error))
puk-retries (common/pin-retries (:error error))] puk-retries (common/pin-retries (:error error))]
(log/debug "[keycard] unblock pin error" error) (log/debug "[keycard] unblock pin error" error)
(when-not tag-was-lost? (when-not tag-was-lost?
(fx/merge cofx (rf/merge cofx
{:db {:db
(-> db (-> db
(assoc-in [:keycard :application-info :puk-retry-counter] puk-retries) (assoc-in [:keycard :application-info :puk-retry-counter] puk-retries)
(update-in [:keycard :pin] merge (update-in [:keycard :pin]
merge
{:status (if (zero? puk-retries) :blocked-card :error) {:status (if (zero? puk-retries) :blocked-card :error)
:error-label :t/puk-mismatch :error-label :t/puk-mismatch
:enter-step :puk :enter-step :puk
@@ -190,19 +202,25 @@
(common/hide-connection-sheet))))) (common/hide-connection-sheet)))))
(fx/defn clear-on-verify-handlers (rf/defn clear-on-verify-handlers
[{:keys [db]}] [{:keys [db]}]
{:db (update-in db [:keycard :pin] {:db (update-in db
dissoc :on-verified-failure :on-verified)}) [:keycard :pin]
dissoc
:on-verified-failure
:on-verified)})
(fx/defn on-verify-pin-success (rf/defn on-verify-pin-success
{:events [:keycard.callback/on-verify-pin-success]} {:events [:keycard.callback/on-verify-pin-success]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(let [on-verified (get-in db [:keycard :pin :on-verified])] (let [on-verified (get-in db [:keycard :pin :on-verified])]
(log/debug "[hardwaller] success pin verification. on-verified" on-verified) (log/debug "[hardwaller] success pin verification. on-verified" on-verified)
(fx/merge cofx (rf/merge cofx
{:db (update-in db [:keycard :pin] merge {:status nil {:db (update-in db
:error-label nil})} [:keycard :pin]
merge
{:status nil
:error-label nil})}
(common/clear-on-card-connected) (common/clear-on-card-connected)
(common/clear-on-card-read) (common/clear-on-card-read)
;; TODO(Ferossgp): Each pin input should handle this event on it's own, ;; TODO(Ferossgp): Each pin input should handle this event on it's own,
@@ -211,33 +229,36 @@
(when-not (contains? #{:keycard/generate-and-load-key (when-not (contains? #{:keycard/generate-and-load-key
:wallet.accounts/generate-new-keycard-account :wallet.accounts/generate-new-keycard-account
:keycard/remove-key-with-unpair :keycard/remove-key-with-unpair
:keycard/unpair-and-delete} on-verified) :keycard/unpair-and-delete}
on-verified)
(common/hide-connection-sheet)) (common/hide-connection-sheet))
(when-not (contains? #{:keycard/unpair (when-not (contains? #{:keycard/unpair
:keycard/generate-and-load-key :keycard/generate-and-load-key
:keycard/remove-key-with-unpair :keycard/remove-key-with-unpair
:keycard/unpair-and-delete :keycard/unpair-and-delete
:wallet.accounts/generate-new-keycard-account} on-verified) :wallet.accounts/generate-new-keycard-account}
on-verified)
(common/get-application-info nil)) (common/get-application-info nil))
(when on-verified (when on-verified
(common/dispatch-event on-verified)) (common/dispatch-event on-verified))
(clear-on-verify-handlers)))) (clear-on-verify-handlers))))
(fx/defn on-verify-pin-error (rf/defn on-verify-pin-error
{:events [:keycard.callback/on-verify-pin-error]} {:events [:keycard.callback/on-verify-pin-error]}
[{:keys [db] :as cofx} error] [{:keys [db] :as cofx} error]
(let [tag-was-lost? (common/tag-lost? (:error error)) (let [tag-was-lost? (common/tag-lost? (:error error))
setup? (boolean (get-in db [:keycard :setup-step])) setup? (boolean (get-in db [:keycard :setup-step]))
on-verified-failure (get-in db [:keycard :pin :on-verified-failure]) on-verified-failure (get-in db [:keycard :pin :on-verified-failure])
exporting? (get-in db [:keycard :on-export-success]) exporting? (get-in db [:keycard :on-export-success])
pin-retries (common/pin-retries (:error error))] pin-retries (common/pin-retries (:error error))]
(log/debug "[keycard] verify pin error" error) (log/debug "[keycard] verify pin error" error)
(when-not tag-was-lost? (when-not tag-was-lost?
(if-not (nil? pin-retries) (if-not (nil? pin-retries)
(fx/merge cofx (rf/merge cofx
{:db (-> db {:db (-> db
(assoc-in [:keycard :application-info :pin-retry-counter] pin-retries) (assoc-in [:keycard :application-info :pin-retry-counter] pin-retries)
(update-in [:keycard :pin] assoc (update-in [:keycard :pin]
assoc
:status :error :status :error
:enter-step :current :enter-step :current
:puk [] :puk []
@@ -251,20 +272,21 @@
(not on-verified-failure)) (not on-verified-failure))
(when exporting? (when exporting?
(navigation/navigate-back))) (navigation/navigate-back)))
;(navigation/navigate-to-cofx :enter-pin-settings nil))) ;(navigation/navigate-to-cofx :enter-pin-settings nil)))
(when (zero? pin-retries) (common/frozen-keycard-popup)) (when (zero? pin-retries) (common/frozen-keycard-popup))
(when on-verified-failure (when on-verified-failure
(fn [_] {:utils/dispatch-later (fn [_]
[{:dispatch [on-verified-failure] {:utils/dispatch-later
:ms 200}]})) [{:dispatch [on-verified-failure]
:ms 200}]}))
#_(clear-on-verify-handlers)) #_(clear-on-verify-handlers))
(fx/merge cofx (rf/merge cofx
(common/hide-connection-sheet) (common/hide-connection-sheet)
(common/show-wrong-keycard-alert) (common/show-wrong-keycard-alert)
(clear-on-verify-handlers)))))) (clear-on-verify-handlers))))))
(fx/defn unblock-pin (rf/defn unblock-pin
{:events [:keycard/unblock-pin]} {:events [:keycard/unblock-pin]}
[cofx] [cofx]
(common/show-connection-sheet (common/show-connection-sheet
@@ -275,7 +297,7 @@
(let [puk (common/vector->string (get-in db [:keycard :pin :puk])) (let [puk (common/vector->string (get-in db [:keycard :pin :puk]))
pin (common/vector->string (get-in db [:keycard :pin :reset])) pin (common/vector->string (get-in db [:keycard :pin :reset]))
key-uid (get-in db [:keycard :application-info :key-uid])] key-uid (get-in db [:keycard :application-info :key-uid])]
{:db (assoc-in db [:keycard :pin :status] :verifying) {:db (assoc-in db [:keycard :pin :status] :verifying)
:keycard/unblock-pin :keycard/unblock-pin
{:puk puk {:puk puk
:new-pin pin}}))})) :new-pin pin}}))}))
@@ -283,22 +305,24 @@
(def pin-code-length 6) (def pin-code-length 6)
(def puk-code-length 12) (def puk-code-length 12)
(fx/defn handle-pin-input (rf/defn handle-pin-input
[{:keys [db]} enter-step] [{:keys [db]} enter-step]
(let [numbers-entered (count (get-in db [:keycard :pin enter-step]))] (let [numbers-entered (count (get-in db [:keycard :pin enter-step]))]
(when (or (= numbers-entered pin-code-length) (when (or (= numbers-entered pin-code-length)
(= numbers-entered puk-code-length)) (= numbers-entered puk-code-length))
{:dispatch [:keycard/process-pin-input]}))) {:dispatch [:keycard/process-pin-input]})))
(fx/defn update-pin (rf/defn update-pin
{:events [:keycard.ui/pin-numpad-button-pressed]} {:events [:keycard.ui/pin-numpad-button-pressed]}
[{:keys [db] :as cofx} number enter-step] [{:keys [db] :as cofx} number enter-step]
(log/debug "update-pin" enter-step) (log/debug "update-pin" enter-step)
(let [numbers-entered (count (get-in db [:keycard :pin enter-step])) (let [numbers-entered (count (get-in db [:keycard :pin enter-step]))
need-update? (if (or (= enter-step :puk) (= enter-step :puk-original) (= enter-step :puk-confirmation)) need-update? (if (or (= enter-step :puk)
(< numbers-entered puk-code-length) (= enter-step :puk-original)
(< numbers-entered pin-code-length))] (= enter-step :puk-confirmation))
(fx/merge cofx (< numbers-entered puk-code-length)
(< numbers-entered pin-code-length))]
(rf/merge cofx
{:db (cond-> (-> db {:db (cond-> (-> db
(assoc-in [:keycard :pin :enter-step] enter-step) (assoc-in [:keycard :pin :enter-step] enter-step)
(assoc-in [:keycard :pin :status] nil)) (assoc-in [:keycard :pin :status] nil))
@@ -306,26 +330,38 @@
(when need-update? (when need-update?
(handle-pin-input enter-step))))) (handle-pin-input enter-step)))))
(defn- pin-enter-error [fx error-label] (defn- pin-enter-error
(update-in fx [:db :keycard :pin] merge {:status :error [fx error-label]
:error-label error-label (update-in fx
:enter-step :original [:db :keycard :pin]
:original [] merge
:confirmation []})) {:status :error
:error-label error-label
:enter-step :original
:original []
:confirmation []}))
(defn- puk-enter-error [fx error-label] (defn- puk-enter-error
(update-in fx [:db :keycard :pin] merge {:status :error [fx error-label]
:error-label error-label (update-in fx
:enter-step :puk-original [:db :keycard :pin]
:puk-original [] merge
:puk-confirmation []})) {:status :error
:error-label error-label
:enter-step :puk-original
:puk-original []
:puk-confirmation []}))
(defn- pin-reset-error [fx error-label] (defn- pin-reset-error
(update-in fx [:db :keycard :pin] merge {:status :error [fx error-label]
:error-label error-label (update-in fx
:enter-step :reset [:db :keycard :pin]
:reset [] merge
:reset-confirmation []})) {:status :error
:error-label error-label
:enter-step :reset
:reset []
:reset-confirmation []}))
; PIN enter steps: ; PIN enter steps:
; login - PIN is used to login ; login - PIN is used to login
@@ -333,14 +369,14 @@
; current - current PIN to perform actions which require PIN auth ; current - current PIN to perform actions which require PIN auth
; original - new PIN when user changes it or creates new one ; original - new PIN when user changes it or creates new one
; confirmation - confirmation for new PIN ; confirmation - confirmation for new PIN
(fx/defn process-pin-input (rf/defn process-pin-input
{:events [:keycard/process-pin-input]} {:events [:keycard/process-pin-input]}
[{:keys [db]}] [{:keys [db]}]
(let [enter-step (get-in db [:keycard :pin :enter-step]) (let [enter-step (get-in db [:keycard :pin :enter-step])
pin (get-in db [:keycard :pin enter-step]) pin (get-in db [:keycard :pin enter-step])
numbers-entered (count pin)] numbers-entered (count pin)]
(log/debug "[keycard] process-pin-input" (log/debug "[keycard] process-pin-input"
"enter-step" enter-step "enter-step" enter-step
"numbers-entered" numbers-entered) "numbers-entered" numbers-entered)
(cond-> {:db (assoc-in db [:keycard :pin :status] nil)} (cond-> {:db (assoc-in db [:keycard :pin :status] nil)}
@@ -417,15 +453,19 @@
(get-in db [:keycard :pin :reset-confirmation]))) (get-in db [:keycard :pin :reset-confirmation])))
(pin-reset-error :t/pin-mismatch)))) (pin-reset-error :t/pin-mismatch))))
(fx/defn set-multiaccount-pairing (rf/defn set-multiaccount-pairing
[cofx _ pairing paired-on] [cofx _ pairing paired-on]
(fx/merge cofx (rf/merge cofx
(multiaccounts.update/multiaccount-update (multiaccounts.update/multiaccount-update
:keycard-pairing pairing {}) :keycard-pairing
pairing
{})
(multiaccounts.update/multiaccount-update (multiaccounts.update/multiaccount-update
:keycard-paired-on paired-on {}))) :keycard-paired-on
paired-on
{})))
(fx/defn on-retrieve-pairings-success (rf/defn on-retrieve-pairings-success
{:events [:keycard.callback/on-retrieve-pairings-success]} {:events [:keycard.callback/on-retrieve-pairings-success]}
[{:keys [db]} pairings] [{:keys [db]} pairings]
(card/set-pairings pairings) (card/set-pairings pairings)
@@ -438,7 +478,7 @@
;; with setup and skip the pairing step if the pairing was already done during a previous ;; with setup and skip the pairing step if the pairing was already done during a previous
;; unfinished setup. ;; unfinished setup.
(fx/defn on-pair-success (rf/defn on-pair-success
{:events [:keycard.callback/on-pair-success]} {:events [:keycard.callback/on-pair-success]}
[{:keys [db] :as cofx} pairing] [{:keys [db] :as cofx} pairing]
(let [setup-step (get-in db [:keycard :setup-step]) (let [setup-step (get-in db [:keycard :setup-step])
@@ -448,19 +488,20 @@
paired-on (utils.datetime/timestamp) paired-on (utils.datetime/timestamp)
pairings (-> (get-in db [:keycard :pairings]) pairings (-> (get-in db [:keycard :pairings])
(dissoc (keyword instance-uid)) (dissoc (keyword instance-uid))
(assoc instance-uid {:pairing pairing :paired-on paired-on})) (assoc instance-uid {:pairing pairing :paired-on paired-on}))
next-step (if (= setup-step :pair) next-step (if (= setup-step :pair)
:begin :begin
:card-ready)] :card-ready)]
(fx/merge cofx (rf/merge cofx
{:keycard/persist-pairings pairings {:keycard/persist-pairings pairings
:db (-> db :db (-> db
(assoc-in [:keycard :pairings] pairings) (assoc-in [:keycard :pairings] pairings)
(assoc-in [:keycard :application-info :paired?] true) (assoc-in [:keycard :application-info :paired?] true)
(assoc-in [:keycard :setup-step] next-step) (assoc-in [:keycard :setup-step] next-step)
(assoc-in [:keycard :secrets :pairing] pairing) (assoc-in [:keycard :secrets :pairing] pairing)
(assoc-in [:keycard :secrets :paired-on] paired-on))} (assoc-in [:keycard :secrets :paired-on] paired-on))}
(when-not (and (= flow :recovery) (= next-step :card-ready)) (common/hide-connection-sheet)) (when-not (and (= flow :recovery) (= next-step :card-ready))
(common/hide-connection-sheet))
(when multiaccount (when multiaccount
(set-multiaccount-pairing multiaccount pairing paired-on)) (set-multiaccount-pairing multiaccount pairing paired-on))
(when (= flow :login) (when (= flow :login)
@@ -474,7 +515,7 @@
(when (= flow :create) (when (= flow :create)
(mnemonic/set-mnemonic))))) (mnemonic/set-mnemonic)))))
(fx/defn on-pair-error (rf/defn on-pair-error
{:events [:keycard.callback/on-pair-error]} {:events [:keycard.callback/on-pair-error]}
[{:keys [db] :as cofx} {:keys [error code]}] [{:keys [db] :as cofx} {:keys [error code]}]
(log/debug "[keycard] pair error: " error) (log/debug "[keycard] pair error: " error)
@@ -483,7 +524,7 @@
flow (get-in db [:keycard :flow])] flow (get-in db [:keycard :flow])]
(log/debug "[keycard] on-pair-error" setup-step "flow:" flow) (log/debug "[keycard] on-pair-error" setup-step "flow:" flow)
(when-not tag-was-lost? (when-not tag-was-lost?
(fx/merge cofx (rf/merge cofx
{:db (assoc-in db [:keycard :setup-error] (i18n/label :t/invalid-pairing-password))} {:db (assoc-in db [:keycard :setup-error] (i18n/label :t/invalid-pairing-password))}
(common/set-on-card-connected (if (= setup-step :pairing) (common/set-on-card-connected (if (= setup-step :pairing)
:keycard/load-pairing-screen :keycard/load-pairing-screen
@@ -494,17 +535,19 @@
(when (not= setup-step :enter-pair-code) (when (not= setup-step :enter-pair-code)
(common/process-error code error)))))) (common/process-error code error))))))
(fx/defn set-setup-step (rf/defn set-setup-step
[{:keys [db]} card-state] [{:keys [db]} card-state]
{:db (assoc-in db [:keycard :setup-step] {:db (assoc-in db
(case card-state [:keycard :setup-step]
:not-paired :pair (case card-state
:no-pairing-slots :no-slots :not-paired :pair
:init :card-ready :no-pairing-slots :no-slots
:multiaccount :import-multiaccount :init :card-ready
:begin))}) :multiaccount :import-multiaccount
:begin))})
(fx/defn show-no-keycard-applet-alert [_] (rf/defn show-no-keycard-applet-alert
[_]
{:utils/show-confirmation {:title (i18n/label :t/no-keycard-applet-on-card) {:utils/show-confirmation {:title (i18n/label :t/no-keycard-applet-on-card)
:content (i18n/label :t/keycard-applet-install-instructions) :content (i18n/label :t/keycard-applet-install-instructions)
:cancel-button-text "" :cancel-button-text ""
@@ -512,17 +555,17 @@
;; NOTE: Maybe replaced by multiple events based on on flow to make it easier to maintain. ;; NOTE: Maybe replaced by multiple events based on on flow to make it easier to maintain.
;; Because there are many execution paths it is harder to follow all possible states. ;; Because there are many execution paths it is harder to follow all possible states.
(fx/defn check-card-state (rf/defn check-card-state
{:events [:keycard/check-card-state]} {:events [:keycard/check-card-state]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(let [app-info (get-in db [:keycard :application-info]) (let [app-info (get-in db [:keycard :application-info])
flow (get-in db [:keycard :flow]) flow (get-in db [:keycard :flow])
{:keys [instance-uid key-uid paired?]} app-info {:keys [instance-uid key-uid paired?]} app-info
card-state (common/get-card-state app-info)] card-state (common/get-card-state app-info)]
(log/debug "[keycard] check-card-state" (log/debug "[keycard] check-card-state"
"card-state" card-state "card-state" card-state
"flow" flow) "flow" flow)
(fx/merge cofx (rf/merge cofx
{:db (assoc-in db [:keycard :card-state] card-state)} {:db (assoc-in db [:keycard :card-state] card-state)}
(set-setup-step card-state) (set-setup-step card-state)
@@ -538,7 +581,7 @@
(if (= flow :import) (if (= flow :import)
(navigation/navigate-to-cofx :keycard-recovery-no-key nil) (navigation/navigate-to-cofx :keycard-recovery-no-key nil)
(fn [cofx] (fn [cofx]
(fx/merge (rf/merge
cofx cofx
(common/clear-on-card-read) (common/clear-on-card-read)
(load-pin-screen))))) (load-pin-screen)))))
@@ -560,72 +603,72 @@
(#{:create :recovery} flow)) (#{:create :recovery} flow))
(show-keycard-has-multiaccount-alert))))) (show-keycard-has-multiaccount-alert)))))
(fx/defn on-card-connected (rf/defn on-card-connected
{:events [:keycard.callback/on-card-connected]} {:events [:keycard.callback/on-card-connected]}
[{:keys [db]} _] [{:keys [db]} _]
(log/debug "[keycard] card globally connected") (log/debug "[keycard] card globally connected")
{:db (assoc-in db [:keycard :card-connected?] true)}) {:db (assoc-in db [:keycard :card-connected?] true)})
(fx/defn on-card-disconnected (rf/defn on-card-disconnected
{:events [:keycard.callback/on-card-disconnected]} {:events [:keycard.callback/on-card-disconnected]}
[{:keys [db]} _] [{:keys [db]} _]
(log/debug "[keycard] card disconnected") (log/debug "[keycard] card disconnected")
{:db (assoc-in db [:keycard :card-connected?] false)}) {:db (assoc-in db [:keycard :card-connected?] false)})
(fx/defn on-nfc-user-cancelled (rf/defn on-nfc-user-cancelled
{:events [:keycard.callback/on-nfc-user-cancelled]} {:events [:keycard.callback/on-nfc-user-cancelled]}
[{:keys [db]} _] [{:keys [db]} _]
(log/debug "[keycard] nfc user cancelled") (log/debug "[keycard] nfc user cancelled")
{:dispatch [:signing.ui/cancel-is-pressed]}) {:dispatch [:signing.ui/cancel-is-pressed]})
(fx/defn on-nfc-timeout (rf/defn on-nfc-timeout
{:events [:keycard.callback/on-nfc-timeout]} {:events [:keycard.callback/on-nfc-timeout]}
[{:keys [db]} _] [{:keys [db]} _]
(log/debug "[keycard] nfc timeout") (log/debug "[keycard] nfc timeout")
{:db (-> db {:db (-> db
(assoc-in [:keycard :nfc-running?] false) (assoc-in [:keycard :nfc-running?] false)
(assoc-in [:keycard :card-connected?] false)) (assoc-in [:keycard :card-connected?] false))
:dispatch-later [{:ms 500 :dispatch [:keycard.ui/start-nfc]}]}) :dispatch-later [{:ms 500 :dispatch [:keycard.ui/start-nfc]}]})
(fx/defn on-register-card-events (rf/defn on-register-card-events
{:events [:keycard.callback/on-register-card-events]} {:events [:keycard.callback/on-register-card-events]}
[{:keys [db]} listeners] [{:keys [db]} listeners]
{:db (update-in db [:keycard :listeners] merge listeners)}) {:db (update-in db [:keycard :listeners] merge listeners)})
(fx/defn ui-recovery-phrase-cancel-pressed (rf/defn ui-recovery-phrase-cancel-pressed
{:events [:keycard.ui/recovery-phrase-cancel-pressed]} {:events [:keycard.ui/recovery-phrase-cancel-pressed]}
[{:keys [db]}] [{:keys [db]}]
{:db (assoc-in db [:keycard :setup-step] :recovery-phrase)}) {:db (assoc-in db [:keycard :setup-step] :recovery-phrase)})
(fx/defn ui-pin-numpad-delete-button-pressed (rf/defn ui-pin-numpad-delete-button-pressed
{:events [:keycard.ui/pin-numpad-delete-button-pressed]} {:events [:keycard.ui/pin-numpad-delete-button-pressed]}
[{:keys [db]} step] [{:keys [db]} step]
(when-not (empty? (get-in db [:keycard :pin step])) (when-not (empty? (get-in db [:keycard :pin step]))
{:db (update-in db [:keycard :pin step] pop)})) {:db (update-in db [:keycard :pin step] pop)}))
(fx/defn start-nfc (rf/defn start-nfc
{:events [:keycard.ui/start-nfc]} {:events [:keycard.ui/start-nfc]}
[cofx] [cofx]
{:keycard/start-nfc nil}) {:keycard/start-nfc nil})
(fx/defn stop-nfc (rf/defn stop-nfc
{:events [:keycard.ui/stop-nfc]} {:events [:keycard.ui/stop-nfc]}
[cofx] [cofx]
{:keycard/stop-nfc nil {:keycard/stop-nfc nil
:keycard.callback/on-card-disconnected nil}) :keycard.callback/on-card-disconnected nil})
(fx/defn start-nfc-success (rf/defn start-nfc-success
{:events [:keycard.callback/start-nfc-success]} {:events [:keycard.callback/start-nfc-success]}
[{:keys [db]} _] [{:keys [db]} _]
(log/debug "[keycard] nfc started success") (log/debug "[keycard] nfc started success")
{:db (assoc-in db [:keycard :nfc-running?] true)}) {:db (assoc-in db [:keycard :nfc-running?] true)})
(fx/defn start-nfc-failure (rf/defn start-nfc-failure
{:events [:keycard.callback/start-nfc-failure]} {:events [:keycard.callback/start-nfc-failure]}
[{:keys [db]} _] [{:keys [db]} _]
(log/debug "[keycard] nfc failed starting")) ;; leave current value on :nfc-running (log/debug "[keycard] nfc failed starting")) ;; leave current value on :nfc-running
(fx/defn stop-nfc-success (rf/defn stop-nfc-success
{:events [:keycard.callback/stop-nfc-success]} {:events [:keycard.callback/stop-nfc-success]}
[{:keys [db]} _] [{:keys [db]} _]
(log/debug "[keycard] nfc stopped success") (log/debug "[keycard] nfc stopped success")
@@ -634,7 +677,7 @@
(assoc-in [:keycard :nfc-running?] false) (assoc-in [:keycard :nfc-running?] false)
(assoc-in [:keycard :card-connected?] false))}) (assoc-in [:keycard :card-connected?] false))})
(fx/defn stop-nfc-failure (rf/defn stop-nfc-failure
{:events [:keycard.callback/stop-nfc-failure]} {:events [:keycard.callback/stop-nfc-failure]}
[{:keys [db]} _] [{:keys [db]} _]
(log/debug "[keycard] nfc failed stopping")) ;; leave current value on :nfc-running (log/debug "[keycard] nfc failed stopping")) ;; leave current value on :nfc-running
+20 -18
View File
@@ -1,44 +1,46 @@
(ns status-im.keycard.delete-key (ns status-im.keycard.delete-key
(:require [status-im2.navigation.events :as navigation] (:require [status-im.keycard.common :as common]
[status-im.utils.fx :as fx] [status-im2.navigation.events :as navigation]
[status-im.keycard.common :as common])) [utils.re-frame :as rf]))
(fx/defn reset-card-pressed (rf/defn reset-card-pressed
{:events [:keycard-settings.ui/reset-card-pressed]} {:events [:keycard-settings.ui/reset-card-pressed]}
[cofx] [cofx]
(navigation/navigate-to-cofx cofx :reset-card nil)) (navigation/navigate-to-cofx cofx :reset-card nil))
(fx/defn delete-card (rf/defn delete-card
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(let [key-uid (get-in db [:keycard :application-info :key-uid]) (let [key-uid (get-in db [:keycard :application-info :key-uid])
multiaccount-key-uid (get-in db [:multiaccount :key-uid])] multiaccount-key-uid (get-in db [:multiaccount :key-uid])]
(if (and key-uid (if (and key-uid
(= key-uid multiaccount-key-uid)) (= key-uid multiaccount-key-uid))
{:keycard/delete nil} {:keycard/delete nil}
(common/unauthorized-operation cofx)))) (common/unauthorized-operation cofx))))
(fx/defn navigate-to-reset-card-screen (rf/defn navigate-to-reset-card-screen
{:events [:keycard/navigate-to-reset-card-screen]} {:events [:keycard/navigate-to-reset-card-screen]}
[cofx] [cofx]
(navigation/navigate-to-cofx cofx :reset-card nil)) (navigation/navigate-to-cofx cofx :reset-card nil))
(fx/defn reset-card-next-button-pressed (rf/defn reset-card-next-button-pressed
{:events [:keycard-settings.ui/reset-card-next-button-pressed]} {:events [:keycard-settings.ui/reset-card-next-button-pressed]}
[{:keys [db]}] [{:keys [db]}]
{:db (assoc-in db [:keycard :reset-card :disabled?] true) {:db (assoc-in db [:keycard :reset-card :disabled?] true)
:dispatch [:keycard/proceed-to-reset-card false]}) :dispatch [:keycard/proceed-to-reset-card false]})
(fx/defn proceed-to-reset-card (rf/defn proceed-to-reset-card
{:events [:keycard/proceed-to-reset-card]} {:events [:keycard/proceed-to-reset-card]}
[{:keys [db] :as cofx} keep-keys-on-keycard?] [{:keys [db] :as cofx} keep-keys-on-keycard?]
(fx/merge cofx (rf/merge cofx
{:db (assoc-in db [:keycard :pin] {:enter-step :current {:db (assoc-in db
:current [] [:keycard :pin]
:puk [] {:enter-step :current
:status nil :current []
:error-label nil :puk []
:on-verified (if keep-keys-on-keycard? :status nil
:keycard/unpair-and-delete :error-label nil
:keycard/remove-key-with-unpair)})} :on-verified (if keep-keys-on-keycard?
:keycard/unpair-and-delete
:keycard/remove-key-with-unpair)})}
(common/set-on-card-connected :keycard/navigate-to-enter-pin-screen) (common/set-on-card-connected :keycard/navigate-to-enter-pin-screen)
(common/navigate-to-enter-pin-screen))) (common/navigate-to-enter-pin-screen)))
+34 -32
View File
@@ -1,47 +1,49 @@
(ns status-im.keycard.export-key (ns status-im.keycard.export-key
(:require [status-im.utils.fx :as fx] (:require [status-im.keycard.common :as common]
[taoensso.timbre :as log]
[status-im.keycard.wallet :as wallet] [status-im.keycard.wallet :as wallet]
[status-im.keycard.common :as common])) [taoensso.timbre :as log]
[utils.re-frame :as rf]))
(fx/defn on-export-key-error (rf/defn on-export-key-error
{:events [:keycard.callback/on-export-key-error]} {:events [:keycard.callback/on-export-key-error]}
[{:keys [db] :as cofx} error] [{:keys [db] :as cofx} error]
(log/debug "[keycard] export key error" error) (log/debug "[keycard] export key error" error)
(let [tag-was-lost? (common/tag-lost? (:error error)) (let [tag-was-lost? (common/tag-lost? (:error error))
pin-retries (common/pin-retries (:error error))] pin-retries (common/pin-retries (:error error))]
(cond tag-was-lost? (cond
(fx/merge cofx tag-was-lost?
{:db (assoc-in db [:keycard :pin :status] nil)} (rf/merge cofx
(common/set-on-card-connected :wallet.accounts/generate-new-keycard-account)) {:db (assoc-in db [:keycard :pin :status] nil)}
(common/set-on-card-connected :wallet.accounts/generate-new-keycard-account))
(not (nil? pin-retries)) (not (nil? pin-retries))
(fx/merge cofx (rf/merge cofx
{:db (-> db {:db (-> db
(assoc-in [:keycard :application-info :pin-retry-counter] pin-retries) (assoc-in [:keycard :application-info :pin-retry-counter] pin-retries)
(update-in [:keycard :pin] assoc (update-in [:keycard :pin]
:status :error assoc
:enter-step :export-key :status :error
:puk [] :enter-step :export-key
:current [] :puk []
:original [] :current []
:confirmation [] :original []
:sign [] :confirmation []
:export-key [] :sign []
:error-label :t/pin-mismatch))} :export-key []
(common/hide-connection-sheet) :error-label :t/pin-mismatch))}
(when (zero? pin-retries) (common/frozen-keycard-popup))) (common/hide-connection-sheet)
:else (when (zero? pin-retries) (common/frozen-keycard-popup)))
(fx/merge cofx :else
(common/show-wrong-keycard-alert) (rf/merge cofx
(common/clear-pin) (common/show-wrong-keycard-alert)
(common/hide-connection-sheet))))) (common/clear-pin)
(common/hide-connection-sheet)))))
(fx/defn on-export-key-success (rf/defn on-export-key-success
{:events [:keycard.callback/on-export-key-success]} {:events [:keycard.callback/on-export-key-success]}
[{:keys [db] :as cofx} pubkey] [{:keys [db] :as cofx} pubkey]
(let [callback-fn (get-in db [:keycard :on-export-success])] (let [callback-fn (get-in db [:keycard :on-export-success])]
(fx/merge cofx (rf/merge cofx
{:dispatch (callback-fn pubkey)} {:dispatch (callback-fn pubkey)}
(wallet/hide-pin-sheet) (wallet/hide-pin-sheet)
(common/clear-pin) (common/clear-pin)
+61 -55
View File
@@ -1,70 +1,73 @@
(ns status-im.keycard.login (ns status-im.keycard.login
(:require [status-im.ethereum.core :as ethereum] (:require [status-im.bottom-sheet.core :as bottom-sheet]
[status-im2.navigation.events :as navigation] [status-im.ethereum.core :as ethereum]
[status-im.utils.fx :as fx]
[status-im.utils.types :as types]
[taoensso.timbre :as log]
[status-im.keycard.common :as common] [status-im.keycard.common :as common]
[status-im.keycard.recovery :as recovery]
[status-im.keycard.onboarding :as onboarding]
status-im.keycard.fx status-im.keycard.fx
[status-im.bottom-sheet.core :as bottom-sheet] [status-im.keycard.onboarding :as onboarding]
[status-im.signing.core :as signing.core])) [status-im.keycard.recovery :as recovery]
[status-im.signing.core :as signing.core]
[status-im.utils.types :as types]
[status-im2.navigation.events :as navigation]
[taoensso.timbre :as log]
[utils.re-frame :as rf]))
(fx/defn login-got-it-pressed (rf/defn login-got-it-pressed
{:events [:keycard.login.pin.ui/got-it-pressed {:events [:keycard.login.pin.ui/got-it-pressed
:keycard.login.pin.ui/cancel-pressed]} :keycard.login.pin.ui/cancel-pressed]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(fx/merge cofx (rf/merge cofx
{:db db} {:db db}
(navigation/pop-to-root-tab :multiaccounts-stack))) (navigation/pop-to-root-tab :multiaccounts-stack)))
(fx/defn login-pin-more-icon-pressed (rf/defn login-pin-more-icon-pressed
{:events [:keycard.login.pin.ui/more-icon-pressed]} {:events [:keycard.login.pin.ui/more-icon-pressed]}
[cofx] [cofx]
(bottom-sheet/show-bottom-sheet cofx {:view :keycard.login/more})) (bottom-sheet/show-bottom-sheet cofx {:view :keycard.login/more}))
(fx/defn login-create-key-pressed (rf/defn login-create-key-pressed
{:events [:keycard.login.ui/create-new-key-pressed]} {:events [:keycard.login.ui/create-new-key-pressed]}
[cofx] [cofx]
(fx/merge cofx (rf/merge cofx
(bottom-sheet/hide-bottom-sheet) (bottom-sheet/hide-bottom-sheet)
(onboarding/start-onboarding-flow))) (onboarding/start-onboarding-flow)))
(fx/defn login-add-key-pressed (rf/defn login-add-key-pressed
{:events [:keycard.login.ui/add-key-pressed]} {:events [:keycard.login.ui/add-key-pressed]}
[cofx] [cofx]
(recovery/start-import-flow cofx)) (recovery/start-import-flow cofx))
(fx/defn login-remember-me-changed (rf/defn login-remember-me-changed
{:events [:keycard.login.ui/remember-me-changed]} {:events [:keycard.login.ui/remember-me-changed]}
[{:keys [db] :as cofx} value] [{:keys [db] :as cofx} value]
(fx/merge cofx (rf/merge cofx
{:db (assoc-in db [:keycard :remember-me?] value)})) {:db (assoc-in db [:keycard :remember-me?] value)}))
(fx/defn login-pair-card-pressed (rf/defn login-pair-card-pressed
{:events [:keycard.login.ui/pair-card-pressed]} {:events [:keycard.login.ui/pair-card-pressed]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(log/debug "[keycard] load-pair-card-pressed") (log/debug "[keycard] load-pair-card-pressed")
(fx/merge cofx (rf/merge cofx
{:db (assoc-in db [:keycard :flow] :login)} {:db (assoc-in db [:keycard :flow] :login)}
(navigation/navigate-to-cofx :keycard-recovery-pair nil))) (navigation/navigate-to-cofx :keycard-recovery-pair nil)))
(fx/defn reset-pin (rf/defn reset-pin
{:events [::reset-pin]} {:events [::reset-pin]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(fx/merge (rf/merge
cofx cofx
(signing.core/discard) (signing.core/discard)
(fn [{:keys [db]}] (fn [{:keys [db]}]
{:db (-> db {:db (-> db
(dissoc :popover/popover) (dissoc :popover/popover)
(update-in [:keycard :pin] dissoc (update-in [:keycard :pin]
:reset :puk) dissoc
(update-in [:keycard :pin] assoc :reset
:enter-step :reset :puk)
:error nil (update-in [:keycard :pin]
:status nil)) assoc
:enter-step :reset
:error nil
:status nil))
:hide-popover nil}) :hide-popover nil})
(when (:multiaccount db) (when (:multiaccount db)
(navigation/change-tab :profile)) (navigation/change-tab :profile))
@@ -73,50 +76,51 @@
(navigation/navigate-replace :keycard-pin nil) (navigation/navigate-replace :keycard-pin nil)
(navigation/navigate-to-cofx :keycard-pin nil))))) (navigation/navigate-to-cofx :keycard-pin nil)))))
(fx/defn dismiss-frozen-keycard-popover (rf/defn dismiss-frozen-keycard-popover
{:events [::frozen-keycard-popover-dismissed]} {:events [::frozen-keycard-popover-dismissed]}
[{:keys [db]}] [{:keys [db]}]
{:db (-> db {:db (-> db
(dissoc :popover/popover) (dissoc :popover/popover)
(update :keycard dissoc :setup-step)) (update :keycard dissoc :setup-step))
:hide-popover nil}) :hide-popover nil})
(fx/defn login-with-keycard (rf/defn login-with-keycard
{:events [:keycard/login-with-keycard]} {:events [:keycard/login-with-keycard]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(let [{:keys [:pin-retry-counter :puk-retry-counter] (let [{:keys [:pin-retry-counter :puk-retry-counter]
:as application-info} :as application-info}
(get-in db [:keycard :application-info]) (get-in db [:keycard :application-info])
key-uid (get-in db [:keycard :application-info :key-uid]) key-uid (get-in db [:keycard :application-info :key-uid])
paired? (get-in db [:keycard :application-info :paired?]) paired? (get-in db [:keycard :application-info :paired?])
multiaccount (get-in db [:multiaccounts/multiaccounts (get-in db [:multiaccounts/login :key-uid])]) multiaccount (get-in db
multiaccount-key-uid (get multiaccount :key-uid) [:multiaccounts/multiaccounts (get-in db [:multiaccounts/login :key-uid])])
multiaccount-key-uid (get multiaccount :key-uid)
multiaccount-mismatch? (or (nil? multiaccount) multiaccount-mismatch? (or (nil? multiaccount)
(not= multiaccount-key-uid key-uid))] (not= multiaccount-key-uid key-uid))]
(log/debug "[keycard] login-with-keycard" (log/debug "[keycard] login-with-keycard"
"empty application info" (empty? application-info) "empty application info" (empty? application-info)
"no key-uid" (empty? key-uid) "no key-uid" (empty? key-uid)
"multiaccount-mismatch?" multiaccount-mismatch? "multiaccount-mismatch?" multiaccount-mismatch?
"no pairing" paired?) "no pairing" paired?)
(cond (cond
(empty? application-info) (empty? application-info)
(fx/merge cofx (rf/merge cofx
(common/hide-connection-sheet) (common/hide-connection-sheet)
(navigation/navigate-to-cofx :not-keycard nil)) (navigation/navigate-to-cofx :not-keycard nil))
(empty? key-uid) (empty? key-uid)
(fx/merge cofx (rf/merge cofx
(common/hide-connection-sheet) (common/hide-connection-sheet)
(navigation/navigate-to-cofx :keycard-blank nil)) (navigation/navigate-to-cofx :keycard-blank nil))
multiaccount-mismatch? multiaccount-mismatch?
(fx/merge cofx (rf/merge cofx
(common/hide-connection-sheet) (common/hide-connection-sheet)
(navigation/navigate-to-cofx :keycard-wrong nil)) (navigation/navigate-to-cofx :keycard-wrong nil))
(not paired?) (not paired?)
(fx/merge cofx (rf/merge cofx
(common/hide-connection-sheet) (common/hide-connection-sheet)
(navigation/navigate-to-cofx :keycard-unpaired nil)) (navigation/navigate-to-cofx :keycard-unpaired nil))
@@ -128,7 +132,7 @@
:else :else
(common/get-keys-from-keycard cofx)))) (common/get-keys-from-keycard cofx))))
(fx/defn proceed-to-login (rf/defn proceed-to-login
{:events [::login-after-reset]} {:events [::login-after-reset]}
[cofx] [cofx]
(log/debug "[keycard] proceed-to-login") (log/debug "[keycard] proceed-to-login")
@@ -139,12 +143,14 @@
:on-card-read :keycard/login-with-keycard :on-card-read :keycard/login-with-keycard
:handler (common/get-application-info :keycard/login-with-keycard)})) :handler (common/get-application-info :keycard/login-with-keycard)}))
(fx/defn on-keycard-keychain-keys (rf/defn on-keycard-keychain-keys
{:events [:multiaccounts.login.callback/get-keycard-keys-success]} {:events [:multiaccounts.login.callback/get-keycard-keys-success]}
[{:keys [db] :as cofx} key-uid [encryption-public-key whisper-private-key :as creds]] [{:keys [db] :as cofx} key-uid [encryption-public-key whisper-private-key :as creds]]
(if (nil? creds) (if (nil? creds)
(navigation/set-stack-root cofx :multiaccounts-stack [:multiaccounts (navigation/set-stack-root cofx
:keycard-login-pin]) :multiaccounts-stack
[:multiaccounts
:keycard-login-pin])
(let [{:keys [identicon name]} (get-in db [:multiaccounts/multiaccounts key-uid]) (let [{:keys [identicon name]} (get-in db [:multiaccounts/multiaccounts key-uid])
multiaccount-data (types/clj->json {:name name multiaccount-data (types/clj->json {:name name
:key-uid key-uid :key-uid key-uid
@@ -160,18 +166,18 @@
(update account-data :whisper-public-key ethereum/normalized-hex)) (update account-data :whisper-public-key ethereum/normalized-hex))
(assoc-in [:keycard :flow] nil) (assoc-in [:keycard :flow] nil)
(update :multiaccounts/login assoc (update :multiaccounts/login assoc
:password encryption-public-key :password encryption-public-key
:key-uid key-uid :key-uid key-uid
:identicon identicon :identicon identicon
:name name :name name
:save-password? true)) :save-password? true))
:keycard/login-with-keycard :keycard/login-with-keycard
{:multiaccount-data multiaccount-data {:multiaccount-data multiaccount-data
:key-uid key-uid :key-uid key-uid
:password encryption-public-key :password encryption-public-key
:chat-key whisper-private-key}}))) :chat-key whisper-private-key}})))
(fx/defn on-login-success (rf/defn on-login-success
{:events [:keycard.login.callback/login-success]} {:events [:keycard.login.callback/login-success]}
[_ result] [_ result]
(log/debug "loginWithKeycard success: " result)) (log/debug "loginWithKeycard success: " result))
+14 -14
View File
@@ -1,21 +1,21 @@
(ns status-im.keycard.mnemonic (ns status-im.keycard.mnemonic
(:require [status-im2.navigation.events :as navigation] (:require [status-im.keycard.common :as common]
[status-im.utils.fx :as fx] status-im.keycard.fx
[status-im2.navigation.events :as navigation]
[taoensso.timbre :as log] [taoensso.timbre :as log]
[status-im.keycard.common :as common] [utils.re-frame :as rf]))
status-im.keycard.fx))
(fx/defn set-mnemonic (rf/defn set-mnemonic
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(log/debug "[keycard] set-mnemonic") (log/debug "[keycard] set-mnemonic")
(let [selected-id (get-in db [:intro-wizard :selected-id]) (let [selected-id (get-in db [:intro-wizard :selected-id])
mnemonic (reduce mnemonic (reduce
(fn [_ {:keys [id mnemonic]}] (fn [_ {:keys [id mnemonic]}]
(when (= selected-id id) (when (= selected-id id)
(reduced mnemonic))) (reduced mnemonic)))
nil nil
(get-in db [:intro-wizard :multiaccounts]))] (get-in db [:intro-wizard :multiaccounts]))]
(fx/merge (rf/merge
cofx cofx
{:db (-> db {:db (-> db
(assoc-in [:keycard :setup-step] :recovery-phrase) (assoc-in [:keycard :setup-step] :recovery-phrase)
@@ -24,11 +24,11 @@
(common/hide-connection-sheet) (common/hide-connection-sheet)
(navigation/navigate-replace :keycard-onboarding-recovery-phrase nil)))) (navigation/navigate-replace :keycard-onboarding-recovery-phrase nil))))
(fx/defn load-loading-keys-screen (rf/defn load-loading-keys-screen
{:events [:keycard.ui/recovery-phrase-confirm-pressed {:events [:keycard.ui/recovery-phrase-confirm-pressed
:keycard/load-loading-keys-screen]} :keycard/load-loading-keys-screen]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(fx/merge (rf/merge
cofx cofx
{:db (assoc-in db [:keycard :setup-step] :loading-keys)} {:db (assoc-in db [:keycard :setup-step] :loading-keys)}
(common/show-connection-sheet (common/show-connection-sheet
+90 -78
View File
@@ -1,18 +1,18 @@
(ns status-im.keycard.onboarding (ns status-im.keycard.onboarding
(:require [clojure.string :as string] (:require [clojure.string :as string]
[re-frame.core :as re-frame] [re-frame.core :as re-frame]
[status-im.constants :as constants]
[status-im.i18n.i18n :as i18n] [status-im.i18n.i18n :as i18n]
[status-im2.navigation.events :as navigation]
[status-im.utils.fx :as fx]
[status-im.utils.utils :as utils]
[status-im.keycard.common :as common] [status-im.keycard.common :as common]
[status-im.keycard.mnemonic :as mnemonic]
[taoensso.timbre :as log]
status-im.keycard.fx status-im.keycard.fx
[status-im.keycard.mnemonic :as mnemonic]
[status-im.ui.components.react :as react] [status-im.ui.components.react :as react]
[status-im.constants :as constants])) [status-im.utils.utils :as utils]
[status-im2.navigation.events :as navigation]
[taoensso.timbre :as log]
[utils.re-frame :as rf]))
(fx/defn begin-setup-button-pressed (rf/defn begin-setup-button-pressed
{:keys [:keycard.ui/begin-setup-button-pressed]} {:keys [:keycard.ui/begin-setup-button-pressed]}
[{:keys [db]}] [{:keys [db]}]
{:db (-> db {:db (-> db
@@ -21,7 +21,7 @@
(assoc-in [:keycard :pin :original] []) (assoc-in [:keycard :pin :original] [])
(assoc-in [:keycard :pin :confirmation] []))}) (assoc-in [:keycard :pin :confirmation] []))})
(fx/defn start-installation (rf/defn start-installation
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(let [card-state (get-in db [:keycard :card-state]) (let [card-state (get-in db [:keycard :card-state])
pin (common/vector->string (get-in db [:keycard :pin :original]))] pin (common/vector->string (get-in db [:keycard :pin :original]))]
@@ -33,12 +33,12 @@
(do (do
(log/debug (str "Cannot start keycard installation from state: " card-state)) (log/debug (str "Cannot start keycard installation from state: " card-state))
(fx/merge cofx (rf/merge cofx
{:utils/show-popup {:title (i18n/label :t/error) {:utils/show-popup {:title (i18n/label :t/error)
:content (i18n/label :t/something-went-wrong)}} :content (i18n/label :t/something-went-wrong)}}
(navigation/navigate-to-cofx :keycard-authentication-method nil)))))) (navigation/navigate-to-cofx :keycard-authentication-method nil))))))
(fx/defn load-preparing-screen (rf/defn load-preparing-screen
{:events [:keycard/load-preparing-screen]} {:events [:keycard/load-preparing-screen]}
[cofx] [cofx]
(common/show-connection-sheet (common/show-connection-sheet
@@ -47,11 +47,11 @@
:on-card-connected :keycard/load-preparing-screen :on-card-connected :keycard/load-preparing-screen
:handler start-installation})) :handler start-installation}))
(fx/defn load-pairing-screen (rf/defn load-pairing-screen
{:events [:keycard/load-pairing-screen {:events [:keycard/load-pairing-screen
:keycard.onboarding.puk-code.ui/confirm-pressed]} :keycard.onboarding.puk-code.ui/confirm-pressed]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(fx/merge (rf/merge
cofx cofx
{:db (assoc-in db [:keycard :setup-step] :pairing)} {:db (assoc-in db [:keycard :setup-step] :pairing)}
(common/show-connection-sheet (common/show-connection-sheet
@@ -59,21 +59,22 @@
:on-card-connected :keycard/load-pairing-screen :on-card-connected :keycard/load-pairing-screen
:handler (common/dispatch-event :keycard/pair)}))) :handler (common/dispatch-event :keycard/pair)})))
(fx/defn puk-code-next-pressed (rf/defn puk-code-next-pressed
{:events [:keycard.onboarding.puk-code.ui/next-pressed]} {:events [:keycard.onboarding.puk-code.ui/next-pressed]}
[_] [_]
{:ui/show-confirmation {:title (i18n/label :t/secret-keys-confirmation-title) {:ui/show-confirmation {:title (i18n/label :t/secret-keys-confirmation-title)
:content (i18n/label :t/secret-keys-confirmation-text) :content (i18n/label :t/secret-keys-confirmation-text)
:confirm-button-text (i18n/label :t/yes) :confirm-button-text (i18n/label :t/yes)
:cancel-button-text (i18n/label :t/cancel) :cancel-button-text (i18n/label :t/cancel)
:on-accept #(re-frame/dispatch [:keycard.onboarding.puk-code.ui/confirm-pressed]) :on-accept #(re-frame/dispatch
[:keycard.onboarding.puk-code.ui/confirm-pressed])
:on-cancel #()}}) :on-cancel #()}})
(fx/defn load-finishing-screen (rf/defn load-finishing-screen
{:events [:keycard.onboarding.recovery-phrase-confirm-word2.ui/next-pressed {:events [:keycard.onboarding.recovery-phrase-confirm-word2.ui/next-pressed
:keycard/load-finishing-screen]} :keycard/load-finishing-screen]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(fx/merge (rf/merge
cofx cofx
{:db (assoc-in db [:keycard :setup-step] :loading-keys)} {:db (assoc-in db [:keycard :setup-step] :loading-keys)}
(common/show-connection-sheet (common/show-connection-sheet
@@ -81,29 +82,30 @@
:on-card-connected :keycard/load-finishing-screen :on-card-connected :keycard/load-finishing-screen
:handler (common/dispatch-event :keycard/generate-and-load-key)}))) :handler (common/dispatch-event :keycard/generate-and-load-key)})))
(fx/defn recovery-phrase-learn-more-pressed (rf/defn recovery-phrase-learn-more-pressed
{:events [:keycard.onboarding.recovery-phrase.ui/learn-more-pressed]} {:events [:keycard.onboarding.recovery-phrase.ui/learn-more-pressed]}
[_] [_]
(.openURL ^js react/linking constants/keycard-integration-link)) (.openURL ^js react/linking constants/keycard-integration-link))
(fx/defn recovery-phrase-next-pressed (rf/defn recovery-phrase-next-pressed
{:events [:keycard.onboarding.recovery-phrase.ui/next-pressed {:events [:keycard.onboarding.recovery-phrase.ui/next-pressed
:keycard.ui/recovery-phrase-next-button-pressed]} :keycard.ui/recovery-phrase-next-button-pressed]}
[_] [_]
{:ui/show-confirmation {:title (i18n/label :t/keycard-recovery-phrase-confirmation-title) {:ui/show-confirmation {:title (i18n/label :t/keycard-recovery-phrase-confirmation-title)
:content (i18n/label :t/keycard-recovery-phrase-confirmation-text) :content (i18n/label :t/keycard-recovery-phrase-confirmation-text)
:confirm-button-text (i18n/label :t/yes) :confirm-button-text (i18n/label :t/yes)
:cancel-button-text (i18n/label :t/cancel) :cancel-button-text (i18n/label :t/cancel)
:on-accept #(re-frame/dispatch [:keycard.onboarding.recovery-phrase.ui/confirm-pressed]) :on-accept #(re-frame/dispatch
:on-cancel #()}}) [:keycard.onboarding.recovery-phrase.ui/confirm-pressed])
:on-cancel #()}})
(fx/defn recovery-phrase-start-confirmation (rf/defn recovery-phrase-start-confirmation
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(let [mnemonic (get-in db [:keycard :secrets :mnemonic]) (let [mnemonic (get-in db [:keycard :secrets :mnemonic])
[word1 word2] (shuffle (map-indexed vector (clojure.string/split mnemonic #" "))) [word1 word2] (shuffle (map-indexed vector (clojure.string/split mnemonic #" ")))
word1 (zipmap [:idx :word] word1) word1 (zipmap [:idx :word] word1)
word2 (zipmap [:idx :word] word2)] word2 (zipmap [:idx :word] word2)]
(fx/merge cofx (rf/merge cofx
{:db (-> db {:db (-> db
(assoc-in [:keycard :setup-step] :recovery-phrase-confirm-word1) (assoc-in [:keycard :setup-step] :recovery-phrase-confirm-word1)
(assoc-in [:keycard :recovery-phrase :step] :word1) (assoc-in [:keycard :recovery-phrase :step] :word1)
@@ -113,14 +115,14 @@
(assoc-in [:keycard :recovery-phrase :word2] word2))} (assoc-in [:keycard :recovery-phrase :word2] word2))}
(common/remove-listener-to-hardware-back-button)))) (common/remove-listener-to-hardware-back-button))))
(fx/defn recovery-phrase-confirm-pressed (rf/defn recovery-phrase-confirm-pressed
{:events [:keycard.onboarding.recovery-phrase.ui/confirm-pressed]} {:events [:keycard.onboarding.recovery-phrase.ui/confirm-pressed]}
[cofx] [cofx]
(fx/merge cofx (rf/merge cofx
(recovery-phrase-start-confirmation) (recovery-phrase-start-confirmation)
(navigation/navigate-to-cofx :keycard-onboarding-recovery-phrase-confirm-word1 nil))) (navigation/navigate-to-cofx :keycard-onboarding-recovery-phrase-confirm-word1 nil)))
(fx/defn recovery-phrase-next-word (rf/defn recovery-phrase-next-word
[{:keys [db]}] [{:keys [db]}]
{:db (-> db {:db (-> db
(assoc-in [:keycard :recovery-phrase :step] :word2) (assoc-in [:keycard :recovery-phrase :step] :word2)
@@ -128,50 +130,52 @@
(assoc-in [:keycard :recovery-phrase :input-word] nil) (assoc-in [:keycard :recovery-phrase :input-word] nil)
(assoc-in [:keycard :setup-step] :recovery-phrase-confirm-word2))}) (assoc-in [:keycard :setup-step] :recovery-phrase-confirm-word2))})
(fx/defn proceed-with-generating-key (rf/defn proceed-with-generating-key
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(let [pin (get-in db [:keycard :secrets :pin] (let [pin (get-in db
[:keycard :secrets :pin]
(common/vector->string (get-in db [:keycard :pin :current])))] (common/vector->string (get-in db [:keycard :pin :current])))]
(if (empty? pin) (if (empty? pin)
(fx/merge cofx (rf/merge cofx
{:db (-> db {:db (-> db
(assoc-in [:keycard :pin] {:enter-step :current (assoc-in [:keycard :pin]
:on-verified :keycard/generate-and-load-key {:enter-step :current
:current []}) :on-verified :keycard/generate-and-load-key
:current []})
(assoc-in [:keycard :setup-step] :loading-keys))} (assoc-in [:keycard :setup-step] :loading-keys))}
(navigation/navigate-to-cofx :keycard-onboarding-pin nil)) (navigation/navigate-to-cofx :keycard-onboarding-pin nil))
(load-finishing-screen cofx)))) (load-finishing-screen cofx))))
(fx/defn recovery-phrase-confirm-word-next-pressed (rf/defn recovery-phrase-confirm-word-next-pressed
{:events [:keycard.onboarding.recovery-phrase-confirm-word.ui/next-pressed {:events [:keycard.onboarding.recovery-phrase-confirm-word.ui/next-pressed
:keycard.onboarding.recovery-phrase-confirm-word.ui/input-submitted]} :keycard.onboarding.recovery-phrase-confirm-word.ui/input-submitted]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(let [step (get-in db [:keycard :recovery-phrase :step]) (let [step (get-in db [:keycard :recovery-phrase :step])
input-word (get-in db [:keycard :recovery-phrase :input-word]) input-word (get-in db [:keycard :recovery-phrase :input-word])
{:keys [word]} (get-in db [:keycard :recovery-phrase step])] {:keys [word]} (get-in db [:keycard :recovery-phrase step])]
(if (= word input-word) (if (= word input-word)
(if (= (:view-id db) :keycard-onboarding-recovery-phrase-confirm-word1) (if (= (:view-id db) :keycard-onboarding-recovery-phrase-confirm-word1)
(fx/merge cofx (rf/merge cofx
(recovery-phrase-next-word) (recovery-phrase-next-word)
(navigation/navigate-replace :keycard-onboarding-recovery-phrase-confirm-word2 nil)) (navigation/navigate-replace :keycard-onboarding-recovery-phrase-confirm-word2 nil))
(proceed-with-generating-key cofx)) (proceed-with-generating-key cofx))
{:db (assoc-in db [:keycard :recovery-phrase :confirm-error] (i18n/label :t/wrong-word))}))) {:db (assoc-in db [:keycard :recovery-phrase :confirm-error] (i18n/label :t/wrong-word))})))
(fx/defn recovery-phrase-confirm-word-input-changed (rf/defn recovery-phrase-confirm-word-input-changed
{:events [:keycard.onboarding.recovery-phrase-confirm-word.ui/input-changed]} {:events [:keycard.onboarding.recovery-phrase-confirm-word.ui/input-changed]}
[{:keys [db]} input] [{:keys [db]} input]
{:db (assoc-in db [:keycard :recovery-phrase :input-word] input)}) {:db (assoc-in db [:keycard :recovery-phrase :input-word] input)})
(fx/defn pair-code-input-changed (rf/defn pair-code-input-changed
{:events [:keycard.onboarding.pair.ui/input-changed]} {:events [:keycard.onboarding.pair.ui/input-changed]}
[{:keys [db]} input] [{:keys [db]} input]
{:db (assoc-in db [:keycard :secrets :password] input)}) {:db (assoc-in db [:keycard :secrets :password] input)})
(fx/defn keycard-option-pressed (rf/defn keycard-option-pressed
{:events [:onboarding.ui/keycard-option-pressed]} {:events [:onboarding.ui/keycard-option-pressed]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(let [flow (get-in db [:keycard :flow])] (let [flow (get-in db [:keycard :flow])]
(fx/merge cofx (rf/merge cofx
{:keycard/check-nfc-enabled nil} {:keycard/check-nfc-enabled nil}
(if (= flow :import) (if (= flow :import)
(navigation/navigate-to-cofx :keycard-recovery-intro nil) (navigation/navigate-to-cofx :keycard-recovery-intro nil)
@@ -179,33 +183,36 @@
(common/listen-to-hardware-back-button) (common/listen-to-hardware-back-button)
(navigation/navigate-to-cofx :keycard-onboarding-intro nil)))))) (navigation/navigate-to-cofx :keycard-onboarding-intro nil))))))
(fx/defn start-onboarding-flow (rf/defn start-onboarding-flow
{:events [:keycard/start-onboarding-flow]} {:events [:keycard/start-onboarding-flow]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(fx/merge cofx (rf/merge cofx
{:db (assoc-in db [:keycard :flow] :create) {:db (assoc-in db [:keycard :flow] :create)
:keycard/check-nfc-enabled nil} :keycard/check-nfc-enabled nil}
(common/listen-to-hardware-back-button) (common/listen-to-hardware-back-button)
(navigation/navigate-to-cofx :keycard-onboarding-intro nil))) (navigation/navigate-to-cofx :keycard-onboarding-intro nil)))
(fx/defn open-nfc-settings-pressed (rf/defn open-nfc-settings-pressed
{:events [:keycard.onboarding.nfc-on/open-nfc-settings-pressed]} {:events [:keycard.onboarding.nfc-on/open-nfc-settings-pressed]}
[_] [_]
{:keycard/open-nfc-settings nil}) {:keycard/open-nfc-settings nil})
(defn- show-recover-confirmation [] (defn- show-recover-confirmation
[]
{:ui/show-confirmation {:title (i18n/label :t/are-you-sure?) {:ui/show-confirmation {:title (i18n/label :t/are-you-sure?)
:content (i18n/label :t/are-you-sure-description) :content (i18n/label :t/are-you-sure-description)
:confirm-button-text (clojure.string/upper-case (i18n/label :t/yes)) :confirm-button-text (clojure.string/upper-case (i18n/label :t/yes))
:cancel-button-text (i18n/label :t/see-it-again) :cancel-button-text (i18n/label :t/see-it-again)
:on-accept #(re-frame/dispatch [:keycard.ui/recovery-phrase-confirm-pressed]) :on-accept #(re-frame/dispatch
:on-cancel #(re-frame/dispatch [:keycard.ui/recovery-phrase-cancel-pressed])}}) [:keycard.ui/recovery-phrase-confirm-pressed])
:on-cancel #(re-frame/dispatch
[:keycard.ui/recovery-phrase-cancel-pressed])}})
(fx/defn recovery-phrase-confirm-word (rf/defn recovery-phrase-confirm-word
{:events [:keycard.ui/recovery-phrase-confirm-word-next-button-pressed]} {:events [:keycard.ui/recovery-phrase-confirm-word-next-button-pressed]}
[{:keys [db]}] [{:keys [db]}]
(let [step (get-in db [:keycard :recovery-phrase :step]) (let [step (get-in db [:keycard :recovery-phrase :step])
input-word (get-in db [:keycard :recovery-phrase :input-word]) input-word (get-in db [:keycard :recovery-phrase :input-word])
{:keys [word]} (get-in db [:keycard :recovery-phrase step])] {:keys [word]} (get-in db [:keycard :recovery-phrase step])]
(if (= word input-word) (if (= word input-word)
(if (= step :word1) (if (= step :word1)
@@ -213,21 +220,21 @@
(show-recover-confirmation)) (show-recover-confirmation))
{:db (assoc-in db [:keycard :recovery-phrase :confirm-error] (i18n/label :t/wrong-word))}))) {:db (assoc-in db [:keycard :recovery-phrase :confirm-error] (i18n/label :t/wrong-word))})))
(fx/defn recovery-phrase-next-button-pressed (rf/defn recovery-phrase-next-button-pressed
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(if (= (get-in db [:keycard :flow]) :create) (if (= (get-in db [:keycard :flow]) :create)
(recovery-phrase-start-confirmation cofx) (recovery-phrase-start-confirmation cofx)
(let [mnemonic (get-in db [:multiaccounts/recover :passphrase])] (let [mnemonic (get-in db [:multiaccounts/recover :passphrase])]
(fx/merge cofx (rf/merge cofx
{:db (assoc-in db [:keycard :secrets :mnemonic] mnemonic)} {:db (assoc-in db [:keycard :secrets :mnemonic] mnemonic)}
(mnemonic/load-loading-keys-screen))))) (mnemonic/load-loading-keys-screen)))))
(fx/defn on-install-applet-and-init-card-success (rf/defn on-install-applet-and-init-card-success
{:events [:keycard.callback/on-install-applet-and-init-card-success {:events [:keycard.callback/on-install-applet-and-init-card-success
:keycard.callback/on-init-card-success]} :keycard.callback/on-init-card-success]}
[{:keys [db] :as cofx} secrets] [{:keys [db] :as cofx} secrets]
(let [secrets' (js->clj secrets :keywordize-keys true)] (let [secrets' (js->clj secrets :keywordize-keys true)]
(fx/merge cofx (rf/merge cofx
{:db (-> db {:db (-> db
(assoc-in [:keycard :card-state] :init) (assoc-in [:keycard :card-state] :init)
(assoc-in [:keycard :setup-step] :secret-keys) (assoc-in [:keycard :setup-step] :secret-keys)
@@ -237,17 +244,17 @@
:on-card-read :keycard/check-card-state :on-card-read :keycard/check-card-state
:handler (common/get-application-info :keycard/check-card-state)})))) :handler (common/get-application-info :keycard/check-card-state)}))))
(fx/defn on-install-applet-and-init-card-error (rf/defn on-install-applet-and-init-card-error
{:events [:keycard.callback/on-install-applet-and-init-card-error {:events [:keycard.callback/on-install-applet-and-init-card-error
:keycard.callback/on-init-card-error]} :keycard.callback/on-init-card-error]}
[{:keys [db] :as cofx} {:keys [code error]}] [{:keys [db] :as cofx} {:keys [code error]}]
(log/debug "[keycard] install applet and init card error: " error) (log/debug "[keycard] install applet and init card error: " error)
(fx/merge cofx (rf/merge cofx
{:db (assoc-in db [:keycard :setup-error] error)} {:db (assoc-in db [:keycard :setup-error] error)}
(common/set-on-card-connected :keycard/load-preparing-screen) (common/set-on-card-connected :keycard/load-preparing-screen)
(common/process-error code error))) (common/process-error code error)))
(fx/defn generate-and-load-key (rf/defn generate-and-load-key
{:events [:keycard/generate-and-load-key]} {:events [:keycard/generate-and-load-key]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(let [{:keys [pin]} (let [{:keys [pin]}
@@ -256,36 +263,41 @@
{:keys [selected-id multiaccounts]} {:keys [selected-id multiaccounts]}
(:intro-wizard db) (:intro-wizard db)
multiaccount (or (->> multiaccounts multiaccount (or (->> multiaccounts
(filter #(= (:id %) selected-id)) (filter #(= (:id %) selected-id))
first) first)
(assoc (get-in db [:intro-wizard :root-key]) (assoc (get-in db [:intro-wizard :root-key])
:derived :derived
(get-in db [:intro-wizard :derived]))) (get-in db [:intro-wizard :derived])))
recovery-mnemonic (get-in db [:intro-wizard :passphrase]) recovery-mnemonic (get-in db [:intro-wizard :passphrase])
mnemonic (or (:mnemonic multiaccount) mnemonic (or (:mnemonic multiaccount)
recovery-mnemonic) recovery-mnemonic)
pin' (or pin (common/vector->string (get-in db [:keycard :pin :current])))] pin' (or pin
(fx/merge cofx (common/vector->string (get-in db
[:keycard :pin
:current])))]
(rf/merge cofx
{:keycard/generate-and-load-key {:keycard/generate-and-load-key
{:mnemonic mnemonic {:mnemonic mnemonic
:pin pin' :pin pin'
:key-uid (:key-uid multiaccount) :key-uid (:key-uid multiaccount)
:delete-multiaccount? (get-in db [:keycard :delete-account?])}}))) :delete-multiaccount? (get-in db [:keycard :delete-account?])}})))
(fx/defn factory-reset-card-toggle (rf/defn factory-reset-card-toggle
{:events [:keycard.onboarding.intro.ui/factory-reset-card-toggle]} {:events [:keycard.onboarding.intro.ui/factory-reset-card-toggle]}
[{:keys [db] :as cofx} checked?] [{:keys [db] :as cofx} checked?]
{:db (assoc-in db [:keycard :factory-reset-card?] checked?)}) {:db (assoc-in db [:keycard :factory-reset-card?] checked?)})
(fx/defn begin-setup-pressed (rf/defn begin-setup-pressed
{:events [:keycard.onboarding.intro.ui/begin-setup-pressed]} {:events [:keycard.onboarding.intro.ui/begin-setup-pressed]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(fx/merge (rf/merge
cofx cofx
{:db (-> db {:db (-> db
(update :keycard (update :keycard
dissoc :secrets :card-state :multiaccount-wallet-address dissoc
:secrets
:card-state :multiaccount-wallet-address
:multiaccount-whisper-public-key :application-info) :multiaccount-whisper-public-key :application-info)
(assoc-in [:keycard :setup-step] :begin) (assoc-in [:keycard :setup-step] :begin)
(assoc-in [:keycard :pin :on-verified] nil))} (assoc-in [:keycard :pin :on-verified] nil))}
@@ -301,7 +313,7 @@
:on-card-read :keycard/check-card-state :on-card-read :keycard/check-card-state
:handler (common/get-application-info :keycard/check-card-state)})))) :handler (common/get-application-info :keycard/check-card-state)}))))
(fx/defn factory-reset (rf/defn factory-reset
{:events [::factory-reset]} {:events [::factory-reset]}
[cofx] [cofx]
(common/show-connection-sheet (common/show-connection-sheet
@@ -310,14 +322,14 @@
:on-card-read :keycard/check-card-state :on-card-read :keycard/check-card-state
:handler (common/factory-reset :keycard/check-card-state)})) :handler (common/factory-reset :keycard/check-card-state)}))
(fx/defn factory-reset-cancel (rf/defn factory-reset-cancel
{:events [::factory-reset-cancel]} {:events [::factory-reset-cancel]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
{:db (update db :keycard dissoc :factory-reset-card?)}) {:db (update db :keycard dissoc :factory-reset-card?)})
(fx/defn cancel-pressed (rf/defn cancel-pressed
{:events [::cancel-pressed]} {:events [::cancel-pressed]}
[cofx] [cofx]
(fx/merge cofx (rf/merge cofx
(navigation/navigate-back) (navigation/navigate-back)
(common/cancel-sheet-confirm))) (common/cancel-sheet-confirm)))
+156 -136
View File
@@ -1,30 +1,31 @@
(ns status-im.keycard.recovery (ns status-im.keycard.recovery
(:require [status-im2.navigation.events :as navigation] (:require [clojure.string :as string]
[status-im.utils.datetime :as utils.datetime]
[status-im.multiaccounts.create.core :as multiaccounts.create]
[status-im.multiaccounts.model :as multiaccounts.model]
[status-im.utils.fx :as fx]
[re-frame.core :as re-frame] [re-frame.core :as re-frame]
[clojure.string :as string] [status-im.bottom-sheet.core :as bottom-sheet]
[status-im.constants :as constants]
[status-im.ethereum.core :as ethereum]
[status-im.ethereum.eip55 :as eip55]
[status-im.i18n.i18n :as i18n] [status-im.i18n.i18n :as i18n]
[taoensso.timbre :as log]
[status-im.keycard.common :as common] [status-im.keycard.common :as common]
status-im.keycard.fx status-im.keycard.fx
[status-im.constants :as constants] [status-im.multiaccounts.create.core :as multiaccounts.create]
[status-im.ethereum.eip55 :as eip55] [status-im.multiaccounts.model :as multiaccounts.model]
[status-im.ethereum.core :as ethereum]
[status-im.bottom-sheet.core :as bottom-sheet]
[status-im.native-module.core :as status] [status-im.native-module.core :as status]
[status-im.popover.core :as popover] [status-im.popover.core :as popover]
[status-im.utils.types :as types] [status-im.utils.datetime :as utils.datetime]
[utils.security.core :as security]
[status-im.utils.keychain.core :as keychain] [status-im.utils.keychain.core :as keychain]
[status-im.utils.platform :as platform])) [status-im.utils.platform :as platform]
[status-im.utils.types :as types]
[status-im2.navigation.events :as navigation]
[taoensso.timbre :as log]
[utils.re-frame :as rf]
[utils.security.core :as security]))
(fx/defn pair* [_ password] (rf/defn pair*
[_ password]
{:keycard/pair {:password password}}) {:keycard/pair {:password password}})
(fx/defn pair (rf/defn pair
{:events [:keycard/pair]} {:events [:keycard/pair]}
[cofx] [cofx]
(let [{:keys [password]} (get-in cofx [:db :keycard :secrets])] (let [{:keys [password]} (get-in cofx [:db :keycard :secrets])]
@@ -33,41 +34,41 @@
{:on-card-connected :keycard/pair {:on-card-connected :keycard/pair
:handler (pair* password)}))) :handler (pair* password)})))
(fx/defn pair-code-next-button-pressed (rf/defn pair-code-next-button-pressed
{:events [:keycard.onboarding.pair.ui/input-submitted {:events [:keycard.onboarding.pair.ui/input-submitted
:keycard.ui/pair-code-next-button-pressed :keycard.ui/pair-code-next-button-pressed
:keycard.onboarding.pair.ui/next-pressed]} :keycard.onboarding.pair.ui/next-pressed]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(let [pairing (get-in db [:keycard :secrets :pairing]) (let [pairing (get-in db [:keycard :secrets :pairing])
paired-on (get-in db [:keycard :secrets :paired-on] (utils.datetime/timestamp))] paired-on (get-in db [:keycard :secrets :paired-on] (utils.datetime/timestamp))]
(fx/merge cofx (rf/merge cofx
(if pairing (if pairing
{:db (-> db {:db (-> db
(assoc-in [:keycard :setup-step] :import-multiaccount) (assoc-in [:keycard :setup-step] :import-multiaccount)
(assoc-in [:keycard :secrets :paired-on] paired-on))} (assoc-in [:keycard :secrets :paired-on] paired-on))}
(pair))))) (pair)))))
(fx/defn load-pair-screen (rf/defn load-pair-screen
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(log/debug "[keycard] load-pair-screen") (log/debug "[keycard] load-pair-screen")
(fx/merge cofx (rf/merge cofx
{:db (-> db {:db (-> db
(assoc-in [:keycard :setup-step] :pair)) (assoc-in [:keycard :setup-step] :pair))
:dispatch [:bottom-sheet/hide]} :dispatch [:bottom-sheet/hide]}
(common/listen-to-hardware-back-button) (common/listen-to-hardware-back-button)
(navigation/navigate-to-cofx :keycard-recovery-pair nil))) (navigation/navigate-to-cofx :keycard-recovery-pair nil)))
(fx/defn keycard-storage-selected-for-recovery (rf/defn keycard-storage-selected-for-recovery
{:events [:recovery.ui/keycard-storage-selected]} {:events [:recovery.ui/keycard-storage-selected]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(fx/merge cofx (rf/merge cofx
{:db (assoc-in db [:keycard :flow] :recovery)} {:db (assoc-in db [:keycard :flow] :recovery)}
(navigation/navigate-to-cofx :keycard-recovery-enter-mnemonic nil))) (navigation/navigate-to-cofx :keycard-recovery-enter-mnemonic nil)))
(fx/defn start-import-flow (rf/defn start-import-flow
{:events [::recover-with-keycard-pressed]} {:events [::recover-with-keycard-pressed]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(fx/merge cofx (rf/merge cofx
{:db {:db
(-> db (-> db
(assoc-in [:keycard :flow] :import) (assoc-in [:keycard :flow] :import)
@@ -76,35 +77,37 @@
(bottom-sheet/hide-bottom-sheet) (bottom-sheet/hide-bottom-sheet)
(navigation/navigate-to-cofx :keycard-recovery-intro nil))) (navigation/navigate-to-cofx :keycard-recovery-intro nil)))
(fx/defn access-key-pressed (rf/defn access-key-pressed
{:events [:multiaccounts.recover.ui/recover-multiaccount-button-pressed]} {:events [:multiaccounts.recover.ui/recover-multiaccount-button-pressed]}
[_] [_]
{:dispatch [:bottom-sheet/show-sheet :recover-sheet]}) {:dispatch [:bottom-sheet/show-sheet :recover-sheet]})
(fx/defn recovery-keycard-selected (rf/defn recovery-keycard-selected
{:events [:recovery.ui/keycard-option-pressed]} {:events [:recovery.ui/keycard-option-pressed]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(fx/merge cofx (rf/merge cofx
{:db (assoc-in db [:keycard :flow] :recovery) {:db (assoc-in db [:keycard :flow] :recovery)
:keycard/check-nfc-enabled nil} :keycard/check-nfc-enabled nil}
(common/listen-to-hardware-back-button) (common/listen-to-hardware-back-button)
(navigation/navigate-to-cofx :keycard-onboarding-intro nil))) (navigation/navigate-to-cofx :keycard-onboarding-intro nil)))
(fx/defn cancel-pressed (rf/defn cancel-pressed
{:events [::cancel-pressed]} {:events [::cancel-pressed]}
[cofx] [cofx]
(fx/merge cofx (rf/merge cofx
(common/cancel-sheet-confirm) (common/cancel-sheet-confirm)
(navigation/navigate-back))) (navigation/navigate-back)))
(fx/defn begin-setup-pressed (rf/defn begin-setup-pressed
{:events [:keycard.recovery.intro.ui/begin-recovery-pressed]} {:events [:keycard.recovery.intro.ui/begin-recovery-pressed]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(fx/merge (rf/merge
cofx cofx
{:db (-> db {:db (-> db
(update :keycard (update :keycard
dissoc :secrets :card-state :multiaccount-wallet-address dissoc
:secrets
:card-state :multiaccount-wallet-address
:multiaccount-whisper-public-key :multiaccount-whisper-public-key
:application-info) :application-info)
(assoc-in [:keycard :setup-step] :begin) (assoc-in [:keycard :setup-step] :begin)
@@ -115,21 +118,24 @@
:sheet-options {:on-cancel [::cancel-pressed]} :sheet-options {:on-cancel [::cancel-pressed]}
:handler (common/get-application-info :keycard/check-card-state)}))) :handler (common/get-application-info :keycard/check-card-state)})))
(fx/defn recovery-success-finish-pressed (rf/defn recovery-success-finish-pressed
{:events [:keycard.recovery.success/finish-pressed]} {:events [:keycard.recovery.success/finish-pressed]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(fx/merge cofx (rf/merge cofx
{:db (update db :keycard dissoc {:db (update db
:keycard dissoc
:multiaccount-wallet-address :multiaccount-wallet-address
:multiaccount-whisper-public-key)} :multiaccount-whisper-public-key)}
(navigation/navigate-to-cofx (if platform/android? (navigation/navigate-to-cofx (if platform/android?
:notifications-settings :welcome) nil))) :notifications-settings
:welcome)
nil)))
(fx/defn intro-wizard (rf/defn intro-wizard
{:events [:multiaccounts.create.ui/intro-wizard]} {:events [:multiaccounts.create.ui/intro-wizard]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(let [accs (get db :multiaccounts/multiaccounts)] (let [accs (get db :multiaccounts/multiaccounts)]
(fx/merge cofx (rf/merge cofx
{:db (-> db {:db (-> db
(update :keycard dissoc :flow) (update :keycard dissoc :flow)
(dissoc :restored-account?))} (dissoc :restored-account?))}
@@ -138,16 +144,16 @@
(navigation/navigate-to-cofx :get-your-keys nil) (navigation/navigate-to-cofx :get-your-keys nil)
(navigation/set-stack-root :onboarding [:get-your-keys]))))) (navigation/set-stack-root :onboarding [:get-your-keys])))))
(fx/defn recovery-no-key (rf/defn recovery-no-key
{:events [:keycard.recovery.no-key.ui/generate-key-pressed]} {:events [:keycard.recovery.no-key.ui/generate-key-pressed]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(fx/merge cofx (rf/merge cofx
{:db (assoc-in db [:keycard :flow] :create) {:db (assoc-in db [:keycard :flow] :create)
:keycard/check-nfc-enabled nil} :keycard/check-nfc-enabled nil}
(intro-wizard))) (intro-wizard)))
(fx/defn create-keycard-multiaccount (rf/defn create-keycard-multiaccount
{:events [::create-keycard-multiaccount] {:events [::create-keycard-multiaccount]
:interceptors [(re-frame/inject-cofx :random-guid-generator) :interceptors [(re-frame/inject-cofx :random-guid-generator)
(re-frame/inject-cofx ::multiaccounts.create/get-signing-phrase)]} (re-frame/inject-cofx ::multiaccounts.create/get-signing-phrase)]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
@@ -166,13 +172,14 @@
encryption-public-key encryption-public-key
instance-uid instance-uid
key-uid key-uid
recovered]} multiaccount recovered]}
{:keys [pairing paired-on]} secrets multiaccount
{:keys [pairing paired-on]} secrets
{:keys [name identicon]} {:keys [name identicon]}
(if (nil? name) (if (nil? name)
;; name might have been generated during recovery via passphrase ;; name might have been generated during recovery via passphrase
(get-in db [:intro-wizard :derived constants/path-whisper-keyword]) (get-in db [:intro-wizard :derived constants/path-whisper-keyword])
{:name name {:name name
:identicon identicon})] :identicon identicon})]
;; if a name is still `nil` we have to generate it before multiaccount's ;; if a name is still `nil` we have to generate it before multiaccount's
;; creation otherwise spec validation will fail ;; creation otherwise spec validation will fail
@@ -180,7 +187,7 @@
{:keycard/generate-name-and-photo {:keycard/generate-name-and-photo
{:public-key whisper-public-key {:public-key whisper-public-key
:on-success ::on-name-and-photo-generated}} :on-success ::on-name-and-photo-generated}}
(fx/merge cofx (rf/merge cofx
{:db (-> db {:db (-> db
(assoc-in [:keycard :setup-step] nil) (assoc-in [:keycard :setup-step] nil)
(dissoc :intro-wizard))} (dissoc :intro-wizard))}
@@ -193,7 +200,7 @@
{:public-key whisper-public-key {:public-key whisper-public-key
:address (eip55/address->checksum whisper-address) :address (eip55/address->checksum whisper-address)
:name name :name name
:identicon identicon} :identicon identicon}
constants/path-default-wallet-keyword constants/path-default-wallet-keyword
{:public-key wallet-public-key {:public-key wallet-public-key
:address (eip55/address->checksum wallet-address)}} :address (eip55/address->checksum wallet-address)}}
@@ -207,24 +214,29 @@
encryption-public-key encryption-public-key
{}))))) {})))))
(fx/defn return-to-keycard-login (rf/defn return-to-keycard-login
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(fx/merge cofx (rf/merge cofx
{:db (-> db {:db (-> db
(update-in [:keycard :pin] assoc :enter-step :login (update-in [:keycard :pin]
:status nil assoc
:login []) :enter-step :login
:status nil
:login [])
(update :keycard dissoc :application-info))} (update :keycard dissoc :application-info))}
(navigation/set-stack-root :multiaccounts-stack [:multiaccounts (navigation/set-stack-root :multiaccounts-stack
:keycard-login-pin]))) [:multiaccounts
:keycard-login-pin])))
(fx/defn on-backup-success (rf/defn on-backup-success
[{:keys [db] :as cofx} backup-type] [{:keys [db] :as cofx} backup-type]
(fx/merge cofx (rf/merge cofx
{:utils/show-popup {:title (i18n/label (if (= backup-type :recovery-card) {:utils/show-popup {:title (i18n/label (if (= backup-type :recovery-card)
:t/keycard-access-reset :t/keycard-backup-success-title)) :t/keycard-access-reset
:content (i18n/label (if (= backup-type :recovery-card) :t/keycard-backup-success-title))
:t/keycard-can-use-with-new-passcode :t/keycard-backup-success-body))}} :content (i18n/label (if (= backup-type :recovery-card)
:t/keycard-can-use-with-new-passcode
:t/keycard-backup-success-body))}}
(cond (cond
(multiaccounts.model/logged-in? cofx) (multiaccounts.model/logged-in? cofx)
(navigation/set-stack-root :profile-stack [:my-profile :keycard-settings]) (navigation/set-stack-root :profile-stack [:my-profile :keycard-settings])
@@ -246,36 +258,40 @@
#(let [{:keys [error]} (types/json->clj %)] #(let [{:keys [error]} (types/json->clj %)]
(if (string/blank? error) (if (string/blank? error)
(status/login-with-keycard login-params) (status/login-with-keycard login-params)
(throw (js/Error. "Please shake the phone to report this error and restart the app. Migration failed unexpectedly."))))))) (throw
(js/Error.
"Please shake the phone to report this error and restart the app. Migration failed unexpectedly.")))))))
(fx/defn migrate-account (rf/defn migrate-account
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(let [pairing (get-in db [:keycard :secrets :pairing]) (let [pairing (get-in db [:keycard :secrets :pairing])
paired-on (get-in db [:keycard :secrets :paired-on]) paired-on (get-in db [:keycard :secrets :paired-on])
instance-uid (get-in db [:keycard :multiaccount :instance-uid]) instance-uid (get-in db [:keycard :multiaccount :instance-uid])
account (-> db account (-> db
:multiaccounts/login :multiaccounts/login
(assoc :keycard-pairing pairing) (assoc :keycard-pairing pairing)
(assoc :save-password? false)) (assoc :save-password? false))
key-uid (-> account :key-uid) key-uid (-> account :key-uid)
settings {:keycard-instance-uid instance-uid settings {:keycard-instance-uid instance-uid
:keycard-paired-on paired-on :keycard-paired-on paired-on
:keycard-pairing pairing} :keycard-pairing pairing}
password (ethereum/sha3 (security/safe-unmask-data (get-in db [:keycard :migration-password]))) password (ethereum/sha3 (security/safe-unmask-data (get-in db
[:keycard
:migration-password])))
encryption-pass (get-in db [:keycard :multiaccount :encryption-public-key]) encryption-pass (get-in db [:keycard :multiaccount :encryption-public-key])
login-params {:key-uid key-uid login-params {:key-uid key-uid
:multiaccount-data (types/clj->json account) :multiaccount-data (types/clj->json account)
:password encryption-pass :password encryption-pass
:chat-key (get-in db [:keycard :multiaccount :whisper-private-key])}] :chat-key (get-in db [:keycard :multiaccount :whisper-private-key])}]
{:db (-> db {:db (-> db
(assoc-in [:multiaccounts/multiaccounts key-uid :keycard-pairing] pairing) (assoc-in [:multiaccounts/multiaccounts key-uid :keycard-pairing] pairing)
(assoc :multiaccounts/login account) (assoc :multiaccounts/login account)
(assoc :auth-method keychain/auth-method-none) (assoc :auth-method keychain/auth-method-none)
(update :keycard dissoc :flow :migration-password) (update :keycard dissoc :flow :migration-password)
(dissoc :recovered-account?)) (dissoc :recovered-account?))
::finish-migration [account settings password encryption-pass login-params]})) ::finish-migration [account settings password encryption-pass login-params]}))
(fx/defn delete-multiaccount (rf/defn delete-multiaccount
[{:keys [db]}] [{:keys [db]}]
(let [key-uid (get-in db [:multiaccounts/login :key-uid])] (let [key-uid (get-in db [:multiaccounts/login :key-uid])]
{:keycard/delete-multiaccount-before-migration {:keycard/delete-multiaccount-before-migration
@@ -283,61 +299,63 @@
:on-error #(re-frame/dispatch [::delete-multiaccount-error %]) :on-error #(re-frame/dispatch [::delete-multiaccount-error %])
:on-success #(re-frame/dispatch [::create-keycard-multiaccount])}})) :on-success #(re-frame/dispatch [::create-keycard-multiaccount])}}))
(fx/defn handle-delete-multiaccount-error (rf/defn handle-delete-multiaccount-error
{:events [::delete-multiaccount-error]} {:events [::delete-multiaccount-error]}
[cofx _] [cofx _]
(popover/show-popover cofx {:view :transfer-multiaccount-unknown-error})) (popover/show-popover cofx {:view :transfer-multiaccount-unknown-error}))
(fx/defn on-generate-and-load-key-success (rf/defn on-generate-and-load-key-success
{:events [:keycard.callback/on-generate-and-load-key-success] {:events [:keycard.callback/on-generate-and-load-key-success]
:interceptors [(re-frame/inject-cofx :random-guid-generator) :interceptors [(re-frame/inject-cofx :random-guid-generator)
(re-frame/inject-cofx ::multiaccounts.create/get-signing-phrase)]} (re-frame/inject-cofx ::multiaccounts.create/get-signing-phrase)]}
[{:keys [db random-guid-generator] :as cofx} data] [{:keys [db random-guid-generator] :as cofx} data]
(let [account-data (js->clj data :keywordize-keys true) (let [account-data (js->clj data :keywordize-keys true)
backup? (get-in db [:keycard :creating-backup?]) backup? (get-in db [:keycard :creating-backup?])
migration? (get-in db [:keycard :converting-account?])] migration? (get-in db [:keycard :converting-account?])]
(fx/merge cofx (rf/merge
{:db (-> db cofx
(assoc-in [:keycard :multiaccount] {:db (-> db
(-> account-data (assoc-in
(update :address ethereum/normalized-hex) [:keycard :multiaccount]
(update :whisper-address ethereum/normalized-hex) (-> account-data
(update :wallet-address ethereum/normalized-hex) (update :address ethereum/normalized-hex)
(update :wallet-root-address ethereum/normalized-hex) (update :whisper-address ethereum/normalized-hex)
(update :public-key ethereum/normalized-hex) (update :wallet-address ethereum/normalized-hex)
(update :whisper-public-key ethereum/normalized-hex) (update :wallet-root-address ethereum/normalized-hex)
(update :wallet-public-key ethereum/normalized-hex) (update :public-key ethereum/normalized-hex)
(update :wallet-root-public-key ethereum/normalized-hex) (update :whisper-public-key ethereum/normalized-hex)
(update :instance-uid #(get-in db [:keycard :multiaccount :instance-uid] %)))) (update :wallet-public-key ethereum/normalized-hex)
(assoc-in [:keycard :multiaccount-wallet-address] (:wallet-address account-data)) (update :wallet-root-public-key ethereum/normalized-hex)
(assoc-in [:keycard :multiaccount-whisper-public-key] (:whisper-public-key account-data)) (update :instance-uid #(get-in db [:keycard :multiaccount :instance-uid] %))))
(assoc-in [:keycard :pin :status] nil) (assoc-in [:keycard :multiaccount-wallet-address] (:wallet-address account-data))
(assoc-in [:keycard :application-info :key-uid] (assoc-in [:keycard :multiaccount-whisper-public-key] (:whisper-public-key account-data))
(ethereum/normalized-hex (:key-uid account-data))) (assoc-in [:keycard :pin :status] nil)
(update :keycard dissoc :recovery-phrase :creating-backup? :converting-account?) (assoc-in [:keycard :application-info :key-uid]
(update-in [:keycard :secrets] dissoc :pin :puk :password :mnemonic) (ethereum/normalized-hex (:key-uid account-data)))
(assoc :multiaccounts/new-installation-id (random-guid-generator)))} (update :keycard dissoc :recovery-phrase :creating-backup? :converting-account?)
(common/remove-listener-to-hardware-back-button) (update-in [:keycard :secrets] dissoc :pin :puk :password :mnemonic)
(common/hide-connection-sheet) (assoc :multiaccounts/new-installation-id (random-guid-generator)))}
(cond backup? (on-backup-success backup?) (common/remove-listener-to-hardware-back-button)
migration? (migrate-account) (common/hide-connection-sheet)
(cond backup? (on-backup-success backup?)
migration? (migrate-account)
(get-in db [:keycard :delete-account?]) (get-in db [:keycard :delete-account?])
(delete-multiaccount) (delete-multiaccount)
:else (create-keycard-multiaccount))))) :else (create-keycard-multiaccount)))))
(fx/defn on-generate-and-load-key-error (rf/defn on-generate-and-load-key-error
{:events [:keycard.callback/on-generate-and-load-key-error]} {:events [:keycard.callback/on-generate-and-load-key-error]}
[{:keys [db] :as cofx} {:keys [error code]}] [{:keys [db] :as cofx} {:keys [error code]}]
(log/debug "[keycard] generate and load key error: " error) (log/debug "[keycard] generate and load key error: " error)
(when-not (common/tag-lost? error) (when-not (common/tag-lost? error)
(fx/merge cofx (rf/merge cofx
{:db (assoc-in db [:keycard :setup-error] error)} {:db (assoc-in db [:keycard :setup-error] error)}
(common/set-on-card-connected :keycard/load-loading-keys-screen) (common/set-on-card-connected :keycard/load-loading-keys-screen)
(common/process-error code error)))) (common/process-error code error))))
(fx/defn import-multiaccount (rf/defn import-multiaccount
{:events [:keycard/import-multiaccount]} {:events [:keycard/import-multiaccount]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(let [{:keys [pairing]} (get-in db [:keycard :secrets]) (let [{:keys [pairing]} (get-in db [:keycard :secrets])
@@ -345,17 +363,18 @@
key-uid (get-in db [:keycard :application-info :key-uid]) key-uid (get-in db [:keycard :application-info :key-uid])
pairing' (or pairing (common/get-pairing db key-uid)) pairing' (or pairing (common/get-pairing db key-uid))
pin (common/vector->string (get-in db [:keycard :pin :import-multiaccount]))] pin (common/vector->string (get-in db [:keycard :pin :import-multiaccount]))]
(fx/merge cofx (rf/merge cofx
{:db (-> db {:db (-> db
(assoc-in [:keycard :multiaccount :instance-uid] instance-uid) (assoc-in [:keycard :multiaccount :instance-uid] instance-uid)
(assoc-in [:keycard :pin :status] :verifying) (assoc-in [:keycard :pin :status] :verifying)
(assoc-in [:keycard :secrets] {:pairing pairing' (assoc-in [:keycard :secrets]
:paired-on (utils.datetime/timestamp)})) {:pairing pairing'
:paired-on (utils.datetime/timestamp)}))
:keycard/import-keys :keycard/import-keys
{:pin pin {:pin pin
:on-success :keycard.callback/on-generate-and-load-key-success}}))) :on-success :keycard.callback/on-generate-and-load-key-success}})))
(fx/defn load-recovering-key-screen (rf/defn load-recovering-key-screen
{:events [:keycard/load-recovering-key-screen]} {:events [:keycard/load-recovering-key-screen]}
[cofx] [cofx]
(common/show-connection-sheet (common/show-connection-sheet
@@ -363,17 +382,18 @@
{:on-card-connected :keycard/load-recovering-key-screen {:on-card-connected :keycard/load-recovering-key-screen
:handler (common/dispatch-event :keycard/import-multiaccount)})) :handler (common/dispatch-event :keycard/import-multiaccount)}))
(fx/defn on-name-and-photo-generated (rf/defn on-name-and-photo-generated
{:events [::on-name-and-photo-generated] {:events [::on-name-and-photo-generated]
:interceptors [(re-frame/inject-cofx :random-guid-generator) :interceptors [(re-frame/inject-cofx :random-guid-generator)
(re-frame/inject-cofx ::multiaccounts.create/get-signing-phrase)]} (re-frame/inject-cofx ::multiaccounts.create/get-signing-phrase)]}
[{:keys [db] :as cofx} whisper-name identicon] [{:keys [db] :as cofx} whisper-name identicon]
(fx/merge (rf/merge
cofx cofx
{:db (update-in db [:keycard :multiaccount] {:db (update-in db
[:keycard :multiaccount]
(fn [multiacc] (fn [multiacc]
(assoc multiacc (assoc multiacc
:recovered (get db :recovered-account?) :recovered (get db :recovered-account?)
:name whisper-name :name whisper-name
:identicon identicon)))} :identicon identicon)))}
(create-keycard-multiaccount))) (create-keycard-multiaccount)))
+72 -63
View File
@@ -1,46 +1,49 @@
(ns status-im.keycard.sign (ns status-im.keycard.sign
(:require [re-frame.core :as re-frame] (:require [clojure.string :as string]
[re-frame.core :as re-frame]
[status-im.ethereum.core :as ethereum] [status-im.ethereum.core :as ethereum]
[status-im.ethereum.json-rpc :as json-rpc] [status-im.ethereum.json-rpc :as json-rpc]
[status-im.utils.fx :as fx] [status-im.keycard.common :as common]
[status-im.utils.money :as money] [status-im.utils.money :as money]
[status-im.utils.types :as types] [status-im.utils.types :as types]
[taoensso.timbre :as log] [taoensso.timbre :as log]
[status-im.keycard.common :as common] [utils.re-frame :as rf]))
[clojure.string :as string]))
(fx/defn sign (rf/defn sign
{:events [:keycard/sign]} {:events [:keycard/sign]}
[{:keys [db] :as cofx} hash on-success] [{:keys [db] :as cofx} hash on-success]
(let [card-connected? (get-in db [:keycard :card-connected?]) (let [card-connected? (get-in db [:keycard :card-connected?])
key-uid (get-in db [:multiaccount :key-uid]) key-uid (get-in db [:multiaccount :key-uid])
keycard-key-uid (get-in db [:keycard :application-info :key-uid]) keycard-key-uid (get-in db [:keycard :application-info :key-uid])
keycard-pin-retries (get-in db [:keycard :application-info :pin-retry-counter]) keycard-pin-retries (get-in db [:keycard :application-info :pin-retry-counter])
keycard-match? (= key-uid keycard-key-uid) keycard-match? (= key-uid keycard-key-uid)
hash (or hash (get-in db [:keycard :hash])) hash (or hash (get-in db [:keycard :hash]))
data (get-in db [:keycard :data]) data (get-in db [:keycard :data])
typed? (get-in db [:keycard :typed?]) typed? (get-in db [:keycard :typed?])
pin (common/vector->string (get-in db [:keycard :pin :sign])) pin (common/vector->string (get-in db [:keycard :pin :sign]))
from (or (get-in db [:signing/tx :from :address]) (get-in db [:signing/tx :message :from]) (ethereum/default-address db)) from (or (get-in db [:signing/tx :from :address])
path (reduce (get-in db [:signing/tx :message :from])
(fn [_ {:keys [address path]}] (ethereum/default-address db))
(when (ethereum/address= from address) path (reduce
(reduced path))) (fn [_ {:keys [address path]}]
nil (when (ethereum/address= from address)
(:multiaccount/accounts db))] (reduced path)))
nil
(:multiaccount/accounts db))]
(cond (cond
(not keycard-match?) (not keycard-match?)
(common/show-wrong-keycard-alert cofx) (common/show-wrong-keycard-alert cofx)
(not card-connected?) (not card-connected?)
(fx/merge cofx (rf/merge cofx
{:db (assoc-in db [:signing/sign :keycard-step] :signing)} {:db (assoc-in db [:signing/sign :keycard-step] :signing)}
(common/set-on-card-connected :keycard/sign)) (common/set-on-card-connected :keycard/sign))
(pos? keycard-pin-retries) ; if 0, get-application-info will have already closed the connection sheet and opened the frozen card popup (pos? keycard-pin-retries) ; if 0, get-application-info will have already closed the connection
{:db (-> db ; sheet and opened the frozen card popup
(assoc-in [:keycard :card-read-in-progress?] true) {:db (-> db
(assoc-in [:keycard :pin :status] :verifying)) (assoc-in [:keycard :card-read-in-progress?] true)
(assoc-in [:keycard :pin :status] :verifying))
:keycard/sign {:hash (ethereum/naked-address hash) :keycard/sign {:hash (ethereum/naked-address hash)
:data data :data data
:typed? typed? ; this parameter is for e2e :typed? typed? ; this parameter is for e2e
@@ -48,76 +51,80 @@
:pin pin :pin pin
:path path}}))) :path path}})))
(defn normalize-signature [signature] (defn normalize-signature
[signature]
(-> signature (-> signature
(string/replace-first #"00$", "1b") (string/replace-first #"00$" "1b")
(string/replace-first #"01$", "1c") (string/replace-first #"01$" "1c")
ethereum/normalized-hex)) ethereum/normalized-hex))
(fx/defn sign-message (rf/defn sign-message
{:events [:keycard/sign-message]} {:events [:keycard/sign-message]}
[cofx params result] [cofx params result]
(let [{:keys [result error]} (types/json->clj result) (let [{:keys [result error]} (types/json->clj result)
on-success #(re-frame/dispatch [:keycard/on-sign-message-success params on-success #(re-frame/dispatch [:keycard/on-sign-message-success params
(normalize-signature %)]) (normalize-signature %)])
hash (ethereum/naked-address result)] hash (ethereum/naked-address result)]
(sign cofx hash on-success))) (sign cofx hash on-success)))
(fx/defn on-sign-message-success (rf/defn on-sign-message-success
{:events [:keycard/on-sign-message-success]} {:events [:keycard/on-sign-message-success]}
[{:keys [db] :as cofx} {:keys [tx-hash message-id chat-id value contract]} signature] [{:keys [db] :as cofx} {:keys [tx-hash message-id chat-id value contract]} signature]
(fx/merge (rf/merge
cofx cofx
{:dispatch {:dispatch
(if message-id (if message-id
[:sign/send-accept-transaction-message message-id tx-hash signature] [:sign/send-accept-transaction-message message-id tx-hash signature]
[:sign/send-transaction-message chat-id value contract tx-hash signature]) [:sign/send-transaction-message chat-id value contract tx-hash signature])
:signing/show-transaction-result nil :signing/show-transaction-result nil
:db (-> db :db (-> db
(assoc-in [:keycard :pin :sign] []) (assoc-in [:keycard :pin :sign] [])
(assoc-in [:keycard :pin :status] nil))} (assoc-in [:keycard :pin :status] nil))}
(common/clear-on-card-connected) (common/clear-on-card-connected)
(common/get-application-info nil) (common/get-application-info nil)
(common/hide-connection-sheet))) (common/hide-connection-sheet)))
(fx/defn sign-typed-data (rf/defn sign-typed-data
{:events [:keycard/sign-typed-data]} {:events [:keycard/sign-typed-data]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(let [card-connected? (get-in db [:keycard :card-connected?]) (let [card-connected? (get-in db [:keycard :card-connected?])
hash (get-in db [:keycard :hash])] hash (get-in db [:keycard :hash])]
(if card-connected? (if card-connected?
{:db (-> db {:db (-> db
(assoc-in [:keycard :card-read-in-progress?] true) (assoc-in [:keycard :card-read-in-progress?] true)
(assoc-in [:signing/sign :keycard-step] :signing)) (assoc-in [:signing/sign :keycard-step] :signing))
:keycard/sign-typed-data {:hash (ethereum/naked-address hash)}} :keycard/sign-typed-data {:hash (ethereum/naked-address hash)}}
(fx/merge cofx (rf/merge cofx
(common/set-on-card-connected :keycard/sign-typed-data) (common/set-on-card-connected :keycard/sign-typed-data)
{:db (assoc-in db [:signing/sign :keycard-step] :signing)})))) {:db (assoc-in db [:signing/sign :keycard-step] :signing)}))))
(fx/defn fetch-currency-token-on-success (rf/defn fetch-currency-token-on-success
{:events [:keycard/fetch-currency-token-on-success]} {:events [:keycard/fetch-currency-token-on-success]}
[{:keys [db]} {:keys [decimals symbol]}] [{:keys [db]} {:keys [decimals symbol]}]
{:db (-> db {:db (-> db
(assoc-in [:signing/sign :formatted-data :message :formatted-currency] symbol) (assoc-in [:signing/sign :formatted-data :message :formatted-currency] symbol)
(update-in [:signing/sign :formatted-data :message] (update-in [:signing/sign :formatted-data :message]
#(assoc % :formatted-amount (.dividedBy ^js (money/bignumber (:amount %)) #(assoc %
(money/bignumber (money/from-decimal decimals))))))}) :formatted-amount
(.dividedBy ^js (money/bignumber (:amount %))
(money/bignumber (money/from-decimal decimals))))))})
(fx/defn store-hash-and-sign-typed (rf/defn store-hash-and-sign-typed
{:events [:keycard/store-hash-and-sign-typed]} {:events [:keycard/store-hash-and-sign-typed]}
[{:keys [db] :as cofx} result] [{:keys [db] :as cofx} result]
(let [{:keys [result]} (types/json->clj result) (let [{:keys [result]} (types/json->clj result)
message (get-in db [:signing/sign :formatted-data :message]) message (get-in db [:signing/sign :formatted-data :message])
currency-contract (:currency message)] currency-contract (:currency message)]
(when currency-contract (when currency-contract
{::json-rpc/call [{:method "wallet_discoverToken" {::json-rpc/call [{:method "wallet_discoverToken"
:params [(ethereum/chain-id db) currency-contract] :params [(ethereum/chain-id db) currency-contract]
:on-success #(re-frame/dispatch [:keycard/fetch-currency-token-on-success %])}]}) :on-success #(re-frame/dispatch [:keycard/fetch-currency-token-on-success
(fx/merge cofx %])}]})
(rf/merge cofx
{:db (assoc-in db [:keycard :hash] result)} {:db (assoc-in db [:keycard :hash] result)}
sign-typed-data))) sign-typed-data)))
(fx/defn prepare-to-sign (rf/defn prepare-to-sign
{:events [:keycard/prepare-to-sign]} {:events [:keycard/prepare-to-sign]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(common/show-connection-sheet (common/show-connection-sheet
@@ -125,30 +132,31 @@
{:on-card-connected :keycard/prepare-to-sign {:on-card-connected :keycard/prepare-to-sign
:handler (common/get-application-info :keycard/sign)})) :handler (common/get-application-info :keycard/sign)}))
(fx/defn sign-message-completed (rf/defn sign-message-completed
[_ signature] [_ signature]
{:dispatch {:dispatch
[:signing/sign-message-completed signature]}) [:signing/sign-message-completed signature]})
(fx/defn send-transaction-with-signature (rf/defn send-transaction-with-signature
[_ data] [_ data]
{:send-transaction-with-signature data}) {:send-transaction-with-signature data})
(fx/defn on-sign-success (rf/defn on-sign-success
{:events [:keycard.callback/on-sign-success]} {:events [:keycard.callback/on-sign-success]}
[{:keys [db] :as cofx} signature] [{:keys [db] :as cofx} signature]
(log/debug "[keycard] sign success: " signature) (log/debug "[keycard] sign success: " signature)
(let [signature-json (types/clj->json {:result (normalize-signature signature)}) (let [signature-json (types/clj->json {:result (normalize-signature signature)})
transaction (get-in db [:keycard :transaction]) transaction (get-in db [:keycard :transaction])
tx-obj (select-keys transaction [:from :to :value :gas :gasPrice :command? :chat-id :message-id]) tx-obj (select-keys transaction
[:from :to :value :gas :gasPrice :command? :chat-id :message-id])
command? (:command? transaction)] command? (:command? transaction)]
(fx/merge cofx (rf/merge cofx
{:db (-> db {:db (-> db
(assoc-in [:keycard :hash] nil) (assoc-in [:keycard :hash] nil)
(assoc-in [:keycard :transaction] nil))} (assoc-in [:keycard :transaction] nil))}
(when-not command? (when-not command?
(fn [{:keys [db] :as cofx}] (fn [{:keys [db] :as cofx}]
(fx/merge (rf/merge
cofx cofx
{:db (-> db {:db (-> db
(assoc-in [:keycard :pin :sign] []) (assoc-in [:keycard :pin :sign] [])
@@ -163,18 +171,19 @@
:on-completed #(re-frame/dispatch [:signing/transaction-completed % tx-obj])}) :on-completed #(re-frame/dispatch [:signing/transaction-completed % tx-obj])})
(sign-message-completed signature-json))))) (sign-message-completed signature-json)))))
(fx/defn on-sign-error (rf/defn on-sign-error
{:events [:keycard.callback/on-sign-error]} {:events [:keycard.callback/on-sign-error]}
[{:keys [db] :as cofx} error] [{:keys [db] :as cofx} error]
(log/debug "[keycard] sign error: " error) (log/debug "[keycard] sign error: " error)
(let [tag-was-lost? (common/tag-lost? (:error error)) (let [tag-was-lost? (common/tag-lost? (:error error))
pin-retries (common/pin-retries (:error error))] pin-retries (common/pin-retries (:error error))]
(when-not tag-was-lost? (when-not tag-was-lost?
(if (not (nil? pin-retries)) (if (not (nil? pin-retries))
(fx/merge cofx (rf/merge cofx
{:db (-> db {:db (-> db
(assoc-in [:keycard :application-info :pin-retry-counter] pin-retries) (assoc-in [:keycard :application-info :pin-retry-counter] pin-retries)
(update-in [:keycard :pin] assoc (update-in [:keycard :pin]
assoc
:status :error :status :error
:sign [] :sign []
:error-label :t/pin-mismatch) :error-label :t/pin-mismatch)
@@ -182,6 +191,6 @@
(common/hide-connection-sheet) (common/hide-connection-sheet)
(when (zero? pin-retries) (common/frozen-keycard-popup))) (when (zero? pin-retries) (common/frozen-keycard-popup)))
(fx/merge cofx (rf/merge cofx
(common/hide-connection-sheet) (common/hide-connection-sheet)
(common/show-wrong-keycard-alert)))))) (common/show-wrong-keycard-alert))))))
+101 -79
View File
@@ -1,42 +1,45 @@
(ns status-im.keycard.unpair (ns status-im.keycard.unpair
(:require [re-frame.core :as re-frame] (:require [re-frame.core :as re-frame]
[status-im.multiaccounts.update.core :as multiaccounts.update]
[status-im.i18n.i18n :as i18n] [status-im.i18n.i18n :as i18n]
[status-im2.navigation.events :as navigation]
[status-im.utils.fx :as fx]
[taoensso.timbre :as log]
[status-im.keycard.common :as common] [status-im.keycard.common :as common]
[status-im.multiaccounts.key-storage.core :as key-storage])) [status-im.multiaccounts.key-storage.core :as key-storage]
[status-im.multiaccounts.update.core :as multiaccounts.update]
[status-im2.navigation.events :as navigation]
[taoensso.timbre :as log]
[utils.re-frame :as rf]))
(fx/defn unpair-card-pressed (rf/defn unpair-card-pressed
{:events [:keycard-settings.ui/unpair-card-pressed]} {:events [:keycard-settings.ui/unpair-card-pressed]}
[_] [_]
{:ui/show-confirmation {:title (i18n/label :t/unpair-card) {:ui/show-confirmation {:title (i18n/label :t/unpair-card)
:content (i18n/label :t/unpair-card-confirmation) :content (i18n/label :t/unpair-card-confirmation)
:confirm-button-text (i18n/label :t/yes) :confirm-button-text (i18n/label :t/yes)
:cancel-button-text (i18n/label :t/no) :cancel-button-text (i18n/label :t/no)
:on-accept #(re-frame/dispatch [:keycard-settings.ui/unpair-card-confirmed]) :on-accept #(re-frame/dispatch
[:keycard-settings.ui/unpair-card-confirmed])
:on-cancel #()}}) :on-cancel #()}})
(fx/defn unpair-card-confirmed (rf/defn unpair-card-confirmed
{:events [:keycard-settings.ui/unpair-card-confirmed]} {:events [:keycard-settings.ui/unpair-card-confirmed]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(fx/merge cofx (rf/merge cofx
{:db (assoc-in db [:keycard :pin] {:enter-step :current {:db (assoc-in db
:current [] [:keycard :pin]
:puk [] {:enter-step :current
:status nil :current []
:error-label nil :puk []
:on-verified :keycard/unpair})} :status nil
:error-label nil
:on-verified :keycard/unpair})}
(common/navigate-to-enter-pin-screen))) (common/navigate-to-enter-pin-screen)))
(fx/defn unpair (rf/defn unpair
{:events [:keycard/unpair]} {:events [:keycard/unpair]}
[{:keys [db]}] [{:keys [db]}]
(let [pin (common/vector->string (get-in db [:keycard :pin :current]))] (let [pin (common/vector->string (get-in db [:keycard :pin :current]))]
{:keycard/unpair {:pin pin}})) {:keycard/unpair {:pin pin}}))
(fx/defn unpair-and-delete (rf/defn unpair-and-delete
{:events [:keycard/unpair-and-delete]} {:events [:keycard/unpair-and-delete]}
[cofx] [cofx]
(common/show-connection-sheet (common/show-connection-sheet
@@ -44,55 +47,65 @@
{:on-card-connected :keycard/unpair-and-delete {:on-card-connected :keycard/unpair-and-delete
:handler :handler
(fn [{:keys [db]}] (fn [{:keys [db]}]
(let [pin (common/vector->string (get-in db [:keycard :pin :current]))] (let [pin (common/vector->string (get-in db [:keycard :pin :current]))]
{:keycard/unpair-and-delete {:keycard/unpair-and-delete
{:pin pin}}))})) {:pin pin}}))}))
(fx/defn remove-pairing-from-multiaccount (rf/defn remove-pairing-from-multiaccount
[cofx {:keys [remove-instance-uid?]}] [cofx {:keys [remove-instance-uid?]}]
(fx/merge cofx (rf/merge cofx
(multiaccounts.update/multiaccount-update (multiaccounts.update/multiaccount-update
:keycard-pairing nil {}) :keycard-pairing
nil
{})
(multiaccounts.update/multiaccount-update (multiaccounts.update/multiaccount-update
:keycard-paired-on nil {}) :keycard-paired-on
nil
{})
(when remove-instance-uid? (when remove-instance-uid?
(multiaccounts.update/multiaccount-update (multiaccounts.update/multiaccount-update
:keycard-instance-uid nil {})))) :keycard-instance-uid
nil
{}))))
(fx/defn on-unpair-success (rf/defn on-unpair-success
{:events [:keycard.callback/on-unpair-success]} {:events [:keycard.callback/on-unpair-success]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(let [instance-uid (get-in db [:keycard :application-info :instance-uid]) (let [instance-uid (get-in db [:keycard :application-info :instance-uid])
pairings (get-in db [:keycard :pairings])] pairings (get-in db [:keycard :pairings])]
(fx/merge cofx (rf/merge
{:db (-> db cofx
(assoc-in [:keycard :secrets] nil) {:db (-> db
(update-in [:keycard :pairings] dissoc (keyword instance-uid)) (assoc-in [:keycard :secrets] nil)
(assoc-in [:keycard :pin] {:status nil (update-in [:keycard :pairings] dissoc (keyword instance-uid))
:error-label nil (assoc-in [:keycard :pin]
:on-verified nil})) {:status nil
:keycard/persist-pairings (dissoc pairings (keyword instance-uid)) :error-label nil
:utils/show-popup {:title "" :on-verified nil}))
:content (i18n/label :t/card-unpaired)}} :keycard/persist-pairings (dissoc pairings (keyword instance-uid))
(common/clear-on-card-connected) :utils/show-popup {:title ""
(remove-pairing-from-multiaccount nil) :content (i18n/label :t/card-unpaired)}}
(navigation/navigate-to-cofx :keycard-settings nil)))) (common/clear-on-card-connected)
(remove-pairing-from-multiaccount nil)
(navigation/navigate-to-cofx :keycard-settings nil))))
(fx/defn on-unpair-error (rf/defn on-unpair-error
{:events [:keycard.callback/on-unpair-error]} {:events [:keycard.callback/on-unpair-error]}
[{:keys [db] :as cofx} error] [{:keys [db] :as cofx} error]
(log/debug "[keycard] unpair error" error) (log/debug "[keycard] unpair error" error)
(fx/merge cofx (rf/merge cofx
{:db (assoc-in db [:keycard :pin] {:status nil {:db (assoc-in db
:error-label nil [:keycard :pin]
:on-verified nil}) {:status nil
:error-label nil
:on-verified nil})
:keycard/get-application-info nil :keycard/get-application-info nil
:utils/show-popup {:title "" :utils/show-popup {:title ""
:content (i18n/label :t/something-went-wrong)}} :content (i18n/label :t/something-went-wrong)}}
(common/clear-on-card-connected) (common/clear-on-card-connected)
(navigation/navigate-to-cofx :keycard-settings nil))) (navigation/navigate-to-cofx :keycard-settings nil)))
(fx/defn remove-key-with-unpair (rf/defn remove-key-with-unpair
{:events [:keycard/remove-key-with-unpair]} {:events [:keycard/remove-key-with-unpair]}
[cofx] [cofx]
(common/show-connection-sheet (common/show-connection-sheet
@@ -100,66 +113,75 @@
{:on-card-connected :keycard/remove-key-with-unpair {:on-card-connected :keycard/remove-key-with-unpair
:handler :handler
(fn [{:keys [db]}] (fn [{:keys [db]}]
(let [pin (common/vector->string (get-in db [:keycard :pin :current]))] (let [pin (common/vector->string (get-in db [:keycard :pin :current]))]
{:keycard/remove-key-with-unpair {:keycard/remove-key-with-unpair
{:pin pin}}))})) {:pin pin}}))}))
(defn handle-account-removal [{:keys [db] :as cofx} keys-removed-from-card?] (defn handle-account-removal
(let [key-uid (get-in db [:multiaccount :key-uid]) [{:keys [db] :as cofx} keys-removed-from-card?]
(let [key-uid (get-in db [:multiaccount :key-uid])
instance-uid (get-in db [:keycard :application-info :instance-uid]) instance-uid (get-in db [:keycard :application-info :instance-uid])
pairings (get-in db [:keycard :pairings])] pairings (get-in db [:keycard :pairings])]
(fx/merge cofx (rf/merge
{:db (-> db cofx
(update :multiaccounts/multiaccounts dissoc key-uid) {:db (-> db
(assoc-in [:keycard :secrets] nil) (update :multiaccounts/multiaccounts dissoc key-uid)
(update-in [:keycard :pairings] dissoc (keyword instance-uid)) (assoc-in [:keycard :secrets] nil)
(update-in [:keycard :pairings] dissoc instance-uid) (update-in [:keycard :pairings]
(assoc-in [:keycard :whisper-public-key] nil) dissoc
(assoc-in [:keycard :wallet-address] nil) (keyword instance-uid))
(assoc-in [:keycard :application-info] nil) (update-in [:keycard :pairings] dissoc instance-uid)
(assoc-in [:keycard :pin] {:status nil (assoc-in [:keycard :whisper-public-key] nil)
:error-label nil (assoc-in [:keycard :wallet-address] nil)
:on-verified nil})) (assoc-in [:keycard :application-info] nil)
:keycard/persist-pairings (dissoc pairings (keyword instance-uid)) (assoc-in [:keycard :pin]
:utils/show-popup {:title (i18n/label (if keys-removed-from-card? :t/profile-deleted-title :t/database-reset-title)) {:status nil
:content (i18n/label (if keys-removed-from-card? :t/profile-deleted-keycard :t/database-reset-content)) :error-label nil
:on-dismiss #(re-frame/dispatch [:logout])} :on-verified nil}))
::key-storage/delete-multiaccount {:key-uid key-uid :keycard/persist-pairings (dissoc pairings (keyword instance-uid))
:on-success #(log/debug "[keycard] remove account ok") :utils/show-popup {:title (i18n/label (if keys-removed-from-card?
:on-error #(log/warn "[keycard] remove account: " %)}} :t/profile-deleted-title
(common/clear-on-card-connected) :t/database-reset-title))
(common/hide-connection-sheet)))) :content (i18n/label (if keys-removed-from-card?
:t/profile-deleted-keycard
:t/database-reset-content))
:on-dismiss #(re-frame/dispatch [:logout])}
::key-storage/delete-multiaccount {:key-uid key-uid
:on-success #(log/debug "[keycard] remove account ok")
:on-error #(log/warn "[keycard] remove account: " %)}}
(common/clear-on-card-connected)
(common/hide-connection-sheet))))
(fx/defn on-remove-key-success (rf/defn on-remove-key-success
{:events [:keycard.callback/on-remove-key-success]} {:events [:keycard.callback/on-remove-key-success]}
[cofx] [cofx]
(handle-account-removal cofx true)) (handle-account-removal cofx true))
(fx/defn on-remove-key-error (rf/defn on-remove-key-error
{:events [:keycard.callback/on-remove-key-error]} {:events [:keycard.callback/on-remove-key-error]}
[{:keys [db] :as cofx} error] [{:keys [db] :as cofx} error]
(log/debug "[keycard] remove key error" error) (log/debug "[keycard] remove key error" error)
(let [tag-was-lost? (common/tag-lost? (:error error))] (let [tag-was-lost? (common/tag-lost? (:error error))]
(fx/merge cofx (rf/merge cofx
(if tag-was-lost? (if tag-was-lost?
(fx/merge cofx (rf/merge cofx
{:db (assoc-in db [:keycard :pin :status] nil)} {:db (assoc-in db [:keycard :pin :status] nil)}
(common/set-on-card-connected :keycard/remove-key-with-unpair)) (common/set-on-card-connected :keycard/remove-key-with-unpair))
(common/show-wrong-keycard-alert))))) (common/show-wrong-keycard-alert)))))
(fx/defn on-unpair-and-delete-success (rf/defn on-unpair-and-delete-success
{:events [:keycard.callback/on-unpair-and-delete-success]} {:events [:keycard.callback/on-unpair-and-delete-success]}
[cofx] [cofx]
(handle-account-removal cofx false)) (handle-account-removal cofx false))
(fx/defn on-unpair-and-delete-error (rf/defn on-unpair-and-delete-error
{:events [:keycard.callback/on-unpair-and-delete-error]} {:events [:keycard.callback/on-unpair-and-delete-error]}
[{:keys [db] :as cofx} error] [{:keys [db] :as cofx} error]
(log/debug "[keycard] unpair and delete error" error) (log/debug "[keycard] unpair and delete error" error)
(let [tag-was-lost? (common/tag-lost? (:error error))] (let [tag-was-lost? (common/tag-lost? (:error error))]
(fx/merge cofx (rf/merge cofx
(if tag-was-lost? (if tag-was-lost?
(fx/merge cofx (rf/merge cofx
{:db (assoc-in db [:keycard :pin :status] nil)} {:db (assoc-in db [:keycard :pin :status] nil)}
(common/set-on-card-connected :keycard/unpair-and-delete)) (common/set-on-card-connected :keycard/unpair-and-delete))
(common/show-wrong-keycard-alert))))) (common/show-wrong-keycard-alert)))))
+17 -16
View File
@@ -1,37 +1,37 @@
(ns status-im.keycard.wallet (ns status-im.keycard.wallet
(:require [status-im.ethereum.core :as ethereum] (:require [status-im.bottom-sheet.core :as bottom-sheet]
[status-im.utils.fx :as fx]
[status-im.keycard.common :as common]
[status-im.constants :as constants] [status-im.constants :as constants]
[status-im.ethereum.core :as ethereum]
[status-im.ethereum.eip55 :as eip55] [status-im.ethereum.eip55 :as eip55]
[status-im.utils.hex :as utils.hex] [status-im.keycard.common :as common]
[status-im.ui.screens.wallet.add-new.views :as add-new.views] [status-im.ui.screens.wallet.add-new.views :as add-new.views]
[status-im.bottom-sheet.core :as bottom-sheet])) [status-im.utils.hex :as utils.hex]
[utils.re-frame :as rf]))
(fx/defn show-pin-sheet (rf/defn show-pin-sheet
{:events [:keycard/new-account-pin-sheet]} {:events [:keycard/new-account-pin-sheet]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(fx/merge (rf/merge
cofx cofx
{:db (-> db {:db (-> db
(assoc-in [:keycard :pin :enter-step] :export-key) (assoc-in [:keycard :pin :enter-step] :export-key)
(update-in [:keycard :pin] dissoc :export-key)) (update-in [:keycard :pin] dissoc :export-key))
:dismiss-keyboard nil} :dismiss-keyboard nil}
(bottom-sheet/show-bottom-sheet {:view {:content add-new.views/pin}}))) (bottom-sheet/show-bottom-sheet {:view {:content add-new.views/pin}})))
(fx/defn verify-pin-with-delay (rf/defn verify-pin-with-delay
[cofx] [cofx]
{:utils/dispatch-later {:utils/dispatch-later
;; We need to give previous sheet some time to be fully hidden ;; We need to give previous sheet some time to be fully hidden
[{:ms 200 [{:ms 200
:dispatch [:wallet.accounts/verify-pin]}]}) :dispatch [:wallet.accounts/verify-pin]}]})
(fx/defn hide-pin-sheet (rf/defn hide-pin-sheet
{:events [:keycard/new-account-pin-sheet-hide]} {:events [:keycard/new-account-pin-sheet-hide]}
[cofx] [cofx]
(bottom-sheet/hide-bottom-sheet cofx)) (bottom-sheet/hide-bottom-sheet cofx))
(fx/defn generate-new-keycard-account (rf/defn generate-new-keycard-account
{:events [:wallet.accounts/generate-new-keycard-account]} {:events [:wallet.accounts/generate-new-keycard-account]}
[{:keys [db]}] [{:keys [db]}]
(let [path-num (inc (get-in db [:multiaccount :latest-derived-path])) (let [path-num (inc (get-in db [:multiaccount :latest-derived-path]))
@@ -39,7 +39,8 @@
pin (common/vector->string (get-in db [:keycard :pin :export-key]))] pin (common/vector->string (get-in db [:keycard :pin :export-key]))]
{:db {:db
(assoc-in (assoc-in
db [:keycard :on-export-success] db
[:keycard :on-export-success]
#(vector :wallet.accounts/account-stored #(vector :wallet.accounts/account-stored
(let [public-key (utils.hex/normalize-hex %)] (let [public-key (utils.hex/normalize-hex %)]
{;; Strip leading 04 prefix denoting uncompressed key format {;; Strip leading 04 prefix denoting uncompressed key format
@@ -52,7 +53,7 @@
:keycard/export-key {:pin pin :path path}})) :keycard/export-key {:pin pin :path path}}))
(fx/defn verify-pin (rf/defn verify-pin
{:events [:wallet.accounts/verify-pin]} {:events [:wallet.accounts/verify-pin]}
[cofx] [cofx]
(common/verify-pin (common/verify-pin
+7 -6
View File
@@ -1,23 +1,24 @@
(ns status-im.log-level.core (ns status-im.log-level.core
(:require [re-frame.core :as re-frame] (:require [re-frame.core :as re-frame]
[status-im.i18n.i18n :as i18n]
[status-im.multiaccounts.update.core :as multiaccounts.update] [status-im.multiaccounts.update.core :as multiaccounts.update]
[status-im.node.core :as node] [status-im.node.core :as node]
[status-im.i18n.i18n :as i18n] [utils.re-frame :as rf]))
[status-im.utils.fx :as fx]))
(fx/defn save-log-level (rf/defn save-log-level
{:events [:log-level.ui/change-log-level-confirmed]} {:events [:log-level.ui/change-log-level-confirmed]}
[{:keys [db now] :as cofx} log-level] [{:keys [db now] :as cofx} log-level]
(let [old-log-level (get-in db [:multiaccount :log-level])] (let [old-log-level (get-in db [:multiaccount :log-level])]
(when (not= old-log-level log-level) (when (not= old-log-level log-level)
(fx/merge cofx (rf/merge cofx
(multiaccounts.update/multiaccount-update (multiaccounts.update/multiaccount-update
:log-level log-level :log-level
log-level
{}) {})
(node/prepare-new-config (node/prepare-new-config
{:on-success #(re-frame/dispatch [:logout])}))))) {:on-success #(re-frame/dispatch [:logout])})))))
(fx/defn show-change-log-level-confirmation (rf/defn show-change-log-level-confirmation
{:events [:log-level.ui/log-level-selected]} {:events [:log-level.ui/log-level-selected]}
[_ {:keys [name value]}] [_ {:keys [name value]}]
{:ui/show-confirmation {:ui/show-confirmation
+142 -119
View File
@@ -1,5 +1,4 @@
(ns ^{:doc "Mailserver events and API"} (ns ^{:doc "Mailserver events and API"} status-im.mailserver.core
status-im.mailserver.core
(:require [clojure.string :as string] (:require [clojure.string :as string]
[re-frame.core :as re-frame] [re-frame.core :as re-frame]
[status-im.ethereum.json-rpc :as json-rpc] [status-im.ethereum.json-rpc :as json-rpc]
@@ -8,8 +7,8 @@
[status-im.node.core :as node] [status-im.node.core :as node]
[status-im.utils.mobile-sync :as mobile-network-utils] [status-im.utils.mobile-sync :as mobile-network-utils]
[status-im2.navigation.events :as navigation] [status-im2.navigation.events :as navigation]
[status-im.utils.fx :as fx] [taoensso.timbre :as log]
[taoensso.timbre :as log])) [utils.re-frame :as rf]))
;; How do mailserver work ? ;; How do mailserver work ?
;; ;;
@@ -24,22 +23,28 @@
;; as soon as the mailserver becomes available ;; as soon as the mailserver becomes available
(defn connected? [db id] (defn connected?
[db id]
(= (:mailserver/current-id db) id)) (= (:mailserver/current-id db) id))
(defn fetch [db id] (defn fetch
[db id]
(get-in db [:mailserver/mailservers (node/current-fleet-key db) id])) (get-in db [:mailserver/mailservers (node/current-fleet-key db) id]))
(defn fetch-current [db] (defn fetch-current
[db]
(fetch db (:mailserver/current-id db))) (fetch db (:mailserver/current-id db)))
(defn preferred-mailserver-id [db] (defn preferred-mailserver-id
[db]
(get-in db [:multiaccount :pinned-mailservers (node/current-fleet-key db)])) (get-in db [:multiaccount :pinned-mailservers (node/current-fleet-key db)]))
(defn connection-error-dismissed [db] (defn connection-error-dismissed
[db]
(get-in db [:mailserver/connection-error-dismissed])) (get-in db [:mailserver/connection-error-dismissed]))
(defn mailserver-address->id [db address] (defn mailserver-address->id
[db address]
(let [current-fleet (node/current-fleet-key db)] (let [current-fleet (node/current-fleet-key db)]
(:id (some #(when (= address (:address %)) (:id (some #(when (= address (:address %))
%) %)
@@ -48,19 +53,21 @@
current-fleet current-fleet
vals))))) vals)))))
(fx/defn disconnect-from-mailserver (rf/defn disconnect-from-mailserver
{:events [::disconnect-from-mailserver]} {:events [::disconnect-from-mailserver]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
{:db (-> db {:db (-> db
(assoc :mailserver/state nil) (assoc :mailserver/state nil)
(dissoc :mailserver/current-request :mailserver/fetching-gaps-in-progress))}) (dissoc :mailserver/current-request :mailserver/fetching-gaps-in-progress))})
(defn fetch-use-mailservers? [{:keys [db]}] (defn fetch-use-mailservers?
[{:keys [db]}]
(get-in db [:multiaccount :use-mailservers?])) (get-in db [:multiaccount :use-mailservers?]))
(defonce showing-connection-error-popup? (atom false)) (defonce showing-connection-error-popup? (atom false))
(defn cancel-connection-popup! [] (defn cancel-connection-popup!
[]
(reset! showing-connection-error-popup? false) (reset! showing-connection-error-popup? false)
(re-frame/dispatch [:mailserver.ui/dismiss-connection-error true])) (re-frame/dispatch [:mailserver.ui/dismiss-connection-error true]))
@@ -68,7 +75,8 @@
::cancel-connection-popup ::cancel-connection-popup
cancel-connection-popup!) cancel-connection-popup!)
(fx/defn show-connection-error! [cofx current-fleet preferred-mailserver] (rf/defn show-connection-error!
[cofx current-fleet preferred-mailserver]
(reset! showing-connection-error-popup? true) (reset! showing-connection-error-popup? true)
{:ui/show-confirmation {:ui/show-confirmation
{:title (i18n/label :t/mailserver-error-title) {:title (i18n/label :t/mailserver-error-title)
@@ -88,27 +96,28 @@
preferred-mailserver])) preferred-mailserver]))
:style "default"}]}}) :style "default"}]}})
(fx/defn handle-successful-request (rf/defn handle-successful-request
{:events [::request-success]} {:events [::request-success]}
[{:keys [db] :as cofx} response-js] [{:keys [db] :as cofx} response-js]
{:db (dissoc db :mailserver/current-request) {:db (dissoc db :mailserver/current-request)
:dispatch [:sanitize-messages-and-process-response response-js]}) :dispatch [:sanitize-messages-and-process-response response-js]})
(fx/defn handle-mailserver-not-working [{:keys [db] :as cofx}] (rf/defn handle-mailserver-not-working
(let [current-fleet (node/current-fleet-key db) [{:keys [db] :as cofx}]
error-dismissed? (connection-error-dismissed db) (let [current-fleet (node/current-fleet-key db)
pinned-mailserver (get-in db [:multiaccount :pinned-mailservers current-fleet])] error-dismissed? (connection-error-dismissed db)
pinned-mailserver (get-in db [:multiaccount :pinned-mailservers current-fleet])]
(when (and pinned-mailserver (when (and pinned-mailserver
(not error-dismissed?) (not error-dismissed?)
(not @showing-connection-error-popup?)) (not @showing-connection-error-popup?))
(show-connection-error! cofx current-fleet pinned-mailserver)))) (show-connection-error! cofx current-fleet pinned-mailserver))))
(fx/defn handle-request-error (rf/defn handle-request-error
{:events [::request-error]} {:events [::request-error]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
{:db (dissoc db :mailserver/current-request)}) {:db (dissoc db :mailserver/current-request)})
(fx/defn process-next-messages-request (rf/defn process-next-messages-request
{:events [::request-messages]} {:events [::request-messages]}
[{:keys [db now] :as cofx}] [{:keys [db now] :as cofx}]
(when (and (when (and
@@ -116,93 +125,99 @@
(mobile-network-utils/syncing-allowed? cofx) (mobile-network-utils/syncing-allowed? cofx)
(fetch-use-mailservers? cofx) (fetch-use-mailservers? cofx)
(not (:mailserver/current-request db))) (not (:mailserver/current-request db)))
{:db (assoc db :mailserver/current-request true) {:db (assoc db :mailserver/current-request true)
::json-rpc/call [{:method "wakuext_requestAllHistoricMessagesWithRetries" ::json-rpc/call [{:method "wakuext_requestAllHistoricMessagesWithRetries"
:params [] :params []
:js-response true :js-response true
:on-success #(do :on-success #(do
(log/info "fetched historical messages") (log/info "fetched historical messages")
(re-frame/dispatch [::request-success %])) (re-frame/dispatch [::request-success %]))
:on-error #(do :on-error #(do
(log/error "failed retrieve historical messages" %) (log/error "failed retrieve historical messages" %)
(re-frame/dispatch [::request-error]))}]})) (re-frame/dispatch [::request-error]))}]}))
(fx/defn handle-mailserver-changed (rf/defn handle-mailserver-changed
[{:keys [db] :as cofx} ms] [{:keys [db] :as cofx} ms]
(if (seq ms) (if (seq ms)
{:db (assoc db :mailserver/state :connecting {:db (assoc db
:mailserver/state :connecting
:mailserver/current-id (keyword ms))} :mailserver/current-id (keyword ms))}
{:db (assoc db :mailserver/state nil)})) {:db (assoc db :mailserver/state nil)}))
(fx/defn handle-mailserver-available (rf/defn handle-mailserver-available
[{:keys [db] :as cofx} ms] [{:keys [db] :as cofx} ms]
{::cancel-connection-popup [] {::cancel-connection-popup []
:db (assoc db :mailserver/state :connected :db (assoc db
:mailserver/current-id (keyword ms))}) :mailserver/state :connected
:mailserver/current-id (keyword ms))})
(fx/defn connected-to-mailserver (rf/defn connected-to-mailserver
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(let [{:keys [address]} (fetch-current db)] (let [{:keys [address]} (fetch-current db)]
(fx/merge (rf/merge
cofx cofx
{:mailserver/update-mailservers [[address] #(re-frame/dispatch [::request-messages])]}))) {:mailserver/update-mailservers [[address] #(re-frame/dispatch [::request-messages])]})))
(fx/defn handle-request-success (rf/defn handle-request-success
{:events [:mailserver.callback/request-success]} {:events [:mailserver.callback/request-success]}
[{{:keys [chats] :as db} :db} {:keys [request-id topics]}] [{{:keys [chats] :as db} :db} {:keys [request-id topics]}]
(when (:mailserver/current-request db) (when (:mailserver/current-request db)
{:db (assoc-in db [:mailserver/current-request :request-id] {:db (assoc-in db
request-id)})) [:mailserver/current-request :request-id]
request-id)}))
(fx/defn toggle-use-mailservers (rf/defn toggle-use-mailservers
[cofx value] [cofx value]
{::json-rpc/call {::json-rpc/call
[{:method "wakuext_toggleUseMailservers" [{:method "wakuext_toggleUseMailservers"
:params [value] :params [value]
:on-success #(log/info "successfully toggled use-mailservers" value) :on-success #(log/info "successfully toggled use-mailservers" value)
:on-failure #(log/error "failed to toggle use-mailserver" value %)}]}) :on-failure #(log/error "failed to toggle use-mailserver" value %)}]})
(fx/defn update-use-mailservers (rf/defn update-use-mailservers
{:events [:mailserver.ui/use-history-switch-pressed]} {:events [:mailserver.ui/use-history-switch-pressed]}
[cofx use-mailservers?] [cofx use-mailservers?]
(fx/merge cofx (rf/merge cofx
(multiaccounts.update/optimistic :use-mailservers? use-mailservers?) (multiaccounts.update/optimistic :use-mailservers? use-mailservers?)
(toggle-use-mailservers use-mailservers?) (toggle-use-mailservers use-mailservers?)
(when use-mailservers? (when use-mailservers?
(disconnect-from-mailserver)))) (disconnect-from-mailserver))))
(fx/defn retry-next-messages-request (rf/defn retry-next-messages-request
{:events [:mailserver.ui/retry-request-pressed]} {:events [:mailserver.ui/retry-request-pressed]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(fx/merge cofx (rf/merge cofx
{:db (dissoc db :mailserver/request-error)} {:db (dissoc db :mailserver/request-error)}
(process-next-messages-request))) (process-next-messages-request)))
(fx/defn show-request-error-popup (rf/defn show-request-error-popup
{:events [:mailserver.ui/request-error-pressed]} {:events [:mailserver.ui/request-error-pressed]}
[{:keys [db]}] [{:keys [db]}]
(let [mailserver-error (:mailserver/request-error db)] (let [mailserver-error (:mailserver/request-error db)]
{:utils/show-confirmation {:utils/show-confirmation
{:title (i18n/label :t/mailserver-request-error-title) {:title (i18n/label :t/mailserver-request-error-title)
:content (i18n/label :t/mailserver-request-error-content :content (i18n/label :t/mailserver-request-error-content
{:error mailserver-error}) {:error mailserver-error})
:on-accept #(re-frame/dispatch [:mailserver.ui/retry-request-pressed]) :on-accept #(re-frame/dispatch [:mailserver.ui/retry-request-pressed])
:confirm-button-text (i18n/label :t/mailserver-request-retry)}})) :confirm-button-text (i18n/label :t/mailserver-request-retry)}}))
(def enode-url-regex (def enode-url-regex
#"enode://[a-zA-Z0-9]+\@\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b:(\d{1,5})") #"enode://[a-zA-Z0-9]+\@\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b:(\d{1,5})")
(defn- extract-url-components [address] (defn- extract-url-components
[address]
(rest (re-matches #"enode://(.*)@(.*)" address))) (rest (re-matches #"enode://(.*)@(.*)" address)))
(defn valid-enode-url? [address] (defn valid-enode-url?
[address]
(re-matches enode-url-regex address)) (re-matches enode-url-regex address))
(defn build-url [address] (defn build-url
[address]
(let [[initial host] (extract-url-components address)] (let [[initial host] (extract-url-components address)]
(str "enode://" initial "@" host))) (str "enode://" initial "@" host)))
(fx/defn set-input (rf/defn set-input
{:events [:mailserver.ui/input-changed]} {:events [:mailserver.ui/input-changed]}
[{:keys [db]} input-key value] [{:keys [db]} input-key value]
{:db (update {:db (update
@@ -216,26 +231,28 @@
:name (string/blank? value) :name (string/blank? value)
:url (not (valid-enode-url? value)))})}) :url (not (valid-enode-url? value)))})})
(defn- address->mailserver [address] (defn- address->mailserver
[address]
(let [[enode url :as response] (extract-url-components address)] (let [[enode url :as response] (extract-url-components address)]
{:address (if (seq response) {:address (if (seq response)
(str "enode://" enode "@" url) (str "enode://" enode "@" url)
address) address)
:custom true})) :custom true}))
(defn- build [id mailserver-name address] (defn- build
[id mailserver-name address]
(assoc (address->mailserver address) (assoc (address->mailserver address)
:id id :id id
:name mailserver-name)) :name mailserver-name))
(def default? (comp not :custom fetch)) (def default? (comp not :custom fetch))
(fx/defn edit (rf/defn edit
{:events [:mailserver.ui/custom-mailserver-selected]} {:events [:mailserver.ui/custom-mailserver-selected]}
[{:keys [db] :as cofx} id] [{:keys [db] :as cofx} id]
(let [{:keys [id address name]} (fetch db id) (let [{:keys [id address name]} (fetch db id)
url (when address (build-url address))] url (when address (build-url address))]
(fx/merge cofx (rf/merge cofx
(set-input :id id) (set-input :id id)
(set-input :url (str url)) (set-input :url (str url))
(set-input :name (str name)) (set-input :name (str name))
@@ -247,14 +264,15 @@
(assoc :fleet (name current-fleet)) (assoc :fleet (name current-fleet))
(update :id name))) (update :id name)))
(fx/defn upsert (rf/defn upsert
{:events [:mailserver.ui/save-pressed] {:events [:mailserver.ui/save-pressed]
:interceptors [(re-frame/inject-cofx :random-id-generator)]} :interceptors [(re-frame/inject-cofx :random-id-generator)]}
[{{:mailserver.edit/keys [mailserver] :keys [multiaccount] :as db} :db [{{:mailserver.edit/keys [mailserver] :keys [multiaccount] :as db} :db
random-id-generator :random-id-generator :as cofx}] random-id-generator :random-id-generator
:as cofx}]
(let [{:keys [name url id]} mailserver (let [{:keys [name url id]} mailserver
current-fleet (node/current-fleet-key db)] current-fleet (node/current-fleet-key db)]
(when (and (not (string/blank? (:value name))) (when (and (not (string/blank? (:value name)))
(valid-enode-url? (:value url))) (valid-enode-url? (:value url)))
@@ -263,14 +281,14 @@
(keyword (string/replace (random-id-generator) "-" ""))) (keyword (string/replace (random-id-generator) "-" "")))
(:value name) (:value name)
(:value url)) (:value url))
current (connected? db (:id mailserver))] current (connected? db (:id mailserver))]
{:db (-> db {:db (-> db
(dissoc :mailserver.edit/mailserver) (dissoc :mailserver.edit/mailserver)
(assoc-in [:mailserver/mailservers current-fleet (:id mailserver)] (assoc-in [:mailserver/mailservers current-fleet (:id mailserver)]
mailserver)) mailserver))
::json-rpc/call ::json-rpc/call
[{:method "mailservers_addMailserver" [{:method "mailservers_addMailserver"
:params [(mailserver->rpc mailserver current-fleet)] :params [(mailserver->rpc mailserver current-fleet)]
:on-success (fn [] :on-success (fn []
;; we naively logout if the user is connected to ;; we naively logout if the user is connected to
;; the edited mailserver ;; the edited mailserver
@@ -279,47 +297,49 @@
[:multiaccounts.logout.ui/logout-confirmed])) [:multiaccounts.logout.ui/logout-confirmed]))
(log/debug "saved mailserver" id "successfuly")) (log/debug "saved mailserver" id "successfuly"))
:on-failure #(log/error "failed to save mailserver" id %)}] :on-failure #(log/error "failed to save mailserver" id %)}]
:dispatch [:navigate-back]})))) :dispatch [:navigate-back]}))))
(defn can-delete? (defn can-delete?
[db id] [db id]
(not (or (default? db id) (not (or (default? db id)
(connected? db id)))) (connected? db id))))
(fx/defn delete (rf/defn delete
{:events [:mailserver.ui/delete-confirmed]} {:events [:mailserver.ui/delete-confirmed]}
[{:keys [db] :as cofx} id] [{:keys [db] :as cofx} id]
(if (can-delete? db id) (if (can-delete? db id)
{:db (-> db {:db (-> db
(update-in (update-in
[:mailserver/mailservers (node/current-fleet-key db)] [:mailserver/mailservers (node/current-fleet-key db)]
dissoc id) dissoc
(dissoc :mailserver.edit/mailserver)) id)
(dissoc :mailserver.edit/mailserver))
::json-rpc/call ::json-rpc/call
[{:method "mailservers_deleteMailserver" [{:method "mailservers_deleteMailserver"
:params [(name id)] :params [(name id)]
:on-success #(log/debug "deleted mailserver" id) :on-success #(log/debug "deleted mailserver" id)
:on-failure #(log/error "failed to delete mailserver" id %)}] :on-failure #(log/error "failed to delete mailserver" id %)}]
:dispatch [:navigate-back]} :dispatch [:navigate-back]}
{:dispatch [:navigate-back]})) {:dispatch [:navigate-back]}))
(fx/defn show-connection-confirmation (rf/defn show-connection-confirmation
{:events [:mailserver.ui/default-mailserver-selected :mailserver.ui/connect-pressed]} {:events [:mailserver.ui/default-mailserver-selected :mailserver.ui/connect-pressed]}
[{:keys [db]} mailserver-id] [{:keys [db]} mailserver-id]
(let [current-fleet (node/current-fleet-key db)] (let [current-fleet (node/current-fleet-key db)]
{:ui/show-confirmation {:ui/show-confirmation
{:title (i18n/label :t/close-app-title) {:title (i18n/label :t/close-app-title)
:content :content
(i18n/label :t/connect-mailserver-content (i18n/label :t/connect-mailserver-content
{:name (get-in db [:mailserver/mailservers {:name (get-in db
current-fleet mailserver-id :name])}) [:mailserver/mailservers
current-fleet mailserver-id :name])})
:confirm-button-text (i18n/label :t/close-app-button) :confirm-button-text (i18n/label :t/close-app-button)
:on-accept :on-accept
#(re-frame/dispatch #(re-frame/dispatch
[:mailserver.ui/connect-confirmed current-fleet mailserver-id]) [:mailserver.ui/connect-confirmed current-fleet mailserver-id])
:on-cancel nil}})) :on-cancel nil}}))
(fx/defn show-delete-confirmation (rf/defn show-delete-confirmation
{:events [:mailserver.ui/delete-pressed]} {:events [:mailserver.ui/delete-pressed]}
[{:keys [db]} mailserver-id] [{:keys [db]} mailserver-id]
{:ui/show-confirmation {:ui/show-confirmation
@@ -329,64 +349,67 @@
:on-accept #(re-frame/dispatch :on-accept #(re-frame/dispatch
[:mailserver.ui/delete-confirmed mailserver-id])}}) [:mailserver.ui/delete-confirmed mailserver-id])}})
(fx/defn set-url-from-qr (rf/defn set-url-from-qr
{:events [:mailserver.callback/qr-code-scanned]} {:events [:mailserver.callback/qr-code-scanned]}
[cofx url] [cofx url]
(assoc (set-input cofx :url url) (assoc (set-input cofx :url url)
:dispatch [:navigate-back])) :dispatch
[:navigate-back]))
(fx/defn dismiss-connection-error (rf/defn dismiss-connection-error
{:events [:mailserver.ui/dismiss-connection-error]} {:events [:mailserver.ui/dismiss-connection-error]}
[{:keys [db]} new-state] [{:keys [db]} new-state]
{:db (assoc db :mailserver/connection-error-dismissed new-state)}) {:db (assoc db :mailserver/connection-error-dismissed new-state)})
(fx/defn pin-mailserver (rf/defn pin-mailserver
{:events [:mailserver.ui/connect-confirmed]} {:events [:mailserver.ui/connect-confirmed]}
[{:keys [db] :as cofx} current-fleet mailserver-id] [{:keys [db] :as cofx} current-fleet mailserver-id]
(let [pinned-mailservers (-> db (let [pinned-mailservers (-> db
(get-in [:multiaccount :pinned-mailservers]) (get-in [:multiaccount :pinned-mailservers])
(assoc current-fleet mailserver-id))] (assoc current-fleet mailserver-id))]
(fx/merge cofx (rf/merge cofx
{:db (assoc db :mailserver/current-id mailserver-id) {:db (assoc db :mailserver/current-id mailserver-id)
::json-rpc/call [{:method "wakuext_setPinnedMailservers" ::json-rpc/call [{:method "wakuext_setPinnedMailservers"
:params [pinned-mailservers] :params [pinned-mailservers]
:on-success #(log/info "successfully pinned mailserver") :on-success #(log/info "successfully pinned mailserver")
:on-error #(log/error "failed to pin mailserver" %)}]} :on-error #(log/error "failed to pin mailserver" %)}]}
(multiaccounts.update/optimistic :pinned-mailservers pinned-mailservers)))) (multiaccounts.update/optimistic :pinned-mailservers pinned-mailservers))))
(fx/defn unpin (rf/defn unpin
{:events [:mailserver.ui/unpin-pressed]} {:events [:mailserver.ui/unpin-pressed]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(let [current-fleet (node/current-fleet-key db) (let [current-fleet (node/current-fleet-key db)
pinned-mailservers (-> db pinned-mailservers (-> db
(get-in [:multiaccount :pinned-mailservers]) (get-in [:multiaccount :pinned-mailservers])
(dissoc current-fleet))] (dissoc current-fleet))]
(fx/merge cofx (rf/merge cofx
{::json-rpc/call [{:method "wakuext_setPinnedMailservers" {::json-rpc/call [{:method "wakuext_setPinnedMailservers"
:params [pinned-mailservers] :params [pinned-mailservers]
:on-success #(log/info "successfully unpinned mailserver") :on-success #(log/info "successfully unpinned mailserver")
:on-error #(log/error "failed to unpin mailserver" %)}]} :on-error #(log/error "failed to unpin mailserver" %)}]}
(multiaccounts.update/optimistic (multiaccounts.update/optimistic
:pinned-mailservers pinned-mailservers) :pinned-mailservers
pinned-mailservers)
(dismiss-connection-error false)))) (dismiss-connection-error false))))
(fx/defn pin (rf/defn pin
{:events [:mailserver.ui/pin-pressed]} {:events [:mailserver.ui/pin-pressed]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(let [current-fleet (node/current-fleet-key db) (let [current-fleet (node/current-fleet-key db)
mailserver-id (:mailserver/current-id db) mailserver-id (:mailserver/current-id db)
pinned-mailservers (get-in db [:multiaccount :pinned-mailservers])] pinned-mailservers (get-in db [:multiaccount :pinned-mailservers])]
(fx/merge cofx (rf/merge cofx
(multiaccounts.update/multiaccount-update (multiaccounts.update/multiaccount-update
:pinned-mailservers (assoc pinned-mailservers :pinned-mailservers
current-fleet (assoc pinned-mailservers
mailserver-id) current-fleet
mailserver-id)
{}) {})
(dismiss-connection-error false)))) (dismiss-connection-error false))))
(fx/defn mailserver-ui-add-pressed (rf/defn mailserver-ui-add-pressed
{:events [:mailserver.ui/add-pressed]} {:events [:mailserver.ui/add-pressed]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(fx/merge cofx (rf/merge cofx
{:db (dissoc db :mailserver.edit/mailserver)} {:db (dissoc db :mailserver.edit/mailserver)}
(navigation/navigate-to-cofx :edit-mailserver nil))) (navigation/navigate-to-cofx :edit-mailserver nil)))
+37 -32
View File
@@ -1,28 +1,29 @@
(ns status-im.mobile-sync-settings.core (ns status-im.mobile-sync-settings.core
(:require (:require [status-im.bottom-sheet.core :as bottom-sheet]
[status-im.multiaccounts.update.core :as multiaccounts.update] [status-im.mailserver.core :as mailserver]
[status-im.utils.fx :as fx] [status-im.multiaccounts.model :as multiaccounts.model]
[status-im.wallet.core :as wallet] [status-im.multiaccounts.update.core :as multiaccounts.update]
[status-im.bottom-sheet.core :as bottom-sheet] [status-im.utils.mobile-sync :as utils]
[status-im.multiaccounts.model :as multiaccounts.model] [status-im.wallet.core :as wallet]
[status-im2.navigation.events :as navigation] [status-im2.navigation.events :as navigation]
[status-im.mailserver.core :as mailserver] [taoensso.timbre :as log]
[status-im.utils.mobile-sync :as utils] [utils.re-frame :as rf]))
[taoensso.timbre :as log]))
(fx/defn sheet-defaults (rf/defn sheet-defaults
[{:keys [db]}] [{:keys [db]}]
(let [remember-choice? (get-in db [:multiaccount :remember-syncing-choice?])] (let [remember-choice? (get-in db [:multiaccount :remember-syncing-choice?])]
{:db (assoc db :mobile-network/remember-choice? (or (nil? remember-choice?) {:db (assoc db
remember-choice?))})) :mobile-network/remember-choice?
(or (nil? remember-choice?)
remember-choice?))}))
(fx/defn on-network-status-change (rf/defn on-network-status-change
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(let [initialized? (get db :network-status/initialized?) (let [initialized? (get db :network-status/initialized?)
logged-in? (multiaccounts.model/logged-in? cofx) logged-in? (multiaccounts.model/logged-in? cofx)
{:keys [remember-syncing-choice?]} (:multiaccount db)] {:keys [remember-syncing-choice?]} (:multiaccount db)]
(apply (apply
fx/merge rf/merge
cofx cofx
{:db (assoc db :network-status/initialized? true)} {:db (assoc db :network-status/initialized? true)}
(cond (cond
@@ -51,67 +52,71 @@
([sync?] (apply-settings sync? :default)) ([sync?] (apply-settings sync? :default))
([sync? remember?] ([sync? remember?]
(fn [{:keys [db] :as cofx}] (fn [{:keys [db] :as cofx}]
(let [network (:network/type db) (let [network (:network/type db)
remember-choice? remember-choice?
(if (not= :default remember?) (if (not= :default remember?)
remember? remember?
(:mobile-network/remember-choice? db)) (:mobile-network/remember-choice? db))
cellular? (utils/cellular? network)] cellular? (utils/cellular? network)]
(log/info "apply mobile network settings" (log/info "apply mobile network settings"
"sunc?" sync? "sunc?" sync?
"remember?" remember? "remember?" remember?
"cellular?" cellular?) "cellular?" cellular?)
(fx/merge (rf/merge
cofx cofx
(multiaccounts.update/multiaccount-update (multiaccounts.update/multiaccount-update
:syncing-on-mobile-network? (boolean sync?) {}) :syncing-on-mobile-network?
(boolean sync?)
{})
(multiaccounts.update/multiaccount-update (multiaccounts.update/multiaccount-update
:remember-syncing-choice? (boolean remember-choice?) {}) :remember-syncing-choice?
(boolean remember-choice?)
{})
(when (and cellular? sync?) (when (and cellular? sync?)
(mailserver/process-next-messages-request)) (mailserver/process-next-messages-request))
(wallet/restart-wallet-service nil)))))) (wallet/restart-wallet-service nil))))))
(fx/defn mobile-network-continue-syncing (rf/defn mobile-network-continue-syncing
{:events [:mobile-network/continue-syncing]} {:events [:mobile-network/continue-syncing]}
[cofx] [cofx]
((apply-settings true) cofx)) ((apply-settings true) cofx))
(fx/defn mobile-network-stop-syncing (rf/defn mobile-network-stop-syncing
{:events [:mobile-network/stop-syncing]} {:events [:mobile-network/stop-syncing]}
[cofx] [cofx]
((apply-settings false) cofx)) ((apply-settings false) cofx))
(fx/defn mobile-network-set-syncing (rf/defn mobile-network-set-syncing
{:events [:mobile-network/set-syncing]} {:events [:mobile-network/set-syncing]}
[{:keys [db] :as cofx} syncing?] [{:keys [db] :as cofx} syncing?]
(let [{:keys [remember-syncing-choice?]} (:multiaccount db)] (let [{:keys [remember-syncing-choice?]} (:multiaccount db)]
((apply-settings syncing? remember-syncing-choice?) cofx))) ((apply-settings syncing? remember-syncing-choice?) cofx)))
(fx/defn mobile-network-ask-on-mobile-network? (rf/defn mobile-network-ask-on-mobile-network?
{:events [:mobile-network/ask-on-mobile-network?]} {:events [:mobile-network/ask-on-mobile-network?]}
[{:keys [db] :as cofx} ask?] [{:keys [db] :as cofx} ask?]
(let [{:keys [syncing-on-mobile-network?]} (:multiaccount db)] (let [{:keys [syncing-on-mobile-network?]} (:multiaccount db)]
((apply-settings syncing-on-mobile-network? (not ask?)) cofx))) ((apply-settings syncing-on-mobile-network? (not ask?)) cofx)))
(fx/defn mobile-network-restore-defaults (rf/defn mobile-network-restore-defaults
{:events [:mobile-network/restore-defaults]} {:events [:mobile-network/restore-defaults]}
[cofx] [cofx]
((apply-settings false false) cofx)) ((apply-settings false false) cofx))
(fx/defn mobile-network-remember-choice? (rf/defn mobile-network-remember-choice?
{:events [:mobile-network/remember-choice?]} {:events [:mobile-network/remember-choice?]}
[{:keys [db]} remember-choice?] [{:keys [db]} remember-choice?]
{:db (assoc db :mobile-network/remember-choice? remember-choice?)}) {:db (assoc db :mobile-network/remember-choice? remember-choice?)})
(fx/defn mobile-network-navigate-to-settings (rf/defn mobile-network-navigate-to-settings
{:events [:mobile-network/navigate-to-settings]} {:events [:mobile-network/navigate-to-settings]}
[cofx] [cofx]
(fx/merge (rf/merge
cofx cofx
(bottom-sheet/hide-bottom-sheet) (bottom-sheet/hide-bottom-sheet)
(navigation/navigate-to-cofx :mobile-network-settings nil))) (navigation/navigate-to-cofx :mobile-network-settings nil)))
(fx/defn mobile-network-show-offline-sheet (rf/defn mobile-network-show-offline-sheet
{:events [:mobile-network/show-offline-sheet]} {:events [:mobile-network/show-offline-sheet]}
[cofx] [cofx]
(bottom-sheet/show-bottom-sheet (bottom-sheet/show-bottom-sheet
+35 -28
View File
@@ -1,14 +1,14 @@
(ns status-im.multiaccounts.biometric.core (ns status-im.multiaccounts.biometric.core
(:require [re-frame.core :as re-frame] (:require ["react-native-touch-id" :default touchid]
[quo.design-system.colors :as colors]
[re-frame.core :as re-frame]
[status-im.i18n.i18n :as i18n] [status-im.i18n.i18n :as i18n]
[status-im.native-module.core :as status] [status-im.native-module.core :as status]
[status-im.popover.core :as popover] [status-im.popover.core :as popover]
[quo.design-system.colors :as colors]
[status-im.utils.fx :as fx]
[status-im.utils.keychain.core :as keychain] [status-im.utils.keychain.core :as keychain]
[status-im.utils.platform :as platform] [status-im.utils.platform :as platform]
[taoensso.timbre :as log] [taoensso.timbre :as log]
["react-native-touch-id" :default touchid])) [utils.re-frame :as rf]))
;; currently, for android, react-native-touch-id ;; currently, for android, react-native-touch-id
;; is not returning supported biometric type ;; is not returning supported biometric type
@@ -28,11 +28,12 @@
(def android-device-blacklisted? (def android-device-blacklisted?
(cond (cond
(= (:brand deviceinfo) "bannedbrand") true (= (:brand deviceinfo) "bannedbrand") true
:else false)) :else false))
;; biometric auth config ;; biometric auth config
;; https://github.com/naoufal/react-native-touch-id#authenticatereason-config ;; https://github.com/naoufal/react-native-touch-id#authenticatereason-config
(defn- authenticate-options [ios-fallback-label] (defn- authenticate-options
[ios-fallback-label]
(clj->js (merge (clj->js (merge
{:unifiedErrors true} {:unifiedErrors true}
(when platform/ios? (when platform/ios?
@@ -46,7 +47,8 @@
:sensorErrorDescription (i18n/label :t/biometric-auth-android-sensor-error-desc) :sensorErrorDescription (i18n/label :t/biometric-auth-android-sensor-error-desc)
:cancelText (i18n/label :cancel)})))) :cancelText (i18n/label :cancel)}))))
(defn get-label [supported-biometric-auth] (defn get-label
[supported-biometric-auth]
(case supported-biometric-auth (case supported-biometric-auth
:fingerprint (i18n/label :t/biometric-fingerprint) :fingerprint (i18n/label :t/biometric-fingerprint)
:FaceID (i18n/label :t/biometric-faceid) :FaceID (i18n/label :t/biometric-faceid)
@@ -61,29 +63,33 @@
(= touchid-error-code "USER_FALLBACK") nil (= touchid-error-code "USER_FALLBACK") nil
;; add here more specific errors if needed ;; add here more specific errors if needed
;; https://github.com/naoufal/react-native-touch-id#unified-errors ;; https://github.com/naoufal/react-native-touch-id#unified-errors
:else (i18n/label :t/biometric-auth-error {:code touchid-error-code}))) :else (i18n/label :t/biometric-auth-error
{:code touchid-error-code})))
(def success-result (def success-result
{:bioauth-success true}) {:bioauth-success true})
(defn- generate-error-result [touchid-error-obj] (defn- generate-error-result
[touchid-error-obj]
(let [code (aget touchid-error-obj "code")] (let [code (aget touchid-error-obj "code")]
{:bioauth-success false {:bioauth-success false
:bioauth-code code :bioauth-code code
:bioauth-message (get-error-message code)})) :bioauth-message (get-error-message code)}))
(defn- do-get-supported [callback] (defn- do-get-supported
[callback]
(-> (.isSupported touchid) (-> (.isSupported touchid)
(.then #(callback (or (keyword %) android-default-support))) (.then #(callback (or (keyword %) android-default-support)))
(.catch #(callback nil)))) (.catch #(callback nil))))
(defn get-supported [callback] (defn get-supported
[callback]
(log/debug "[biometric] get-supported") (log/debug "[biometric] get-supported")
(cond platform/ios? (do-get-supported callback) (cond platform/ios? (do-get-supported callback)
platform/android? (if android-device-blacklisted? platform/android? (if android-device-blacklisted?
(callback nil) (callback nil)
(do-get-supported callback)) (do-get-supported callback))
:else (callback nil))) :else (callback nil)))
(defn authenticate-fx (defn authenticate-fx
([cb] ([cb]
@@ -105,12 +111,12 @@
(get-supported callback) (get-supported callback)
(callback nil))))))) (callback nil)))))))
(fx/defn set-supported-biometric-auth (rf/defn set-supported-biometric-auth
{:events [:init.callback/get-supported-biometric-auth-success]} {:events [:init.callback/get-supported-biometric-auth-success]}
[{:keys [db]} supported-biometric-auth] [{:keys [db]} supported-biometric-auth]
{:db (assoc db :supported-biometric-auth supported-biometric-auth)}) {:db (assoc db :supported-biometric-auth supported-biometric-auth)})
(fx/defn authenticate (rf/defn authenticate
[_ cb options] [_ cb options]
{:biometric-auth/authenticate [cb options]}) {:biometric-auth/authenticate [cb options]})
@@ -119,10 +125,11 @@
(fn [[cb options]] (fn [[cb options]]
(authenticate-fx #(cb %) options))) (authenticate-fx #(cb %) options)))
(fx/defn update-biometric [{db :db :as cofx} biometric-auth?] (rf/defn update-biometric
[{db :db :as cofx} biometric-auth?]
(let [key-uid (or (get-in db [:multiaccount :key-uid]) (let [key-uid (or (get-in db [:multiaccount :key-uid])
(get-in db [:multiaccounts/login :key-uid]))] (get-in db [:multiaccounts/login :key-uid]))]
(fx/merge cofx (rf/merge cofx
(keychain/save-auth-method (keychain/save-auth-method
key-uid key-uid
(if biometric-auth? (if biometric-auth?
@@ -131,7 +138,7 @@
#(when-not biometric-auth? #(when-not biometric-auth?
{:keychain/clear-user-password key-uid})))) {:keychain/clear-user-password key-uid}))))
(fx/defn biometric-auth-switched (rf/defn biometric-auth-switched
{:events [:multiaccounts.ui/biometric-auth-switched]} {:events [:multiaccounts.ui/biometric-auth-switched]}
[cofx biometric-auth?] [cofx biometric-auth?]
(if biometric-auth? (if biometric-auth?
@@ -141,17 +148,17 @@
{}) {})
(update-biometric cofx false))) (update-biometric cofx false)))
(fx/defn show-message (rf/defn show-message
[cofx bioauth-message bioauth-code] [cofx bioauth-message bioauth-code]
(let [content (or (when (get #{"NOT_AVAILABLE" "NOT_ENROLLED"} bioauth-code) (let [content (or (when (get #{"NOT_AVAILABLE" "NOT_ENROLLED"} bioauth-code)
(i18n/label :t/grant-face-id-permissions)) (i18n/label :t/grant-face-id-permissions))
bioauth-message)] bioauth-message)]
(when content (when content
{:utils/show-popup {:utils/show-popup
{:title (i18n/label :t/biometric-auth-login-error-title) {:title (i18n/label :t/biometric-auth-login-error-title)
:content content}}))) :content content}})))
(fx/defn biometric-init-done (rf/defn biometric-init-done
{:events [:biometric-init-done]} {:events [:biometric-init-done]}
[cofx {:keys [bioauth-success bioauth-message bioauth-code]}] [cofx {:keys [bioauth-success bioauth-message bioauth-code]}]
(if bioauth-success (if bioauth-success
@@ -161,7 +168,7 @@
(popover/show-popover cofx {:view :enable-biometric})) (popover/show-popover cofx {:view :enable-biometric}))
(show-message cofx bioauth-message bioauth-code))) (show-message cofx bioauth-message bioauth-code)))
(fx/defn biometric-auth (rf/defn biometric-auth
{:events [:biometric-authenticate]} {:events [:biometric-authenticate]}
[cofx] [cofx]
(authenticate (authenticate
@@ -170,31 +177,31 @@
{:reason (i18n/label :t/biometric-auth-reason-login) {:reason (i18n/label :t/biometric-auth-reason-login)
:ios-fallback-label (i18n/label :t/biometric-auth-login-ios-fallback-label)})) :ios-fallback-label (i18n/label :t/biometric-auth-login-ios-fallback-label)}))
(fx/defn enable (rf/defn enable
{:events [:biometric/enable]} {:events [:biometric/enable]}
[cofx] [cofx]
(fx/merge (rf/merge
cofx cofx
(popover/hide-popover) (popover/hide-popover)
(authenticate #(re-frame/dispatch [:biometric/setup-done %]) {}))) (authenticate #(re-frame/dispatch [:biometric/setup-done %]) {})))
(fx/defn disable (rf/defn disable
{:events [:biometric/disable]} {:events [:biometric/disable]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(fx/merge (rf/merge
cofx cofx
{:db (-> db {:db (-> db
(assoc :auth-method keychain/auth-method-none) (assoc :auth-method keychain/auth-method-none)
(assoc-in [:multiaccounts/login :save-password?] false))} (assoc-in [:multiaccounts/login :save-password?] false))}
(popover/hide-popover))) (popover/hide-popover)))
(fx/defn setup-done (rf/defn setup-done
{:events [:biometric/setup-done]} {:events [:biometric/setup-done]}
[{:keys [db] :as cofx} {:keys [bioauth-success bioauth-message bioauth-code]}] [{:keys [db] :as cofx} {:keys [bioauth-success bioauth-message bioauth-code]}]
(log/debug "[biometric] setup-done" (log/debug "[biometric] setup-done"
"bioauth-success" bioauth-success "bioauth-success" bioauth-success
"bioauth-message" bioauth-message "bioauth-message" bioauth-message
"bioauth-code" bioauth-code) "bioauth-code" bioauth-code)
(if bioauth-success (if bioauth-success
{:db (assoc db :auth-method keychain/auth-method-biometric-prepare)} {:db (assoc db :auth-method keychain/auth-method-biometric-prepare)}
(show-message cofx bioauth-message bioauth-code))) (show-message cofx bioauth-message bioauth-code)))
+63 -46
View File
@@ -1,19 +1,19 @@
(ns status-im.multiaccounts.core (ns status-im.multiaccounts.core
(:require [re-frame.core :as re-frame] (:require [clojure.string :as string]
[quo.platform :as platform]
[re-frame.core :as re-frame]
[status-im.bottom-sheet.core :as bottom-sheet]
[status-im.ethereum.json-rpc :as json-rpc]
[status-im.ethereum.stateofus :as stateofus] [status-im.ethereum.stateofus :as stateofus]
[status-im.multiaccounts.update.core :as multiaccounts.update] [status-im.multiaccounts.update.core :as multiaccounts.update]
[status-im.bottom-sheet.core :as bottom-sheet]
[status-im.native-module.core :as native-module] [status-im.native-module.core :as native-module]
[status-im.ethereum.json-rpc :as json-rpc] [status-im.theme.core :as theme]
[status-im.utils.fx :as fx]
[status-im.utils.gfycat.core :as gfycat] [status-im.utils.gfycat.core :as gfycat]
[status-im.utils.identicon :as identicon] [status-im.utils.identicon :as identicon]
[status-im.theme.core :as theme]
[status-im.utils.utils :as utils] [status-im.utils.utils :as utils]
[quo.platform :as platform] [status-im2.common.theme.core :as utils.theme]
[taoensso.timbre :as log] [taoensso.timbre :as log]
[clojure.string :as string] [utils.re-frame :as rf]))
[status-im2.common.theme.core :as utils.theme]))
;; validate that the given mnemonic was generated from Status Dictionary ;; validate that the given mnemonic was generated from Status Dictionary
(re-frame/reg-fx (re-frame/reg-fx
@@ -35,7 +35,7 @@
(cond-> {:nickname nickname (cond-> {:nickname nickname
:display-name display-name :display-name display-name
:three-words-name (or alias (gfycat/generate-gfy public-key))} :three-words-name (or alias (gfycat/generate-gfy public-key))}
;; Preferred name is our own otherwise we make sure it's verified ;; Preferred name is our own otherwise we make sure it's verified
(or preferred-name (and ens-verified name)) (or preferred-name (and ens-verified name))
(assoc :ens-name (str "@" (or (stateofus/username ens-name) ens-name)))))) (assoc :ens-name (str "@" (or (stateofus/username ens-name) ens-name))))))
@@ -48,7 +48,8 @@
(let [{:keys [nickname (let [{:keys [nickname
ens-name ens-name
display-name display-name
three-words-name]} (or names (contact-names contact)) three-words-name]}
(or names (contact-names contact))
short-public-key (when public-key? (utils/get-shortened-address public-key))] short-public-key (when public-key? (utils/get-shortened-address public-key))]
(->> [nickname ens-name display-name three-words-name short-public-key] (->> [nickname ens-name display-name three-words-name short-public-key]
(remove string/blank?) (remove string/blank?)
@@ -70,11 +71,13 @@
(str "@" (or username ens-name))) (str "@" (or username ens-name)))
(or alias (gfycat/generate-gfy public-key))))) (or alias (gfycat/generate-gfy public-key)))))
(defn contact-by-identity [contacts identity] (defn contact-by-identity
[contacts identity]
(or (get contacts identity) (or (get contacts identity)
(contact-with-names {:public-key identity}))) (contact-with-names {:public-key identity})))
(defn contact-two-names-by-identity [contact current-multiaccount identity] (defn contact-two-names-by-identity
[contact current-multiaccount identity]
(let [me? (= (:public-key current-multiaccount) identity)] (let [me? (= (:public-key current-multiaccount) identity)]
(if me? (if me?
[(or (:preferred-name current-multiaccount) [(or (:preferred-name current-multiaccount)
@@ -110,53 +113,61 @@
(fn [flag] (fn [flag]
(native-module/set-blank-preview-flag flag))) (native-module/set-blank-preview-flag flag)))
(fx/defn confirm-wallet-set-up (rf/defn confirm-wallet-set-up
{:events [:multiaccounts.ui/wallet-set-up-confirmed]} {:events [:multiaccounts.ui/wallet-set-up-confirmed]}
[cofx] [cofx]
(multiaccounts.update/multiaccount-update cofx (multiaccounts.update/multiaccount-update cofx
:wallet-set-up-passed? true {})) :wallet-set-up-passed?
true
{}))
(fx/defn confirm-home-tooltip (rf/defn confirm-home-tooltip
{:events [:multiaccounts.ui/hide-home-tooltip]} {:events [:multiaccounts.ui/hide-home-tooltip]}
[cofx] [cofx]
(multiaccounts.update/multiaccount-update cofx (multiaccounts.update/multiaccount-update cofx
:hide-home-tooltip? true {})) :hide-home-tooltip?
true
{}))
(fx/defn switch-webview-debug (rf/defn switch-webview-debug
{:events [:multiaccounts.ui/switch-webview-debug]} {:events [:multiaccounts.ui/switch-webview-debug]}
[{:keys [db] :as cofx} value] [{:keys [db] :as cofx} value]
(fx/merge cofx (rf/merge cofx
{::webview-debug-changed value} {::webview-debug-changed value}
(multiaccounts.update/multiaccount-update (multiaccounts.update/multiaccount-update
:webview-debug (boolean value) :webview-debug
(boolean value)
{}))) {})))
(fx/defn switch-preview-privacy-mode (rf/defn switch-preview-privacy-mode
{:events [:multiaccounts.ui/preview-privacy-mode-switched]} {:events [:multiaccounts.ui/preview-privacy-mode-switched]}
[{:keys [db] :as cofx} private?] [{:keys [db] :as cofx} private?]
(fx/merge cofx (rf/merge cofx
{::blank-preview-flag-changed private?} {::blank-preview-flag-changed private?}
(multiaccounts.update/multiaccount-update (multiaccounts.update/multiaccount-update
:preview-privacy? (boolean private?) :preview-privacy?
(boolean private?)
{}))) {})))
(fx/defn switch-webview-permission-requests? (rf/defn switch-webview-permission-requests?
{:events [:multiaccounts.ui/webview-permission-requests-switched]} {:events [:multiaccounts.ui/webview-permission-requests-switched]}
[cofx enabled?] [cofx enabled?]
(multiaccounts.update/multiaccount-update (multiaccounts.update/multiaccount-update
cofx cofx
:webview-allow-permission-requests? (boolean enabled?) :webview-allow-permission-requests?
(boolean enabled?)
{})) {}))
(fx/defn switch-default-sync-period (rf/defn switch-default-sync-period
{:events [:multiaccounts.ui/default-sync-period-switched]} {:events [:multiaccounts.ui/default-sync-period-switched]}
[cofx value] [cofx value]
(multiaccounts.update/multiaccount-update (multiaccounts.update/multiaccount-update
cofx cofx
:default-sync-period value :default-sync-period
value
{})) {}))
(fx/defn switch-preview-privacy-mode-flag (rf/defn switch-preview-privacy-mode-flag
[{:keys [db]}] [{:keys [db]}]
(let [private? (get-in db [:multiaccount :preview-privacy?])] (let [private? (get-in db [:multiaccount :preview-privacy?])]
{::blank-preview-flag-changed private?})) {::blank-preview-flag-changed private?}))
@@ -169,48 +180,49 @@
:light)] :light)]
(theme/change-theme theme)))) (theme/change-theme theme))))
(fx/defn switch-appearance (rf/defn switch-appearance
{:events [:multiaccounts.ui/appearance-switched]} {:events [:multiaccounts.ui/appearance-switched]}
[cofx theme] [cofx theme]
(fx/merge cofx (rf/merge cofx
{:multiaccounts.ui/switch-theme theme} {:multiaccounts.ui/switch-theme theme}
(multiaccounts.update/multiaccount-update :appearance theme {}))) (multiaccounts.update/multiaccount-update :appearance theme {})))
(fx/defn switch-profile-picture-show-to (rf/defn switch-profile-picture-show-to
{:events [:multiaccounts.ui/profile-picture-show-to-switched]} {:events [:multiaccounts.ui/profile-picture-show-to-switched]}
[cofx id] [cofx id]
(fx/merge cofx (rf/merge cofx
{::json-rpc/call [{:method "wakuext_changeIdentityImageShowTo" {::json-rpc/call [{:method "wakuext_changeIdentityImageShowTo"
:params [id] :params [id]
:on-success #(log/debug "picture settings changed successfully")}]} :on-success #(log/debug "picture settings changed successfully")}]}
(multiaccounts.update/optimistic :profile-pictures-show-to id))) (multiaccounts.update/optimistic :profile-pictures-show-to id)))
(fx/defn switch-appearance-profile (rf/defn switch-appearance-profile
{:events [:multiaccounts.ui/appearance-profile-switched]} {:events [:multiaccounts.ui/appearance-profile-switched]}
[cofx id] [cofx id]
(multiaccounts.update/multiaccount-update cofx :profile-pictures-visibility id {})) (multiaccounts.update/multiaccount-update cofx :profile-pictures-visibility id {}))
(defn clean-path [path] (defn clean-path
[path]
(if path (if path
(string/replace-first path #"file://" "") (string/replace-first path #"file://" "")
(log/warn "[native-module] Empty path was provided"))) (log/warn "[native-module] Empty path was provided")))
(fx/defn save-profile-picture (rf/defn save-profile-picture
{:events [::save-profile-picture]} {:events [::save-profile-picture]}
[cofx path ax ay bx by] [cofx path ax ay bx by]
(let [key-uid (get-in cofx [:db :multiaccount :key-uid])] (let [key-uid (get-in cofx [:db :multiaccount :key-uid])]
(fx/merge cofx (rf/merge cofx
{::json-rpc/call [{:method "multiaccounts_storeIdentityImage" {::json-rpc/call [{:method "multiaccounts_storeIdentityImage"
:params [key-uid (clean-path path) ax ay bx by] :params [key-uid (clean-path path) ax ay bx by]
;; NOTE: In case of an error we can show a toast error ;; NOTE: In case of an error we can show a toast error
:on-success #(re-frame/dispatch [::update-local-picture %])}]} :on-success #(re-frame/dispatch [::update-local-picture %])}]}
(bottom-sheet/hide-bottom-sheet)))) (bottom-sheet/hide-bottom-sheet))))
(fx/defn save-profile-picture-from-url (rf/defn save-profile-picture-from-url
{:events [::save-profile-picture-from-url]} {:events [::save-profile-picture-from-url]}
[cofx url] [cofx url]
(let [key-uid (get-in cofx [:db :multiaccount :key-uid])] (let [key-uid (get-in cofx [:db :multiaccount :key-uid])]
(fx/merge cofx (rf/merge cofx
{::json-rpc/call [{:method "multiaccounts_storeIdentityImageFromURL" {::json-rpc/call [{:method "multiaccounts_storeIdentityImageFromURL"
:params [key-uid url] :params [key-uid url]
:on-error #(log/error "::save-profile-picture-from-url error" %) :on-error #(log/error "::save-profile-picture-from-url error" %)
@@ -218,32 +230,37 @@
(bottom-sheet/hide-bottom-sheet)))) (bottom-sheet/hide-bottom-sheet))))
(comment (comment
(re-frame/dispatch [::save-profile-picture-from-url "https://lh3.googleusercontent.com/XuKjNm3HydsaxbPkbpGs9YyCKhn5QQk5oDC8XF2jzmPyYXeZofxFtfUDZuQ3EVmacS_BlBKzbX2ypm37YNX3n1fDJA3WndeFcPsp7Z0=w600"])) (re-frame/dispatch
[::save-profile-picture-from-url
"https://lh3.googleusercontent.com/XuKjNm3HydsaxbPkbpGs9YyCKhn5QQk5oDC8XF2jzmPyYXeZofxFtfUDZuQ3EVmacS_BlBKzbX2ypm37YNX3n1fDJA3WndeFcPsp7Z0=w600"]))
(fx/defn delete-profile-picture (rf/defn delete-profile-picture
{:events [::delete-profile-picture]} {:events [::delete-profile-picture]}
[cofx name] [cofx name]
(let [key-uid (get-in cofx [:db :multiaccount :key-uid])] (let [key-uid (get-in cofx [:db :multiaccount :key-uid])]
(fx/merge cofx (rf/merge cofx
{::json-rpc/call [{:method "multiaccounts_deleteIdentityImage" {::json-rpc/call [{:method "multiaccounts_deleteIdentityImage"
:params [key-uid] :params [key-uid]
;; NOTE: In case of an error we could fallback to previous image in UI with a toast error ;; NOTE: In case of an error we could fallback to previous image in UI
;; with a toast error
:on-success #(log/info "[multiaccount] Delete profile image" %)}]} :on-success #(log/info "[multiaccount] Delete profile image" %)}]}
(multiaccounts.update/optimistic :images nil) (multiaccounts.update/optimistic :images nil)
(bottom-sheet/hide-bottom-sheet)))) (bottom-sheet/hide-bottom-sheet))))
(fx/defn get-profile-picture (rf/defn get-profile-picture
[cofx] [cofx]
(let [key-uid (get-in cofx [:db :multiaccount :key-uid])] (let [key-uid (get-in cofx [:db :multiaccount :key-uid])]
{::json-rpc/call [{:method "multiaccounts_getIdentityImages" {::json-rpc/call [{:method "multiaccounts_getIdentityImages"
:params [key-uid] :params [key-uid]
:on-success #(re-frame/dispatch [::update-local-picture %])}]})) :on-success #(re-frame/dispatch [::update-local-picture %])}]}))
(fx/defn store-profile-picture (rf/defn store-profile-picture
{:events [::update-local-picture]} {:events [::update-local-picture]}
[cofx pics] [cofx pics]
(multiaccounts.update/optimistic cofx :images pics)) (multiaccounts.update/optimistic cofx :images pics))
(comment (comment
;; Test seed for Dim Venerated Yaffle, it's not here by mistake, this is just a test account ;; Test seed for Dim Venerated Yaffle, it's not here by mistake, this is just a test account
(native-module/validate-mnemonic "rocket mixed rebel affair umbrella legal resemble scene virus park deposit cargo" prn)) (native-module/validate-mnemonic
"rocket mixed rebel affair umbrella legal resemble scene virus park deposit cargo"
prn))
+96 -86
View File
@@ -1,5 +1,6 @@
(ns status-im.multiaccounts.create.core (ns status-im.multiaccounts.create.core
(:require [re-frame.core :as re-frame] (:require [quo.design-system.colors :as colors]
[re-frame.core :as re-frame]
[status-im.constants :as constants] [status-im.constants :as constants]
[status-im.data-store.settings :as data-store.settings] [status-im.data-store.settings :as data-store.settings]
[status-im.ethereum.core :as ethereum] [status-im.ethereum.core :as ethereum]
@@ -7,26 +8,27 @@
[status-im.i18n.i18n :as i18n] [status-im.i18n.i18n :as i18n]
[status-im.native-module.core :as status] [status-im.native-module.core :as status]
[status-im.node.core :as node] [status-im.node.core :as node]
[quo.design-system.colors :as colors]
[status-im.utils.config :as config] [status-im.utils.config :as config]
[status-im.utils.fx :as fx]
[utils.security.core :as security]
[status-im.utils.signing-phrase.core :as signing-phrase] [status-im.utils.signing-phrase.core :as signing-phrase]
[status-im.utils.types :as types])) [status-im.utils.types :as types]
[utils.re-frame :as rf]
[utils.security.core :as security]))
(defn normalize-derived-data-keys [derived-data] (defn normalize-derived-data-keys
[derived-data]
(->> derived-data (->> derived-data
(map (fn [[path {:keys [publicKey] :as data}]] (map (fn [[path {:keys [publicKey] :as data}]]
[path (cond-> (-> data [path
(dissoc :publicKey) (cond-> (-> data
(assoc :public-key publicKey)))])) (dissoc :publicKey)
(assoc :public-key publicKey)))]))
(into {}))) (into {})))
(defn normalize-multiaccount-data-keys (defn normalize-multiaccount-data-keys
[{:keys [publicKey keyUid derived] :as data}] [{:keys [publicKey keyUid derived] :as data}]
(cond-> (-> data (cond-> (-> data
(dissoc :keyUid :publicKey) (dissoc :keyUid :publicKey)
(assoc :key-uid keyUid (assoc :key-uid keyUid
:public-key publicKey)) :public-key publicKey))
derived derived
(update :derived normalize-derived-data-keys))) (update :derived normalize-derived-data-keys)))
@@ -49,7 +51,7 @@
hashed-password hashed-password
callback))) callback)))
(fx/defn create-multiaccount (rf/defn create-multiaccount
{:events [:create-multiaccount]} {:events [:create-multiaccount]}
[{:keys [db]} key-code] [{:keys [db]} key-code]
(let [{:keys [selected-id]} (:intro-wizard db)] (let [{:keys [selected-id]} (:intro-wizard db)]
@@ -63,14 +65,14 @@
(ethereum/sha3 (security/safe-unmask-data key-code)) (ethereum/sha3 (security/safe-unmask-data key-code))
(fn [result] (fn [result]
(let [derived-data (normalize-derived-data-keys (types/json->clj result)) (let [derived-data (normalize-derived-data-keys (types/json->clj result))
public-key (get-in derived-data [constants/path-whisper-keyword :public-key])] public-key (get-in derived-data [constants/path-whisper-keyword :public-key])]
(status/gfycat-identicon-async (status/gfycat-identicon-async
public-key public-key
(fn [name identicon] (fn [name identicon]
(let [derived-whisper (derived-data constants/path-whisper-keyword) (let [derived-whisper (derived-data constants/path-whisper-keyword)
derived-data-extended (assoc-in derived-data derived-data-extended (assoc-in derived-data
[constants/path-whisper-keyword] [constants/path-whisper-keyword]
(merge derived-whisper {:name name :identicon identicon}))] (merge derived-whisper {:name name :identicon identicon}))]
(re-frame/dispatch [::store-multiaccount-success key-code derived-data-extended]))))))]})) (re-frame/dispatch [::store-multiaccount-success key-code derived-data-extended]))))))]}))
(re-frame/reg-fx (re-frame/reg-fx
@@ -86,32 +88,36 @@
(mapv normalize-multiaccount-data-keys (mapv normalize-multiaccount-data-keys
(types/json->clj %))])))) (types/json->clj %))]))))
(fx/defn multiaccount-generate-and-derive-addresses-success (rf/defn multiaccount-generate-and-derive-addresses-success
{:events [:multiaccount-generate-and-derive-addresses-success]} {:events [:multiaccount-generate-and-derive-addresses-success]}
[{:keys [db]} result] [{:keys [db]} result]
{:db (update db :intro-wizard {:db (update db
(fn [data] :intro-wizard
(-> data (fn [data]
(dissoc :processing?) (-> data
(assoc :multiaccounts result (dissoc :processing?)
:selected-storage-type :default (assoc :multiaccounts result
:selected-id (-> result first :id) :selected-storage-type :default
:step :choose-key)))) :selected-id (-> result first :id)
:step :choose-key))))
:navigate-to-fx :choose-name}) :navigate-to-fx :choose-name})
(fx/defn generate-and-derive-addresses (rf/defn generate-and-derive-addresses
{:events [:generate-and-derive-addresses]} {:events [:generate-and-derive-addresses]}
[{:keys [db]}] [{:keys [db]}]
{:db (-> db {:db (-> db
(update :intro-wizard #(-> % (assoc :processing? true) (dissoc :recovering?))) (update :intro-wizard
(dissoc :recovered-account?)) #(-> %
(assoc :processing? true)
(dissoc :recovering?)))
(dissoc :recovered-account?))
:multiaccount-generate-and-derive-addresses nil}) :multiaccount-generate-and-derive-addresses nil})
(fx/defn prepare-intro-wizard (rf/defn prepare-intro-wizard
[{:keys [db]}] [{:keys [db]}]
{:db (assoc db :intro-wizard {})}) {:db (assoc db :intro-wizard {})})
(fx/defn save-multiaccount-and-login-with-keycard (rf/defn save-multiaccount-and-login-with-keycard
[_ args] [_ args]
{:keycard/save-multiaccount-and-login args}) {:keycard/save-multiaccount-and-login args})
@@ -126,7 +132,7 @@
config config
accounts-data))) accounts-data)))
(fx/defn save-account-and-login (rf/defn save-account-and-login
[_ key-uid multiaccount-data password settings node-config accounts-data] [_ key-uid multiaccount-data password settings node-config accounts-data]
{::save-account-and-login [key-uid {::save-account-and-login [key-uid
(types/clj->json multiaccount-data) (types/clj->json multiaccount-data)
@@ -150,53 +156,57 @@
{:public-key public-key {:public-key public-key
:address (eip55/address->checksum address) :address (eip55/address->checksum address)
:name name :name name
:identicon identicon :identicon identicon
:path constants/path-whisper :path constants/path-whisper
:chat true})]) :chat true})])
(fx/defn on-multiaccount-created (rf/defn on-multiaccount-created
[{:keys [signing-phrase random-guid-generator db] :as cofx} [{:keys [signing-phrase random-guid-generator db] :as cofx}
{:keys [address chat-key keycard-instance-uid key-uid {:keys [address chat-key keycard-instance-uid key-uid
keycard-pairing keycard-paired-on mnemonic recovered] keycard-pairing keycard-paired-on mnemonic recovered]
:as multiaccount} :as multiaccount}
password password
{:keys [save-mnemonic? login?] :or {login? true save-mnemonic? false}}] {:keys [save-mnemonic? login?] :or {login? true save-mnemonic? false}}]
(let [[wallet-account {:keys [public-key identicon name]} :as accounts-data] (prepare-accounts-data multiaccount) (let [[wallet-account {:keys [public-key identicon name]} :as accounts-data] (prepare-accounts-data
multiaccount-data {:name name multiaccount)
:address address multiaccount-data {:name name
:identicon identicon :address address
:key-uid key-uid :identicon identicon
:key-uid key-uid
:keycard-pairing keycard-pairing} :keycard-pairing keycard-pairing}
keycard-multiaccount? (boolean keycard-pairing) keycard-multiaccount? (boolean keycard-pairing)
eip1581-address (get-in multiaccount [:derived eip1581-address (get-in multiaccount
constants/path-eip1581-keyword [:derived
:address]) constants/path-eip1581-keyword
:address])
new-multiaccount new-multiaccount
(cond-> (merge (cond->
{;; address of the master key (merge
:address address {;; address of the master key
;; sha256 of master public key :address address
:key-uid key-uid ;; sha256 of master public key
;; The address from which we derive any wallet :key-uid key-uid
:wallet-root-address ;; The address from which we derive any wallet
(get-in multiaccount [:derived :wallet-root-address
constants/path-wallet-root-keyword (get-in multiaccount
:address]) [:derived
:name name constants/path-wallet-root-keyword
:identicon identicon :address])
;; public key of the chat account :name name
:public-key public-key :identicon identicon
;; default address for Dapps ;; public key of the chat account
:dapps-address (:address wallet-account) :public-key public-key
:latest-derived-path 0 ;; default address for Dapps
:signing-phrase signing-phrase :dapps-address (:address wallet-account)
:send-push-notifications? true :latest-derived-path 0
:backup-enabled? true :signing-phrase signing-phrase
:installation-id (random-guid-generator) :send-push-notifications? true
;; default mailserver (history node) setting :backup-enabled? true
:use-mailservers? true :installation-id (random-guid-generator)
:recovered recovered} ;; default mailserver (history node) setting
config/default-multiaccount) :use-mailservers? true
:recovered recovered}
config/default-multiaccount)
;; The address from which we derive any chat ;; The address from which we derive any chat
;; account/encryption keys ;; account/encryption keys
eip1581-address eip1581-address
@@ -205,23 +215,23 @@
(assoc :mnemonic mnemonic) (assoc :mnemonic mnemonic)
keycard-multiaccount? keycard-multiaccount?
(assoc :keycard-instance-uid keycard-instance-uid (assoc :keycard-instance-uid keycard-instance-uid
:keycard-pairing keycard-pairing :keycard-pairing keycard-pairing
:keycard-paired-on keycard-paired-on)) :keycard-paired-on keycard-paired-on))
db (assoc db db (assoc db
:multiaccounts/login {:key-uid key-uid :multiaccounts/login {:key-uid key-uid
:name name :name name
:identicon identicon :identicon identicon
:password password :password password
:creating? true :creating? true
:processing true} :processing true}
:multiaccount new-multiaccount :multiaccount new-multiaccount
:multiaccount/accounts [wallet-account] :multiaccount/accounts [wallet-account]
:networks/current-network config/default-network :networks/current-network config/default-network
:networks/networks (data-store.settings/rpc->networks config/default-networks)) :networks/networks (data-store.settings/rpc->networks config/default-networks))
settings (assoc new-multiaccount settings (assoc new-multiaccount
:networks/current-network config/default-network :networks/current-network config/default-network
:networks/networks config/default-networks)] :networks/networks config/default-networks)]
(fx/merge cofx (rf/merge cofx
{:db db} {:db db}
(if keycard-multiaccount? (if keycard-multiaccount?
(save-multiaccount-and-login-with-keycard (save-multiaccount-and-login-with-keycard
@@ -240,26 +250,26 @@
(node/get-new-config db) (node/get-new-config db)
accounts-data))))) accounts-data)))))
(fx/defn store-multiaccount-success (rf/defn store-multiaccount-success
{:events [::store-multiaccount-success] {:events [::store-multiaccount-success]
:interceptors [(re-frame/inject-cofx :random-guid-generator) :interceptors [(re-frame/inject-cofx :random-guid-generator)
(re-frame/inject-cofx ::get-signing-phrase)]} (re-frame/inject-cofx ::get-signing-phrase)]}
[{:keys [db] :as cofx} password derived] [{:keys [db] :as cofx} password derived]
(fx/merge cofx (rf/merge cofx
{:db (dissoc db :intro-wizard)} {:db (dissoc db :intro-wizard)}
(on-multiaccount-created (assoc (let [{:keys [selected-id multiaccounts]} (:intro-wizard db)] (on-multiaccount-created (assoc (let [{:keys [selected-id multiaccounts]} (:intro-wizard db)]
(some #(when (= selected-id (:id %)) %) multiaccounts)) (some #(when (= selected-id (:id %)) %) multiaccounts))
:derived derived :derived derived
:recovered (get-in db [:intro-wizard :recovering?])) :recovered (get-in db [:intro-wizard :recovering?]))
password password
{:save-mnemonic? true}))) {:save-mnemonic? true})))
(fx/defn on-key-selected (rf/defn on-key-selected
{:events [:intro-wizard/on-key-selected]} {:events [:intro-wizard/on-key-selected]}
[{:keys [db]} id] [{:keys [db]} id]
{:db (assoc-in db [:intro-wizard :selected-id] id)}) {:db (assoc-in db [:intro-wizard :selected-id] id)})
(fx/defn on-key-storage-selected (rf/defn on-key-storage-selected
{:events [:intro-wizard/on-key-storage-selected]} {:events [:intro-wizard/on-key-storage-selected]}
[{:keys [db]} storage-type] [{:keys [db]} storage-type]
{:db (assoc-in db [:intro-wizard :selected-storage-type] storage-type)}) {:db (assoc-in db [:intro-wizard :selected-storage-type] storage-type)})
@@ -1,24 +1,24 @@
(ns status-im.multiaccounts.key-storage.core (ns status-im.multiaccounts.key-storage.core
(:require [clojure.string :as string] (:require [clojure.string :as string]
[re-frame.core :as re-frame] [re-frame.core :as re-frame]
[status-im.bottom-sheet.core :as bottom-sheet]
[status-im.ethereum.core :as ethereum]
[status-im.ethereum.mnemonic :as mnemonic] [status-im.ethereum.mnemonic :as mnemonic]
[status-im.i18n.i18n :as i18n]
[status-im.keycard.backup-key :as keycard.backup]
[status-im.keycard.common :as common] [status-im.keycard.common :as common]
[status-im.multiaccounts.core :as multiaccounts] [status-im.multiaccounts.core :as multiaccounts]
[status-im.multiaccounts.recover.core :as multiaccounts.recover]
[status-im.multiaccounts.model :as multiaccounts.model]
[status-im.multiaccounts.logout.core :as multiaccounts.logout] [status-im.multiaccounts.logout.core :as multiaccounts.logout]
[status-im.multiaccounts.model :as multiaccounts.model]
[status-im.multiaccounts.recover.core :as multiaccounts.recover]
[status-im.native-module.core :as native-module] [status-im.native-module.core :as native-module]
[status-im2.navigation.events :as navigation]
[status-im.popover.core :as popover] [status-im.popover.core :as popover]
[status-im.utils.fx :as fx]
[utils.security.core :as security]
[status-im.ethereum.core :as ethereum]
[status-im.i18n.i18n :as i18n]
[status-im.utils.types :as types] [status-im.utils.types :as types]
[status-im.keycard.backup-key :as keycard.backup] [status-im2.navigation.events :as navigation]
[status-im.bottom-sheet.core :as bottom-sheet])) [utils.re-frame :as rf]
[utils.security.core :as security]))
(fx/defn key-and-storage-management-pressed (rf/defn key-and-storage-management-pressed
"This event can be dispatched before login and from profile and needs to redirect accordingly" "This event can be dispatched before login and from profile and needs to redirect accordingly"
{:events [::key-and-storage-management-pressed]} {:events [::key-and-storage-management-pressed]}
[cofx] [cofx]
@@ -29,52 +29,57 @@
:actions-not-logged-in) :actions-not-logged-in)
nil)) nil))
(fx/defn move-keystore-checked (rf/defn move-keystore-checked
{:events [::move-keystore-checked]} {:events [::move-keystore-checked]}
[{:keys [db] :as cofx} checked?] [{:keys [db] :as cofx} checked?]
{:db (assoc-in db [:multiaccounts/key-storage :move-keystore-checked?] checked?)}) {:db (assoc-in db [:multiaccounts/key-storage :move-keystore-checked?] checked?)})
(fx/defn reset-db-checked (rf/defn reset-db-checked
{:events [::reset-db-checked]} {:events [::reset-db-checked]}
[{:keys [db] :as cofx} checked?] [{:keys [db] :as cofx} checked?]
{:db (assoc-in db [:multiaccounts/key-storage :reset-db-checked?] checked?)}) {:db (assoc-in db [:multiaccounts/key-storage :reset-db-checked?] checked?)})
(fx/defn navigate-back (rf/defn navigate-back
{:events [::navigate-back]} {:events [::navigate-back]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(fx/merge (rf/merge
cofx cofx
{:db (-> db {:db (-> db
(dissoc :recovered-account?) (dissoc :recovered-account?)
(update :keycard dissoc :from-key-storage-and-migration? :creating-backup? :factory-reset-card?))} (update :keycard
dissoc
:from-key-storage-and-migration?
:creating-backup?
:factory-reset-card?))}
(navigation/navigate-back))) (navigation/navigate-back)))
(fx/defn enter-seed-pressed (rf/defn enter-seed-pressed
"User is logged out and probably wants to move multiaccount to Keycard. Navigate to enter seed phrase screen" "User is logged out and probably wants to move multiaccount to Keycard. Navigate to enter seed phrase screen"
{:events [::enter-seed-pressed]} {:events [::enter-seed-pressed]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(fx/merge (rf/merge
cofx cofx
{:db (assoc db :recovered-account? true)} {:db (assoc db :recovered-account? true)}
(navigation/navigate-to-cofx :seed-phrase nil))) (navigation/navigate-to-cofx :seed-phrase nil)))
(fx/defn seed-phrase-input-changed (rf/defn seed-phrase-input-changed
{:events [::seed-phrase-input-changed]} {:events [::seed-phrase-input-changed]}
[{:keys [db] :as cofx} masked-seed-phrase] [{:keys [db] :as cofx} masked-seed-phrase]
(let [seed-phrase (security/safe-unmask-data masked-seed-phrase)] (let [seed-phrase (security/safe-unmask-data masked-seed-phrase)]
{:db (update db :multiaccounts/key-storage assoc {:db (update db
:seed-phrase (when seed-phrase :multiaccounts/key-storage assoc
(string/lower-case seed-phrase)) :seed-phrase (when seed-phrase
:seed-shape-invalid? (or (empty? seed-phrase) (string/lower-case seed-phrase))
(not (mnemonic/valid-length? seed-phrase))) :seed-shape-invalid? (or (empty? seed-phrase)
:seed-word-count (mnemonic/words-count seed-phrase))})) (not (mnemonic/valid-length? seed-phrase)))
:seed-word-count (mnemonic/words-count seed-phrase))}))
(fx/defn key-uid-seed-mismatch (rf/defn key-uid-seed-mismatch
{:events [::show-seed-key-uid-mismatch-error-popup]} {:events [::show-seed-key-uid-mismatch-error-popup]}
[cofx _] [cofx _]
(popover/show-popover cofx {:view :seed-key-uid-mismatch})) (popover/show-popover cofx {:view :seed-key-uid-mismatch}))
(fx/defn key-uid-matches (rf/defn key-uid-matches
{:events [::key-uid-matches]} {:events [::key-uid-matches]}
[{:keys [db] :as cofx} _] [{:keys [db] :as cofx} _]
(let [backup? (get-in db [:keycard :creating-backup?])] (let [backup? (get-in db [:keycard :creating-backup?])]
@@ -100,27 +105,32 @@
::validate-seed-against-key-uid ::validate-seed-against-key-uid
(partial validate-seed-against-key-uid (partial validate-seed-against-key-uid
{:import-mnemonic-fn native-module/multiaccount-import-mnemonic {:import-mnemonic-fn native-module/multiaccount-import-mnemonic
:on-success #(re-frame/dispatch [::key-uid-matches]) :on-success #(re-frame/dispatch [::key-uid-matches])
:on-error #(re-frame/dispatch [::show-seed-key-uid-mismatch-error-popup])})) :on-error #(re-frame/dispatch [::show-seed-key-uid-mismatch-error-popup])}))
(fx/defn seed-phrase-validated (rf/defn seed-phrase-validated
{:events [::seed-phrase-validated]} {:events [::seed-phrase-validated]}
[{:keys [db] :as cofx} validation-error] [{:keys [db] :as cofx} validation-error]
(let [error? (-> validation-error (let [error? (-> validation-error
types/json->clj types/json->clj
:error :error
string/blank? string/blank?
not) not)
onboarding? (not (or (:multiaccounts/login db) (:multiaccount db)))] onboarding? (not (or (:multiaccounts/login db) (:multiaccount db)))]
(if error? (if error?
(popover/show-popover cofx {:view :custom-seed-phrase}) (popover/show-popover cofx {:view :custom-seed-phrase})
{::validate-seed-against-key-uid {:seed-phrase (-> db :multiaccounts/key-storage :seed-phrase) {::validate-seed-against-key-uid {:seed-phrase (-> db :multiaccounts/key-storage :seed-phrase)
;; Unique key-uid of the account for which we are going to move keys ;; Unique key-uid of the account for which we are going to move
:key-uid (or (-> db :multiaccounts/login :key-uid) ;; keys
(-> db :multiaccount :key-uid) :key-uid (or (-> db :multiaccounts/login :key-uid)
(and onboarding? (-> db :keycard :application-info :key-uid)))}}))) (-> db :multiaccount :key-uid)
(and onboarding?
(-> db
:keycard
:application-info
:key-uid)))}})))
(fx/defn choose-storage-pressed (rf/defn choose-storage-pressed
{:events [::choose-storage-pressed]} {:events [::choose-storage-pressed]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(let [{:keys [seed-phrase]} (:multiaccounts/key-storage db)] (let [{:keys [seed-phrase]} (:multiaccounts/key-storage db)]
@@ -128,7 +138,7 @@
[(mnemonic/sanitize-passphrase seed-phrase) [(mnemonic/sanitize-passphrase seed-phrase)
#(re-frame/dispatch [::seed-phrase-validated %])]})) #(re-frame/dispatch [::seed-phrase-validated %])]}))
(fx/defn keycard-storage-pressed (rf/defn keycard-storage-pressed
{:events [::keycard-storage-pressed]} {:events [::keycard-storage-pressed]}
[{:keys [db]} selected?] [{:keys [db]} selected?]
{:db (assoc-in db [:multiaccounts/key-storage :keycard-storage-selected?] selected?)}) {:db (assoc-in db [:multiaccounts/key-storage :keycard-storage-selected?] selected?)})
@@ -161,55 +171,56 @@ The exact events dispatched for this flow if consumed from the UI are:
We don't need to take the exact steps, just set the required state and redirect to correct screen We don't need to take the exact steps, just set the required state and redirect to correct screen
" "
(fx/defn import-multiaccount (rf/defn import-multiaccount
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
{:dispatch [:bottom-sheet/hide] {:dispatch [:bottom-sheet/hide]
::multiaccounts.recover/import-multiaccount ::multiaccounts.recover/import-multiaccount
{:passphrase (get-in db [:multiaccounts/key-storage :seed-phrase]) {:passphrase (get-in db [:multiaccounts/key-storage :seed-phrase])
:password nil :password nil
:success-event ::import-multiaccount-success}}) :success-event ::import-multiaccount-success}})
(fx/defn delete-multiaccount-and-init-keycard-onboarding (rf/defn delete-multiaccount-and-init-keycard-onboarding
{:events [::delete-multiaccount-and-init-keycard-onboarding]} {:events [::delete-multiaccount-and-init-keycard-onboarding]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(fx/merge (rf/merge
{:dispatch [:bottom-sheet/hide] {:dispatch [:bottom-sheet/hide]
:db (assoc-in db [:multiaccounts/key-storage :reset-db-checked?] true)} :db (assoc-in db [:multiaccounts/key-storage :reset-db-checked?] true)}
(import-multiaccount))) (import-multiaccount)))
(fx/defn storage-selected (rf/defn storage-selected
{:events [::storage-selected]} {:events [::storage-selected]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(if (get-in db [:multiaccounts/key-storage :reset-db-checked?]) (if (get-in db [:multiaccounts/key-storage :reset-db-checked?])
(popover/show-popover cofx {:view :transfer-multiaccount-to-keycard-warning}) (popover/show-popover cofx {:view :transfer-multiaccount-to-keycard-warning})
(bottom-sheet/show-bottom-sheet cofx {:view :migrate-account-password}))) (bottom-sheet/show-bottom-sheet cofx {:view :migrate-account-password})))
(fx/defn skip-password-pressed (rf/defn skip-password-pressed
{:events [::skip-password-pressed]} {:events [::skip-password-pressed]}
[cofx] [cofx]
(popover/show-popover cofx {:view :transfer-multiaccount-to-keycard-warning})) (popover/show-popover cofx {:view :transfer-multiaccount-to-keycard-warning}))
(fx/defn password-changed (rf/defn password-changed
{:events [::password-changed]} {:events [::password-changed]}
[{db :db} password] [{db :db} password]
(let [unmasked-pass (security/safe-unmask-data password)] (let [unmasked-pass (security/safe-unmask-data password)]
{:db (update db :keycard assoc {:db (update db
:migration-password password :keycard assoc
:migration-password-error nil :migration-password password
:migration-password-error nil
:migration-password-valid? (and unmasked-pass (> (count unmasked-pass) 5)))})) :migration-password-valid? (and unmasked-pass (> (count unmasked-pass) 5)))}))
(fx/defn verify-password-result (rf/defn verify-password-result
{:events [::verify-password-result]} {:events [::verify-password-result]}
[{:keys [db] :as cofx} result] [{:keys [db] :as cofx} result]
(let [{:keys [error]} (types/json->clj result)] (let [{:keys [error]} (types/json->clj result)]
(if (string/blank? error) (if (string/blank? error)
(fx/merge (rf/merge
cofx cofx
{:db (update db :keycard dissoc :migration-password-error :migration-password-valid?)} {:db (update db :keycard dissoc :migration-password-error :migration-password-valid?)}
(import-multiaccount)) (import-multiaccount))
{:db (assoc-in db [:keycard :migration-password-error] (i18n/label :t/wrong-password))}))) {:db (assoc-in db [:keycard :migration-password-error] (i18n/label :t/wrong-password))})))
(fx/defn verify-password (rf/defn verify-password
{:events [::verify-password]} {:events [::verify-password]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(native-module/verify-database-password (native-module/verify-database-password
@@ -217,42 +228,43 @@ We don't need to take the exact steps, just set the required state and redirect
(ethereum/sha3 (security/safe-unmask-data (get-in db [:keycard :migration-password]))) (ethereum/sha3 (security/safe-unmask-data (get-in db [:keycard :migration-password])))
#(re-frame/dispatch [::verify-password-result %]))) #(re-frame/dispatch [::verify-password-result %])))
(fx/defn handle-multiaccount-import (rf/defn handle-multiaccount-import
{:events [::import-multiaccount-success]} {:events [::import-multiaccount-success]}
[{:keys [db] :as cofx} root-data derived-data] [{:keys [db] :as cofx} root-data derived-data]
(fx/merge cofx (rf/merge cofx
{:db (-> db {:db (-> db
(update :intro-wizard (update :intro-wizard
assoc assoc
:root-key root-data :root-key root-data
:derived derived-data :derived derived-data
:recovering? true :recovering? true
:selected-storage-type :advanced) :selected-storage-type :advanced)
(assoc-in [:keycard :flow] :recovery) (assoc-in [:keycard :flow] :recovery)
(assoc-in [:keycard :from-key-storage-and-migration?] true) (assoc-in [:keycard :from-key-storage-and-migration?] true)
(assoc-in [:keycard :converting-account?] (not (get-in db [:multiaccounts/key-storage :reset-db-checked?]))) (assoc-in [:keycard :converting-account?]
(assoc-in [:keycard :delete-account?] (not (get-in db [:multiaccounts/key-storage :reset-db-checked?])))
(true? (get-in db [:multiaccounts/key-storage :reset-db-checked?]))) (assoc-in [:keycard :delete-account?]
(dissoc :multiaccounts/key-storage))} (true? (get-in db [:multiaccounts/key-storage :reset-db-checked?])))
(dissoc :multiaccounts/key-storage))}
(popover/hide-popover) (popover/hide-popover)
(common/listen-to-hardware-back-button) (common/listen-to-hardware-back-button)
(navigation/navigate-to-cofx :keycard-onboarding-intro nil))) (navigation/navigate-to-cofx :keycard-onboarding-intro nil)))
(fx/defn goto-multiaccounts-screen (rf/defn goto-multiaccounts-screen
{:events [::hide-popover-and-goto-multiaccounts-screen]} {:events [::hide-popover-and-goto-multiaccounts-screen]}
[cofx _] [cofx _]
(fx/merge cofx (rf/merge cofx
(popover/hide-popover) (popover/hide-popover)
(navigation/navigate-to-cofx :multiaccounts nil))) (navigation/navigate-to-cofx :multiaccounts nil)))
(fx/defn confirm-logout-and-goto-key-storage (rf/defn confirm-logout-and-goto-key-storage
{:events [::confirm-logout-and-goto-key-storage]} {:events [::confirm-logout-and-goto-key-storage]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(fx/merge cofx (rf/merge cofx
{:db (assoc db :goto-key-storage? true)} {:db (assoc db :goto-key-storage? true)}
(multiaccounts.logout/logout))) (multiaccounts.logout/logout)))
(fx/defn logout-and-goto-key-storage (rf/defn logout-and-goto-key-storage
{:events [::logout-and-goto-key-storage]} {:events [::logout-and-goto-key-storage]}
[_] [_]
{:ui/show-confirmation {:ui/show-confirmation
@@ -264,9 +276,11 @@ We don't need to take the exact steps, just set the required state and redirect
(comment (comment
;; check import mnemonic output ;; check import mnemonic output
(native-module/multiaccount-import-mnemonic "rocket mixed rebel affair umbrella legal resemble scene virus park deposit cargo" nil (native-module/multiaccount-import-mnemonic
(fn [result] "rocket mixed rebel affair umbrella legal resemble scene virus park deposit cargo"
(prn (types/json->clj result)))) nil
(fn [result]
(prn (types/json->clj result))))
;; check delete account output ;; check delete account output
(native-module/delete-multiaccount "0x3831d0f22996a65970a214f0a94bfa9a63a21dac235d8dadb91be8e32e7d3ab7" (native-module/delete-multiaccount "0x3831d0f22996a65970a214f0a94bfa9a63a21dac235d8dadb91be8e32e7d3ab7"
(fn [result] (fn [result]
+208 -177
View File
@@ -1,48 +1,48 @@
(ns status-im.multiaccounts.login.core (ns status-im.multiaccounts.login.core
(:require [re-frame.core :as re-frame] (:require [clojure.string :as string]
[re-frame.core :as re-frame]
[status-im.async-storage.core :as async-storage]
[status-im.chat.models.link-preview :as link-preview]
[status-im.communities.core :as communities]
[status-im.contact.core :as contact] [status-im.contact.core :as contact]
[status-im.utils.config :as config] [status-im.data-store.chats :as data-store.chats]
[status-im.data-store.invitations :as data-store.invitations]
[status-im.data-store.settings :as data-store.settings] [status-im.data-store.settings :as data-store.settings]
[status-im.data-store.visibility-status-updates :as visibility-status-updates-store]
[status-im.ethereum.core :as ethereum] [status-im.ethereum.core :as ethereum]
[status-im.ethereum.transactions.core :as transactions]
[status-im.ethereum.eip55 :as eip55] [status-im.ethereum.eip55 :as eip55]
[status-im.ethereum.json-rpc :as json-rpc] [status-im.ethereum.json-rpc :as json-rpc]
[status-im.keycard.common :as keycard.common] [status-im.ethereum.tokens :as tokens]
[status-im.ethereum.transactions.core :as transactions]
[status-im.fleet.core :as fleet] [status-im.fleet.core :as fleet]
[status-im.i18n.i18n :as i18n] [status-im.i18n.i18n :as i18n]
[status-im.keycard.common :as keycard.common]
[status-im.mobile-sync-settings.core :as mobile-network]
[status-im.multiaccounts.biometric.core :as biometric] [status-im.multiaccounts.biometric.core :as biometric]
[status-im.multiaccounts.core :as multiaccounts] [status-im.multiaccounts.core :as multiaccounts]
[status-im.native-module.core :as status] [status-im.native-module.core :as status]
[status-im.node.core :as node]
[status-im.notifications.core :as notifications] [status-im.notifications.core :as notifications]
[status-im.popover.core :as popover] [status-im.popover.core :as popover]
[status-im.communities.core :as communities] [status-im.signing.eip1559 :as eip1559]
[status-im.transport.core :as transport] [status-im.transport.core :as transport]
[status-im.mobile-sync-settings.core :as mobile-network] [status-im.ui.components.react :as react]
[status-im.utils.fx :as fx] [status-im.utils.config :as config]
[status-im.utils.keychain.core :as keychain] [status-im.utils.keychain.core :as keychain]
[status-im2.setup.log :as logging] [status-im.utils.mobile-sync :as utils.mobile-sync]
[utils.security.core :as security] [status-im.utils.platform :as platform]
[status-im.utils.types :as types] [status-im.utils.types :as types]
[status-im.utils.utils :as utils] [status-im.utils.utils :as utils]
[status-im.wallet.core :as wallet] [status-im.utils.wallet-connect :as wallet-connect]
[status-im.wallet-connect-legacy.core :as wallet-connect-legacy] [status-im.wallet-connect-legacy.core :as wallet-connect-legacy]
[status-im.wallet.core :as wallet]
[status-im.wallet.prices :as prices] [status-im.wallet.prices :as prices]
[taoensso.timbre :as log]
[status-im.data-store.invitations :as data-store.invitations]
[status-im.chat.models.link-preview :as link-preview]
[status-im.utils.mobile-sync :as utils.mobile-sync]
[status-im.async-storage.core :as async-storage]
[status-im2.contexts.activity-center.events :as activity-center] [status-im2.contexts.activity-center.events :as activity-center]
[status-im2.navigation.events :as navigation] [status-im2.navigation.events :as navigation]
[status-im.signing.eip1559 :as eip1559] [status-im2.setup.log :as logging]
[status-im.data-store.chats :as data-store.chats] [taoensso.timbre :as log]
[status-im.data-store.visibility-status-updates :as visibility-status-updates-store] [utils.re-frame :as rf]
[status-im.ui.components.react :as react] [utils.security.core :as security]))
[status-im.utils.platform :as platform]
[status-im.ethereum.tokens :as tokens]
[clojure.string :as string]
[status-im.utils.wallet-connect :as wallet-connect]
[status-im.node.core :as node]))
(re-frame/reg-fx (re-frame/reg-fx
::initialize-communities-enabled ::initialize-communities-enabled
@@ -89,11 +89,13 @@
#(re-frame/dispatch [:wallet-connect/client-init %]) #(re-frame/dispatch [:wallet-connect/client-init %])
#(log/error "[wallet-connect]" %)))) #(log/error "[wallet-connect]" %))))
(defn rpc->accounts [accounts] (defn rpc->accounts
[accounts]
(reduce (fn [acc {:keys [chat type wallet] :as account}] (reduce (fn [acc {:keys [chat type wallet] :as account}]
(if chat (if chat
acc acc
(let [account (cond-> (update account :address (let [account (cond-> (update account
:address
eip55/address->checksum) eip55/address->checksum)
type type
(update :type keyword))] (update :type keyword))]
@@ -119,25 +121,28 @@
"0x86a12d91c813f69a53349ff640e32af97d5f5c1f8d42d54cf4c8aa8dea231955" "0x86a12d91c813f69a53349ff640e32af97d5f5c1f8d42d54cf4c8aa8dea231955"
"0x0011a30f5b2023bc228f6dd5608b3e7149646fa83f33350926ceb1925af78f08"}) "0x0011a30f5b2023bc228f6dd5608b3e7149646fa83f33350926ceb1925af78f08"})
(fx/defn check-invalid-ens [{:keys [db]}] (rf/defn check-invalid-ens
[{:keys [db]}]
(async-storage/get-item (async-storage/get-item
:invalid-ens-name-seen :invalid-ens-name-seen
(fn [already-seen] (fn [already-seen]
(when (and (not already-seen) (when (and (not already-seen)
(boolean (get invalid-addrr (ethereum/sha3 (string/lower-case (ethereum/default-address db)))))) (boolean (get invalid-addrr
(ethereum/sha3 (string/lower-case (ethereum/default-address db))))))
(utils/show-popup (utils/show-popup
(i18n/label :t/warning) (i18n/label :t/warning)
(i18n/label :t/ens-username-invalid-name-warning) (i18n/label :t/ens-username-invalid-name-warning)
#(async-storage/set-item! :invalid-ens-name-seen true))))) #(async-storage/set-item! :invalid-ens-name-seen true)))))
nil) nil)
(fx/defn initialize-wallet (rf/defn initialize-wallet
{:events [::initialize-wallet]} {:events [::initialize-wallet]}
[{:keys [db] :as cofx} accounts tokens custom-tokens [{:keys [db] :as cofx} accounts tokens custom-tokens
favourites scan-all-tokens? new-account?] favourites scan-all-tokens? new-account?]
(fx/merge (rf/merge
cofx cofx
{:db (assoc db :multiaccount/accounts {:db (assoc db
:multiaccount/accounts
(rpc->accounts accounts)) (rpc->accounts accounts))
;; NOTE: Local notifications should be enabled only after wallet was started ;; NOTE: Local notifications should be enabled only after wallet was started
::enable-local-notifications nil} ::enable-local-notifications nil}
@@ -146,16 +151,18 @@
(wallet/initialize-favourites favourites) (wallet/initialize-favourites favourites)
(wallet/get-pending-transactions) (wallet/get-pending-transactions)
(wallet/fetch-collectibles-collection) (wallet/fetch-collectibles-collection)
(cond (and new-account? (cond
(not scan-all-tokens?)) (and new-account?
(wallet/set-zero-balances (first accounts)) (not scan-all-tokens?))
(wallet/set-zero-balances (first accounts))
(and new-account? scan-all-tokens? (and new-account?
(not (utils.mobile-sync/cellular? (:network/type db)))) scan-all-tokens?
(wallet/set-max-block (get (first accounts) :address) 0) (not (utils.mobile-sync/cellular? (:network/type db))))
(wallet/set-max-block (get (first accounts) :address) 0)
:else :else
(wallet/get-cached-balances scan-all-tokens?)) (wallet/get-cached-balances scan-all-tokens?))
(when-not (get db :wallet/new-account) (when-not (get db :wallet/new-account)
(wallet/restart-wallet-service nil)) (wallet/restart-wallet-service nil))
(when (or (not (utils.mobile-sync/syncing-allowed? cofx)) (when (or (not (utils.mobile-sync/syncing-allowed? cofx))
@@ -166,21 +173,21 @@
(prices/update-prices) (prices/update-prices)
(wallet-connect-legacy/get-connector-session-from-db))) (wallet-connect-legacy/get-connector-session-from-db)))
(fx/defn login (rf/defn login
{:events [:multiaccounts.login.ui/password-input-submitted]} {:events [:multiaccounts.login.ui/password-input-submitted]}
[{:keys [db]}] [{:keys [db]}]
(let [{:keys [key-uid password name identicon]} (:multiaccounts/login db)] (let [{:keys [key-uid password name identicon]} (:multiaccounts/login db)]
{:db (-> db {:db (-> db
(assoc-in [:multiaccounts/login :processing] true) (assoc-in [:multiaccounts/login :processing] true)
(dissoc :intro-wizard :recovered-account?) (dissoc :intro-wizard :recovered-account?)
(update :keycard dissoc :flow)) (update :keycard dissoc :flow))
::login [key-uid ::login [key-uid
(types/clj->json {:name name (types/clj->json {:name name
:key-uid key-uid :key-uid key-uid
:identicon identicon}) :identicon identicon})
(ethereum/sha3 (security/safe-unmask-data password))]})) (ethereum/sha3 (security/safe-unmask-data password))]}))
(fx/defn export-db-submitted (rf/defn export-db-submitted
{:events [:multiaccounts.login.ui/export-db-submitted]} {:events [:multiaccounts.login.ui/export-db-submitted]}
[{:keys [db]}] [{:keys [db]}]
(let [{:keys [key-uid password name identicon]} (:multiaccounts/login db)] (let [{:keys [key-uid password name identicon]} (:multiaccounts/login db)]
@@ -192,24 +199,25 @@
(fn [path] (fn [path]
(when platform/ios? (when platform/ios?
(let [uri (str "file://" path)] (let [uri (str "file://" path)]
(.share ^js react/sharing (clj->js {:title "Unencrypted database" (.share ^js react/sharing
:url uri})))))]})) (clj->js {:title "Unencrypted database"
:url uri})))))]}))
(fx/defn import-db-submitted (rf/defn import-db-submitted
{:events [:multiaccounts.login.ui/import-db-submitted]} {:events [:multiaccounts.login.ui/import-db-submitted]}
[{:keys [db]}] [{:keys [db]}]
(let [{:keys [key-uid password name identicon]} (:multiaccounts/login db)] (let [{:keys [key-uid password name identicon]} (:multiaccounts/login db)]
{::import-db [key-uid {::import-db [key-uid
(types/clj->json {:name name (types/clj->json {:name name
:key-uid key-uid :key-uid key-uid
:identicon identicon}) :identicon identicon})
(ethereum/sha3 (security/safe-unmask-data password))]})) (ethereum/sha3 (security/safe-unmask-data password))]}))
(fx/defn finish-keycard-setup (rf/defn finish-keycard-setup
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
{:db (update db :keycard dissoc :flow)}) {:db (update db :keycard dissoc :flow)})
(fx/defn initialize-dapp-permissions (rf/defn initialize-dapp-permissions
{:events [::initialize-dapp-permissions]} {:events [::initialize-dapp-permissions]}
[{:keys [db]} all-dapp-permissions] [{:keys [db]} all-dapp-permissions]
(let [dapp-permissions (reduce (fn [acc {:keys [dapp] :as dapp-permissions}] (let [dapp-permissions (reduce (fn [acc {:keys [dapp] :as dapp-permissions}]
@@ -218,7 +226,7 @@
all-dapp-permissions)] all-dapp-permissions)]
{:db (assoc db :dapps/permissions dapp-permissions)})) {:db (assoc db :dapps/permissions dapp-permissions)}))
(fx/defn initialize-browsers (rf/defn initialize-browsers
{:events [::initialize-browsers]} {:events [::initialize-browsers]}
[{:keys [db]} all-stored-browsers] [{:keys [db]} all-stored-browsers]
(let [browsers (reduce (fn [acc {:keys [browser-id] :as browser}] (let [browsers (reduce (fn [acc {:keys [browser-id] :as browser}]
@@ -227,7 +235,7 @@
all-stored-browsers)] all-stored-browsers)]
{:db (assoc db :browser/browsers browsers)})) {:db (assoc db :browser/browsers browsers)}))
(fx/defn initialize-bookmarks (rf/defn initialize-bookmarks
{:events [::initialize-bookmarks]} {:events [::initialize-bookmarks]}
[{:keys [db]} stored-bookmarks] [{:keys [db]} stored-bookmarks]
(let [bookmarks (reduce (fn [acc {:keys [url] :as bookmark}] (let [bookmarks (reduce (fn [acc {:keys [url] :as bookmark}]
@@ -236,28 +244,30 @@
stored-bookmarks)] stored-bookmarks)]
{:db (assoc db :bookmarks/bookmarks bookmarks)})) {:db (assoc db :bookmarks/bookmarks bookmarks)}))
(fx/defn initialize-invitations (rf/defn initialize-invitations
{:events [::initialize-invitations]} {:events [::initialize-invitations]}
[{:keys [db]} invitations] [{:keys [db]} invitations]
{:db (assoc db :group-chat/invitations (reduce (fn [acc {:keys [id] :as inv}] {:db (assoc db
(assoc acc id (data-store.invitations/<-rpc inv))) :group-chat/invitations
{} (reduce (fn [acc {:keys [id] :as inv}]
invitations))}) (assoc acc id (data-store.invitations/<-rpc inv)))
{}
invitations))})
(fx/defn initialize-web3-client-version (rf/defn initialize-web3-client-version
{:events [::initialize-web3-client-version]} {:events [::initialize-web3-client-version]}
[{:keys [db]} node-version] [{:keys [db]} node-version]
{:db (assoc db :web3-node-version node-version)}) {:db (assoc db :web3-node-version node-version)})
(fx/defn handle-close-app-confirmed (rf/defn handle-close-app-confirmed
{:events [::close-app-confirmed]} {:events [::close-app-confirmed]}
[_] [_]
{:ui/close-application nil}) {:ui/close-application nil})
(fx/defn check-network-version (rf/defn check-network-version
[_ network-id] [_ network-id]
{::json-rpc/call {::json-rpc/call
[{:method "net_version" [{:method "net_version"
:on-success :on-success
(fn [fetched-network-id] (fn [fetched-network-id]
(when (not= network-id (str (int fetched-network-id))) (when (not= network-id (str (int fetched-network-id)))
@@ -270,7 +280,8 @@
:fetched-network-id fetched-network-id}) :fetched-network-id fetched-network-id})
#(re-frame/dispatch [::close-app-confirmed]))))}]}) #(re-frame/dispatch [::close-app-confirmed]))))}]})
(defn normalize-tokens [network-id tokens] (defn normalize-tokens
[network-id tokens]
(mapv #(-> % (mapv #(-> %
(update :symbol keyword) (update :symbol keyword)
((partial tokens/update-icon (ethereum/chain-id->chain-keyword (int network-id))))) ((partial tokens/update-icon (ethereum/chain-id->chain-keyword (int network-id)))))
@@ -281,8 +292,8 @@
(fn [[network-id accounts recovered-account?]] (fn [[network-id accounts recovered-account?]]
(utils/set-timeout (utils/set-timeout
(fn [] (fn []
(json-rpc/call {:method "wallet_getTokens" (json-rpc/call {:method "wallet_getTokens"
:params [(int network-id)] :params [(int network-id)]
:on-success #(re-frame/dispatch [::initialize-wallet :on-success #(re-frame/dispatch [::initialize-wallet
accounts accounts
(normalize-tokens network-id %) (normalize-tokens network-id %)
@@ -299,25 +310,25 @@
(clj->js (clj->js
[(js/Promise. [(js/Promise.
(fn [resolve reject] (fn [resolve reject]
(json-rpc/call {:method "accounts_getAccounts" (json-rpc/call {:method "accounts_getAccounts"
:on-success resolve :on-success resolve
:on-error reject}))) :on-error reject})))
(js/Promise. (js/Promise.
(fn [resolve _] (fn [resolve _]
(json-rpc/call {:method "wallet_getTokens" (json-rpc/call {:method "wallet_getTokens"
:params [(int network-id)] :params [(int network-id)]
:on-success resolve :on-success resolve
:on-error #(resolve nil)}))) :on-error #(resolve nil)})))
(js/Promise. (js/Promise.
(fn [resolve reject] (fn [resolve reject]
(json-rpc/call {:method "wallet_getCustomTokens" (json-rpc/call {:method "wallet_getCustomTokens"
:on-success resolve :on-success resolve
:on-error reject}))) :on-error reject})))
(js/Promise. (js/Promise.
(fn [resolve reject] (fn [resolve reject]
(json-rpc/call {:method "wallet_getSavedAddresses" (json-rpc/call {:method "wallet_getSavedAddresses"
:on-success resolve :on-success resolve
:on-error reject})))])) :on-error reject})))]))
(.then (fn [[accounts tokens custom-tokens favourites]] (.then (fn [[accounts tokens custom-tokens favourites]]
(callback accounts (callback accounts
(normalize-tokens network-id tokens) (normalize-tokens network-id tokens)
@@ -326,7 +337,8 @@
(.catch (fn [_] (.catch (fn [_]
(log/error "Failed to initialize wallet")))))) (log/error "Failed to initialize wallet"))))))
(fx/defn initialize-browser [_] (rf/defn initialize-browser
[_]
{::json-rpc/call {::json-rpc/call
[{:method "wakuext_getBrowsers" [{:method "wakuext_getBrowsers"
:on-success #(re-frame/dispatch [::initialize-browsers %])} :on-success #(re-frame/dispatch [::initialize-browsers %])}
@@ -335,53 +347,57 @@
{:method "permissions_getDappPermissions" {:method "permissions_getDappPermissions"
:on-success #(re-frame/dispatch [::initialize-dapp-permissions %])}]}) :on-success #(re-frame/dispatch [::initialize-dapp-permissions %])}]})
(fx/defn initialize-appearance [cofx] (rf/defn initialize-appearance
[cofx]
{:multiaccounts.ui/switch-theme (get-in cofx [:db :multiaccount :appearance])}) {:multiaccounts.ui/switch-theme (get-in cofx [:db :multiaccount :appearance])})
(fx/defn get-group-chat-invitations [_] (rf/defn get-group-chat-invitations
[_]
{::json-rpc/call {::json-rpc/call
[{:method "wakuext_getGroupChatInvitations" [{:method "wakuext_getGroupChatInvitations"
:on-success #(re-frame/dispatch [::initialize-invitations %])}]}) :on-success #(re-frame/dispatch [::initialize-invitations %])}]})
(fx/defn initialize-communities-enabled (rf/defn initialize-communities-enabled
[cofx] [cofx]
{::initialize-communities-enabled nil}) {::initialize-communities-enabled nil})
(fx/defn initialize-transactions-management-enabled (rf/defn initialize-transactions-management-enabled
[cofx] [cofx]
{::initialize-transactions-management-enabled nil}) {::initialize-transactions-management-enabled nil})
(fx/defn initialize-wallet-connect (rf/defn initialize-wallet-connect
[cofx] [cofx]
{::initialize-wallet-connect nil}) {::initialize-wallet-connect nil})
(fx/defn get-node-config-callback (rf/defn get-node-config-callback
{:events [::get-node-config-callback]} {:events [::get-node-config-callback]}
[{:keys [db] :as cofx} node-config-json] [{:keys [db] :as cofx} node-config-json]
(let [node-config (types/json->clj node-config-json)] (let [node-config (types/json->clj node-config-json)]
{:db (assoc-in db [:multiaccount :wakuv2-config] {:db (assoc-in db
(get node-config :WakuV2Config))})) [:multiaccount :wakuv2-config]
(get node-config :WakuV2Config))}))
(fx/defn get-node-config (rf/defn get-node-config
[_] [_]
(status/get-node-config #(re-frame/dispatch [::get-node-config-callback %]))) (status/get-node-config #(re-frame/dispatch [::get-node-config-callback %])))
(fx/defn get-settings-callback (rf/defn get-settings-callback
{:events [::get-settings-callback]} {:events [::get-settings-callback]}
[{:keys [db] :as cofx} settings] [{:keys [db] :as cofx} settings]
(let [{:networks/keys [current-network networks] (let [{:networks/keys [current-network networks]
:as settings} :as settings}
(data-store.settings/rpc->settings settings) (data-store.settings/rpc->settings settings)
multiaccount (dissoc settings :networks/current-network :networks/networks) multiaccount (dissoc settings :networks/current-network :networks/networks)
;;for some reason we save default networks in db, in case when we want to modify default-networks for ;;for some reason we save default networks in db, in case when we want to modify default-networks
;;for
;; existing accounts we have to merge them again into networks ;; existing accounts we have to merge them again into networks
merged-networks (merge networks config/default-networks-by-id)] merged-networks (merge networks config/default-networks-by-id)]
(fx/merge cofx (rf/merge cofx
{:db (-> db {:db (-> db
(dissoc :multiaccounts/login) (dissoc :multiaccounts/login)
(assoc :networks/current-network current-network (assoc :networks/current-network current-network
:networks/networks merged-networks :networks/networks merged-networks
:multiaccount multiaccount))} :multiaccount multiaccount))}
(data-store.chats/fetch-chats-rpc (data-store.chats/fetch-chats-rpc
{:on-success {:on-success
#(do (re-frame/dispatch [:chats-list/load-success %]) #(do (re-frame/dispatch [:chats-list/load-success %])
@@ -408,31 +424,32 @@
(when (= stored-key-uid key-uid) (when (= stored-key-uid key-uid)
(re-frame/dispatch [:chat.ui/navigate-to-chat chat-id]))))))))) (re-frame/dispatch [:chat.ui/navigate-to-chat chat-id])))))))))
(fx/defn check-last-chat (rf/defn check-last-chat
{:events [::check-last-chat]} {:events [::check-last-chat]}
[{:keys [db]}] [{:keys [db]}]
{::open-last-chat (get-in db [:multiaccount :key-uid])}) {::open-last-chat (get-in db [:multiaccount :key-uid])})
(fx/defn update-wallet-accounts (rf/defn update-wallet-accounts
[{:keys [db]} accounts] [{:keys [db]} accounts]
(let [existing-accounts (into {} (map #(vector (:address %1) %1) (:multiaccount/accounts db))) (let [existing-accounts (into {} (map #(vector (:address %1) %1) (:multiaccount/accounts db)))
reduce-fn (fn [existing-accs new-acc] reduce-fn (fn [existing-accs new-acc]
(let [address (:address new-acc)] (let [address (:address new-acc)]
(if (:removed new-acc) (if (:removed new-acc)
(dissoc existing-accs address) (dissoc existing-accs address)
(assoc existing-accs address new-acc)))) (assoc existing-accs address new-acc))))
new-accounts (reduce reduce-fn existing-accounts (rpc->accounts accounts))] new-accounts (reduce reduce-fn existing-accounts (rpc->accounts accounts))]
{:db (assoc db :multiaccount/accounts (vals new-accounts))})) {:db (assoc db :multiaccount/accounts (vals new-accounts))}))
(fx/defn get-chats-callback (rf/defn get-chats-callback
{:events [::get-chats-callback]} {:events [::get-chats-callback]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(let [{:networks/keys [current-network networks]} db (let [{:networks/keys [current-network networks]} db
notifications-enabled? (get-in db [:multiaccount :notifications-enabled?]) notifications-enabled? (get-in db [:multiaccount :notifications-enabled?])
network-id (str (get-in networks [current-network :config :NetworkId])) network-id (str (get-in networks
[current-network :config :NetworkId]))
remote-push-notifications-enabled? remote-push-notifications-enabled?
(get-in db [:multiaccount :remote-push-notifications-enabled?])] (get-in db [:multiaccount :remote-push-notifications-enabled?])]
(fx/merge cofx (rf/merge cofx
(cond-> {::eip1559/check-eip1559-activation (cond-> {::eip1559/check-eip1559-activation
{:network-id network-id {:network-id network-id
:on-enabled #(log/info "eip1550 is activated") :on-enabled #(log/info "eip1550 is activated")
@@ -442,7 +459,7 @@
(fn [accounts tokens custom-tokens favourites] (fn [accounts tokens custom-tokens favourites]
(re-frame/dispatch [::initialize-wallet (re-frame/dispatch [::initialize-wallet
accounts tokens custom-tokens favourites]))] accounts tokens custom-tokens favourites]))]
::open-last-chat (get-in db [:multiaccount :key-uid])} ::open-last-chat (get-in db [:multiaccount :key-uid])}
(or notifications-enabled? remote-push-notifications-enabled?) (or notifications-enabled? remote-push-notifications-enabled?)
(assoc ::notifications/enable remote-push-notifications-enabled?)) (assoc ::notifications/enable remote-push-notifications-enabled?))
(transport/start-messenger) (transport/start-messenger)
@@ -457,7 +474,8 @@
(link-preview/request-link-preview-whitelist) (link-preview/request-link-preview-whitelist)
(visibility-status-updates-store/fetch-visibility-status-updates-rpc)))) (visibility-status-updates-store/fetch-visibility-status-updates-rpc))))
(defn get-new-auth-method [auth-method save-password?] (defn get-new-auth-method
[auth-method save-password?]
(when save-password? (when save-password?
(when-not (or (= keychain/auth-method-biometric auth-method) (when-not (or (= keychain/auth-method-biometric auth-method)
(= keychain/auth-method-password auth-method)) (= keychain/auth-method-password auth-method))
@@ -472,15 +490,15 @@
(re-frame/dispatch [:init-root (if config/new-ui-enabled? :shell-stack :chat-stack)]) (re-frame/dispatch [:init-root (if config/new-ui-enabled? :shell-stack :chat-stack)])
(re-frame/dispatch [:init-root :tos]))) (re-frame/dispatch [:init-root :tos])))
(fx/defn login-only-events (rf/defn login-only-events
[{:keys [db] :as cofx} key-uid password save-password?] [{:keys [db] :as cofx} key-uid password save-password?]
(let [auth-method (:auth-method db) (let [auth-method (:auth-method db)
new-auth-method (get-new-auth-method auth-method save-password?)] new-auth-method (get-new-auth-method auth-method save-password?)]
(log/debug "[login] login-only-events" (log/debug "[login] login-only-events"
"auth-method" auth-method "auth-method" auth-method
"new-auth-method" new-auth-method) "new-auth-method" new-auth-method)
(fx/merge cofx (rf/merge cofx
{:db (assoc db :chats/loading? true) {:db (assoc db :chats/loading? true)
::json-rpc/call ::json-rpc/call
[{:method "settings_getSettings" [{:method "settings_getSettings"
:on-success #(do (re-frame/dispatch [::get-settings-callback %]) :on-success #(do (re-frame/dispatch [::get-settings-callback %])
@@ -488,24 +506,26 @@
(notifications/load-notification-preferences) (notifications/load-notification-preferences)
(when save-password? (when save-password?
(keychain/save-user-password key-uid password)) (keychain/save-user-password key-uid password))
(keychain/save-auth-method key-uid (or new-auth-method auth-method keychain/auth-method-none))))) (keychain/save-auth-method key-uid
(or new-auth-method auth-method keychain/auth-method-none)))))
(fx/defn create-only-events (rf/defn create-only-events
[{:keys [db] :as cofx} recovered-account?] [{:keys [db] :as cofx} recovered-account?]
(let [{:keys [multiaccount (let [{:keys [multiaccount
:multiaccounts/multiaccounts :multiaccounts/multiaccounts
:multiaccount/accounts]} db :multiaccount/accounts]}
{:keys [creating?]} (:multiaccounts/login db) db
first-account? (and creating? {:keys [creating?]} (:multiaccounts/login db)
(empty? multiaccounts)) first-account? (and creating?
tos-accepted? (get db :tos/accepted?) (empty? multiaccounts))
tos-accepted? (get db :tos/accepted?)
{:networks/keys [current-network networks]} db {:networks/keys [current-network networks]} db
network-id (str (get-in networks [current-network :config :NetworkId]))] network-id (str (get-in networks [current-network :config :NetworkId]))]
(fx/merge cofx (rf/merge cofx
{:db (-> db {:db (-> db
(dissoc :multiaccounts/login) (dissoc :multiaccounts/login)
(assoc :tos/next-root :onboarding-notification :chats/loading? false) (assoc :tos/next-root :onboarding-notification :chats/loading? false)
(assoc-in [:multiaccount :multiaccounts/first-account] first-account?)) (assoc-in [:multiaccount :multiaccounts/first-account] first-account?))
::get-tokens [network-id accounts recovered-account?]} ::get-tokens [network-id accounts recovered-account?]}
(finish-keycard-setup) (finish-keycard-setup)
(transport/start-messenger) (transport/start-messenger)
@@ -525,14 +545,16 @@
(navigation/init-root :onboarding-notification) (navigation/init-root :onboarding-notification)
(navigation/init-root :tos)))))) (navigation/init-root :tos))))))
(defn- keycard-setup? [cofx] (defn- keycard-setup?
[cofx]
(boolean (get-in cofx [:db :keycard :flow]))) (boolean (get-in cofx [:db :keycard :flow])))
(defn on-login-update-db [db login-only? now] (defn on-login-update-db
[db login-only? now]
(-> db (-> db
(dissoc :connectivity/ui-status-properties) (dissoc :connectivity/ui-status-properties)
(update :keycard dissoc :from-key-storage-and-migration?) (update :keycard dissoc :from-key-storage-and-migration?)
(update :keycard dissoc (update :keycard dissoc
:on-card-read :on-card-read
:card-read-in-progress? :card-read-in-progress?
:pin :pin
@@ -544,23 +566,25 @@
(assoc :logged-in-since now) (assoc :logged-in-since now)
(assoc :view-id :home))) (assoc :view-id :home)))
(fx/defn multiaccount-login-success (rf/defn multiaccount-login-success
[{:keys [db now] :as cofx}] [{:keys [db now] :as cofx}]
(let [{:keys [key-uid password save-password? creating?]} (let [{:keys [key-uid password save-password? creating?]}
(:multiaccounts/login db) (:multiaccounts/login db)
multiaccounts (:multiaccounts/multiaccounts db) multiaccounts (:multiaccounts/multiaccounts db)
recovered-account? (get db :recovered-account?) recovered-account? (get db :recovered-account?)
login-only? (not (or creating? login-only? (not (or creating?
recovered-account? recovered-account?
(keycard-setup? cofx))) (keycard-setup? cofx)))
from-migration? (get-in db [:keycard :from-key-storage-and-migration?]) from-migration? (get-in db
nodes nil] [:keycard
:from-key-storage-and-migration?])
nodes nil]
(log/debug "[multiaccount] multiaccount-login-success" (log/debug "[multiaccount] multiaccount-login-success"
"login-only?" login-only? "login-only?" login-only?
"recovered-account?" recovered-account?) "recovered-account?" recovered-account?)
(fx/merge cofx (rf/merge cofx
{:db (on-login-update-db db login-only? now) {:db (on-login-update-db db login-only? now)
::json-rpc/call ::json-rpc/call
[{:method "web3_clientVersion" [{:method "web3_clientVersion"
:on-success #(re-frame/dispatch [::initialize-web3-client-version %])}]} :on-success #(re-frame/dispatch [::initialize-web3-client-version %])}]}
@@ -571,7 +595,8 @@
(not recovered-account?)) (not recovered-account?))
(wallet/set-initial-blocks-range)) (wallet/set-initial-blocks-range))
(when from-migration? (when from-migration?
(utils/show-popup (i18n/label :t/migration-successful) (i18n/label :t/migration-successful-text))) (utils/show-popup (i18n/label :t/migration-successful)
(i18n/label :t/migration-successful-text)))
(if login-only? (if login-only?
(login-only-events key-uid password save-password?) (login-only-events key-uid password save-password?)
(create-only-events recovered-account?))))) (create-only-events recovered-account?)))))
@@ -582,7 +607,7 @@
;; are alreayd in `multiaccounts/login` and should not be overriden, as they come from ;; are alreayd in `multiaccounts/login` and should not be overriden, as they come from
;; the keychain (save-password), this is not very explicit and we should probably ;; the keychain (save-password), this is not very explicit and we should probably
;; make it clearer ;; make it clearer
(fx/defn open-login (rf/defn open-login
[{:keys [db]} override-multiaccount] [{:keys [db]} override-multiaccount]
{:db (-> db {:db (-> db
(update :multiaccounts/login (update :multiaccounts/login
@@ -593,23 +618,26 @@
:error :error
:password))}) :password))})
(fx/defn open-login-callback (rf/defn open-login-callback
{:events [:multiaccounts.login.callback/get-user-password-success]} {:events [:multiaccounts.login.callback/get-user-password-success]}
[{:keys [db] :as cofx} password] [{:keys [db] :as cofx} password]
(let [key-uid (get-in db [:multiaccounts/login :key-uid]) (let [key-uid (get-in db [:multiaccounts/login :key-uid])
keycard-account? (boolean (get-in db [:multiaccounts/multiaccounts keycard-account? (boolean (get-in db
key-uid [:multiaccounts/multiaccounts
:keycard-pairing])) key-uid
:keycard-pairing]))
goto-key-storage? (:goto-key-storage? db)] goto-key-storage? (:goto-key-storage? db)]
(if password (if password
(fx/merge (rf/merge
cofx cofx
{:db (update-in db [:multiaccounts/login] assoc {:db (update-in db
:password password [:multiaccounts/login]
:save-password? true) assoc
:password password
:save-password? true)
:init-root-fx :progress} :init-root-fx :progress}
login) login)
(fx/merge (rf/merge
cofx cofx
{:db (dissoc db :goto-key-storage?)} {:db (dissoc db :goto-key-storage?)}
(when keycard-account? (when keycard-account?
@@ -622,25 +650,28 @@
#(when goto-key-storage? #(when goto-key-storage?
(navigation/navigate-to-cofx % :actions-not-logged-in nil)))))) (navigation/navigate-to-cofx % :actions-not-logged-in nil))))))
(fx/defn get-credentials (rf/defn get-credentials
[{:keys [db] :as cofx} key-uid] [{:keys [db] :as cofx} key-uid]
(let [keycard-multiaccount? (boolean (get-in db [:multiaccounts/multiaccounts key-uid :keycard-pairing]))] (let [keycard-multiaccount? (boolean (get-in db
[:multiaccounts/multiaccounts key-uid :keycard-pairing]))]
(log/debug "[login] get-credentials" (log/debug "[login] get-credentials"
"keycard-multiacc?" keycard-multiaccount?) "keycard-multiacc?"
keycard-multiaccount?)
(if keycard-multiaccount? (if keycard-multiaccount?
(keychain/get-keycard-keys cofx key-uid) (keychain/get-keycard-keys cofx key-uid)
(keychain/get-user-password cofx key-uid)))) (keychain/get-user-password cofx key-uid))))
(fx/defn get-auth-method-success (rf/defn get-auth-method-success
"Auth method: nil - not supported, \"none\" - not selected, \"password\", \"biometric\", \"biometric-prepare\"" "Auth method: nil - not supported, \"none\" - not selected, \"password\", \"biometric\", \"biometric-prepare\""
{:events [:multiaccounts.login/get-auth-method-success]} {:events [:multiaccounts.login/get-auth-method-success]}
[{:keys [db] :as cofx} auth-method] [{:keys [db] :as cofx} auth-method]
(let [key-uid (get-in db [:multiaccounts/login :key-uid]) (let [key-uid (get-in db [:multiaccounts/login :key-uid])
keycard-multiaccount? (boolean (get-in db [:multiaccounts/multiaccounts key-uid :keycard-pairing]))] keycard-multiaccount? (boolean (get-in db
[:multiaccounts/multiaccounts key-uid :keycard-pairing]))]
(log/debug "[login] get-auth-method-success" (log/debug "[login] get-auth-method-success"
"auth-method" auth-method "auth-method" auth-method
"keycard-multiacc?" keycard-multiaccount?) "keycard-multiacc?" keycard-multiaccount?)
(fx/merge (rf/merge
cofx cofx
{:db (assoc db :auth-method auth-method)} {:db (assoc db :auth-method auth-method)}
#(cond #(cond
@@ -653,7 +684,7 @@
(keycard.common/get-application-info % nil)) (keycard.common/get-application-info % nil))
(open-login-callback nil)))) (open-login-callback nil))))
(fx/defn biometric-auth-done (rf/defn biometric-auth-done
{:events [:biometric-auth-done]} {:events [:biometric-auth-done]}
[{:keys [db] :as cofx} {:keys [bioauth-success bioauth-message bioauth-code]}] [{:keys [db] :as cofx} {:keys [bioauth-success bioauth-message bioauth-code]}]
(let [key-uid (get-in db [:multiaccounts/login :key-uid]) (let [key-uid (get-in db [:multiaccounts/login :key-uid])
@@ -661,28 +692,28 @@
(log/debug "[biometric] biometric-auth-done" (log/debug "[biometric] biometric-auth-done"
"bioauth-success" bioauth-success "bioauth-success" bioauth-success
"bioauth-message" bioauth-message "bioauth-message" bioauth-message
"bioauth-code" bioauth-code) "bioauth-code" bioauth-code)
(if bioauth-success (if bioauth-success
(get-credentials cofx key-uid) (get-credentials cofx key-uid)
(fx/merge cofx (rf/merge cofx
{:db (assoc-in db {:db (assoc-in db
[:multiaccounts/login :save-password?] [:multiaccounts/login :save-password?]
(= auth-method keychain/auth-method-biometric))} (= auth-method keychain/auth-method-biometric))}
(when-not (= auth-method keychain/auth-method-biometric) (when-not (= auth-method keychain/auth-method-biometric)
(keychain/save-auth-method key-uid keychain/auth-method-none)) (keychain/save-auth-method key-uid keychain/auth-method-none))
(biometric/show-message bioauth-message bioauth-code) (biometric/show-message bioauth-message bioauth-code)
(open-login-callback nil))))) (open-login-callback nil)))))
(fx/defn save-password (rf/defn save-password
{:events [:multiaccounts/save-password]} {:events [:multiaccounts/save-password]}
[{:keys [db] :as cofx} save-password?] [{:keys [db] :as cofx} save-password?]
(let [bioauth-supported? (boolean (get db :supported-biometric-auth)) (let [bioauth-supported? (boolean (get db :supported-biometric-auth))
previous-auth-method (get db :auth-method)] previous-auth-method (get db :auth-method)]
(log/debug "[login] save-password" (log/debug "[login] save-password"
"save-password?" save-password? "save-password?" save-password?
"bioauth-supported?" bioauth-supported? "bioauth-supported?" bioauth-supported?
"previous-auth-method" previous-auth-method) "previous-auth-method" previous-auth-method)
(fx/merge (rf/merge
cofx cofx
{:db (cond-> db {:db (cond-> db
(not= previous-auth-method (not= previous-auth-method
@@ -700,54 +731,54 @@
(when-not (= previous-auth-method keychain/auth-method-none) (when-not (= previous-auth-method keychain/auth-method-none)
(popover/show-popover {:view :disable-password-saving}))))))) (popover/show-popover {:view :disable-password-saving})))))))
(fx/defn welcome-lets-go (rf/defn welcome-lets-go
{:events [:welcome-lets-go]} {:events [:welcome-lets-go]}
[_] [_]
{:init-root-fx :chat-stack}) {:init-root-fx :chat-stack})
(fx/defn multiaccount-selected (rf/defn multiaccount-selected
{:events [:multiaccounts.login.ui/multiaccount-selected]} {:events [:multiaccounts.login.ui/multiaccount-selected]}
[{:keys [db] :as cofx} key-uid] [{:keys [db] :as cofx} key-uid]
;; We specifically pass a bunch of fields instead of the whole multiaccount ;; We specifically pass a bunch of fields instead of the whole multiaccount
;; as we want store some fields in multiaccount that are not here ;; as we want store some fields in multiaccount that are not here
(let [multiaccount (get-in db [:multiaccounts/multiaccounts key-uid]) (let [multiaccount (get-in db [:multiaccounts/multiaccounts key-uid])
keycard-multiaccount? (boolean (:keycard-pairing multiaccount))] keycard-multiaccount? (boolean (:keycard-pairing multiaccount))]
(fx/merge (rf/merge
cofx cofx
{:db (update db :keycard dissoc :application-info) {:db (update db :keycard dissoc :application-info)
:navigate-to-fx (if keycard-multiaccount? :keycard-login-pin :login)} :navigate-to-fx (if keycard-multiaccount? :keycard-login-pin :login)}
(open-login (select-keys multiaccount [:key-uid :name :public-key :identicon :images]))))) (open-login (select-keys multiaccount [:key-uid :name :public-key :identicon :images])))))
(fx/defn hide-keycard-banner (rf/defn hide-keycard-banner
{:events [:hide-keycard-banner]} {:events [:hide-keycard-banner]}
[{:keys [db]}] [{:keys [db]}]
{:db (assoc db :keycard/banner-hidden true) {:db (assoc db :keycard/banner-hidden true)
::async-storage/set! {:keycard-banner-hidden true}}) ::async-storage/set! {:keycard-banner-hidden true}})
(fx/defn get-keycard-banner-preference-cb (rf/defn get-keycard-banner-preference-cb
{:events [:get-keycard-banner-preference-cb]} {:events [:get-keycard-banner-preference-cb]}
[{:keys [db]} {:keys [keycard-banner-hidden]}] [{:keys [db]} {:keys [keycard-banner-hidden]}]
{:db (assoc db :keycard/banner-hidden keycard-banner-hidden)}) {:db (assoc db :keycard/banner-hidden keycard-banner-hidden)})
(fx/defn get-keycard-banner-preference (rf/defn get-keycard-banner-preference
{:events [:get-keycard-banner-preference]} {:events [:get-keycard-banner-preference]}
[_] [_]
{::async-storage/get {:keys [:keycard-banner-hidden] {::async-storage/get {:keys [:keycard-banner-hidden]
:cb #(re-frame/dispatch [:get-keycard-banner-preference-cb %])}}) :cb #(re-frame/dispatch [:get-keycard-banner-preference-cb %])}})
(fx/defn get-opted-in-to-new-terms-of-service-cb (rf/defn get-opted-in-to-new-terms-of-service-cb
{:events [:get-opted-in-to-new-terms-of-service-cb]} {:events [:get-opted-in-to-new-terms-of-service-cb]}
[{:keys [db]} {:keys [new-terms-of-service-accepted]}] [{:keys [db]} {:keys [new-terms-of-service-accepted]}]
{:db (assoc db :tos/accepted? new-terms-of-service-accepted)}) {:db (assoc db :tos/accepted? new-terms-of-service-accepted)})
(fx/defn get-opted-in-to-new-terms-of-service (rf/defn get-opted-in-to-new-terms-of-service
"New TOS sprint https://github.com/status-im/status-mobile/pull/12240" "New TOS sprint https://github.com/status-im/status-mobile/pull/12240"
{:events [:get-opted-in-to-new-terms-of-service]} {:events [:get-opted-in-to-new-terms-of-service]}
[{:keys [db]}] [{:keys [db]}]
{::async-storage/get {:keys [:new-terms-of-service-accepted] {::async-storage/get {:keys [:new-terms-of-service-accepted]
:cb #(re-frame/dispatch [:get-opted-in-to-new-terms-of-service-cb %])}}) :cb #(re-frame/dispatch [:get-opted-in-to-new-terms-of-service-cb %])}})
(fx/defn hide-terms-of-services-opt-in-screen (rf/defn hide-terms-of-services-opt-in-screen
{:events [:hide-terms-of-services-opt-in-screen]} {:events [:hide-terms-of-services-opt-in-screen]}
[{:keys [db]}] [{:keys [db]}]
{::async-storage/set! {:new-terms-of-service-accepted true} {::async-storage/set! {:new-terms-of-service-accepted true}
@@ -2,8 +2,8 @@
(:require [cljs.test :as test] (:require [cljs.test :as test]
[status-im.multiaccounts.biometric.core :as biometric] [status-im.multiaccounts.biometric.core :as biometric]
[status-im.multiaccounts.login.core :as login] [status-im.multiaccounts.login.core :as login]
[status-im.utils.fx :as fx] [status-im.utils.keychain.core :as keychain]
[status-im.utils.keychain.core :as keychain])) [utils.re-frame :as rf]))
(test/deftest save-password-test (test/deftest save-password-test
(test/testing "check save password, biometric unavailable" (test/testing "check save password, biometric unavailable"
@@ -44,7 +44,7 @@
"uncheck save password") "uncheck save password")
(let [initial-cofx {:db {:auth-method keychain/auth-method-none (let [initial-cofx {:db {:auth-method keychain/auth-method-none
:supported-biometric-auth true}} :supported-biometric-auth true}}
{:keys [db]} (login/save-password (fx/merge {:keys [db]} (login/save-password (rf/merge
initial-cofx initial-cofx
(login/save-password true) (login/save-password true)
(biometric/enable) (biometric/enable)
+17 -15
View File
@@ -1,19 +1,19 @@
(ns status-im.multiaccounts.logout.core (ns status-im.multiaccounts.logout.core
(:require [re-frame.core :as re-frame] (:require [re-frame.core :as re-frame]
[status-im.i18n.i18n :as i18n] [status-im.i18n.i18n :as i18n]
[status-im.native-module.core :as status]
[status-im.utils.fx :as fx]
[status-im.multiaccounts.core :as multiaccounts] [status-im.multiaccounts.core :as multiaccounts]
[status-im.utils.keychain.core :as keychain] [status-im.native-module.core :as status]
[status-im.notifications.core :as notifications] [status-im.notifications.core :as notifications]
[status-im.utils.keychain.core :as keychain]
[status-im.wallet.core :as wallet] [status-im.wallet.core :as wallet]
[status-im2.setup.events :as init])) [status-im2.setup.events :as init]
[utils.re-frame :as rf]))
(fx/defn logout-method (rf/defn logout-method
{:events [::logout-method]} {:events [::logout-method]}
[{:keys [db] :as cofx} {:keys [auth-method logout?]}] [{:keys [db] :as cofx} {:keys [auth-method logout?]}]
(let [key-uid (get-in db [:multiaccount :key-uid])] (let [key-uid (get-in db [:multiaccount :key-uid])]
(fx/merge cofx (rf/merge cofx
{:init-root-fx :progress {:init-root-fx :progress
:chat.ui/clear-inputs nil :chat.ui/clear-inputs nil
:chat.ui/clear-inputs-old nil :chat.ui/clear-inputs-old nil
@@ -22,24 +22,26 @@
::logout nil ::logout nil
::multiaccounts/webview-debug-changed false ::multiaccounts/webview-debug-changed false
:keychain/clear-user-password key-uid :keychain/clear-user-password key-uid
:setup/open-multiaccounts #(re-frame/dispatch [:setup/initialize-multiaccounts % {:logout? logout?}])} :setup/open-multiaccounts #(re-frame/dispatch [:setup/initialize-multiaccounts
% {:logout? logout?}])}
(keychain/save-auth-method key-uid auth-method) (keychain/save-auth-method key-uid auth-method)
(wallet/clear-timeouts) (wallet/clear-timeouts)
(init/initialize-app-db)))) (init/initialize-app-db))))
(fx/defn logout (rf/defn logout
{:events [:logout :multiaccounts.logout.ui/logout-confirmed :multiaccounts.update.callback/save-settings-success]} {:events [:logout :multiaccounts.logout.ui/logout-confirmed
:multiaccounts.update.callback/save-settings-success]}
[cofx] [cofx]
;; we need to disable notifications before starting the logout process ;; we need to disable notifications before starting the logout process
(fx/merge cofx (rf/merge cofx
{:dispatch [:wallet-connect-legacy/clean-up-sessions] {:dispatch [:wallet-connect-legacy/clean-up-sessions]
:dispatch-later [{:ms 100 :dispatch-later [{:ms 100
:dispatch [::logout-method :dispatch [::logout-method
{:auth-method keychain/auth-method-none {:auth-method keychain/auth-method-none
:logout? true}]}]} :logout? true}]}]}
(notifications/logout-disable))) (notifications/logout-disable)))
(fx/defn show-logout-confirmation (rf/defn show-logout-confirmation
{:events [:multiaccounts.logout.ui/logout-pressed]} {:events [:multiaccounts.logout.ui/logout-pressed]}
[_] [_]
{:ui/show-confirmation {:ui/show-confirmation
@@ -49,10 +51,10 @@
:on-accept #(re-frame/dispatch [:multiaccounts.logout.ui/logout-confirmed]) :on-accept #(re-frame/dispatch [:multiaccounts.logout.ui/logout-confirmed])
:on-cancel nil}}) :on-cancel nil}})
(fx/defn biometric-logout (rf/defn biometric-logout
{:events [:biometric-logout]} {:events [:biometric-logout]}
[cofx] [cofx]
(fx/merge cofx (rf/merge cofx
(logout-method {:auth-method keychain/auth-method-biometric-prepare (logout-method {:auth-method keychain/auth-method-biometric-prepare
:logout? false}) :logout? false})
(fn [{:keys [db]}] (fn [{:keys [db]}]
+85 -68
View File
@@ -1,49 +1,52 @@
(ns status-im.multiaccounts.recover.core (ns status-im.multiaccounts.recover.core
(:require [clojure.string :as string] (:require [clojure.string :as string]
[re-frame.core :as re-frame] [re-frame.core :as re-frame]
[status-im.bottom-sheet.core :as bottom-sheet]
[status-im.constants :as constants] [status-im.constants :as constants]
[status-im.ethereum.core :as ethereum] [status-im.ethereum.core :as ethereum]
[status-im.ethereum.mnemonic :as mnemonic] [status-im.ethereum.mnemonic :as mnemonic]
[status-im.keycard.nfc :as nfc]
[status-im.i18n.i18n :as i18n] [status-im.i18n.i18n :as i18n]
[status-im.keycard.nfc :as nfc]
[status-im.multiaccounts.core :as multiaccounts] [status-im.multiaccounts.core :as multiaccounts]
[status-im.multiaccounts.create.core :as multiaccounts.create] [status-im.multiaccounts.create.core :as multiaccounts.create]
[status-im.native-module.core :as status] [status-im.native-module.core :as status]
[status-im.popover.core :as popover] [status-im.popover.core :as popover]
[status-im2.navigation.events :as navigation]
[status-im.utils.fx :as fx]
[utils.security.core :as security]
[status-im.utils.types :as types] [status-im.utils.types :as types]
[status-im.utils.utils :as utils] [status-im.utils.utils :as utils]
[status-im.bottom-sheet.core :as bottom-sheet] [status-im2.navigation.events :as navigation]
[taoensso.timbre :as log])) [taoensso.timbre :as log]
[utils.re-frame :as rf]
[utils.security.core :as security]))
(defn existing-account? (defn existing-account?
[multiaccounts key-uid] [multiaccounts key-uid]
{:pre [(not (nil? key-uid))]} {:pre [(not (nil? key-uid))]}
(contains? multiaccounts key-uid)) (contains? multiaccounts key-uid))
(defn check-phrase-warnings [recovery-phrase] (defn check-phrase-warnings
[recovery-phrase]
(cond (string/blank? recovery-phrase) :t/required-field)) (cond (string/blank? recovery-phrase) :t/required-field))
(fx/defn set-phrase (rf/defn set-phrase
{:events [:multiaccounts.recover/passphrase-input-changed]} {:events [:multiaccounts.recover/passphrase-input-changed]}
[{:keys [db]} masked-recovery-phrase] [{:keys [db]} masked-recovery-phrase]
(let [recovery-phrase (security/safe-unmask-data masked-recovery-phrase)] (let [recovery-phrase (security/safe-unmask-data masked-recovery-phrase)]
(fx/merge (rf/merge
{:db (update db :intro-wizard assoc {:db (update db
:passphrase (string/lower-case recovery-phrase) :intro-wizard assoc
:passphrase-error nil :passphrase (string/lower-case recovery-phrase)
:passphrase-error nil
:next-button-disabled? (or (empty? recovery-phrase) :next-button-disabled? (or (empty? recovery-phrase)
(not (mnemonic/valid-length? recovery-phrase))))}))) (not (mnemonic/valid-length? recovery-phrase))))})))
(fx/defn validate-phrase-for-warnings (rf/defn validate-phrase-for-warnings
[{:keys [db]}] [{:keys [db]}]
(let [recovery-phrase (get-in db [:intro-wizard :passphrase])] (let [recovery-phrase (get-in db [:intro-wizard :passphrase])]
{:db (update db :intro-wizard assoc {:db (update db
:intro-wizard assoc
:passphrase-error (check-phrase-warnings recovery-phrase))})) :passphrase-error (check-phrase-warnings recovery-phrase))}))
(fx/defn on-store-multiaccount-success (rf/defn on-store-multiaccount-success
{:events [::store-multiaccount-success] {:events [::store-multiaccount-success]
:interceptors [(re-frame/inject-cofx :random-guid-generator) :interceptors [(re-frame/inject-cofx :random-guid-generator)
(re-frame/inject-cofx ::multiaccounts.create/get-signing-phrase)]} (re-frame/inject-cofx ::multiaccounts.create/get-signing-phrase)]}
@@ -54,29 +57,32 @@
:content (i18n/label :t/multiaccount-exists-title) :content (i18n/label :t/multiaccount-exists-title)
:on-dismiss #(re-frame/dispatch [:pop-to-root-tab :multiaccounts])}} :on-dismiss #(re-frame/dispatch [:pop-to-root-tab :multiaccounts])}}
(let [{:keys [key-uid] :as multiaccount} (get-in db [:intro-wizard :root-key]) (let [{:keys [key-uid] :as multiaccount} (get-in db [:intro-wizard :root-key])
keycard-multiaccount? (boolean (get-in db [:multiaccounts/multiaccounts key-uid :keycard-pairing]))] keycard-multiaccount? (boolean (get-in db
[:multiaccounts/multiaccounts key-uid
:keycard-pairing]))]
(if keycard-multiaccount? (if keycard-multiaccount?
;; trying to recover multiaccount created with keycard ;; trying to recover multiaccount created with keycard
{:db (-> db {:db (-> db
(update :intro-wizard assoc (update :intro-wizard assoc
:processing? false :processing? false
:passphrase-error :recover-keycard-multiaccount-not-supported) :passphrase-error :recover-keycard-multiaccount-not-supported)
(update :intro-wizard dissoc (update :intro-wizard
:passphrase-valid?))} dissoc
:passphrase-valid?))}
(let [multiaccount (assoc multiaccount :derived (get-in db [:intro-wizard :derived]))] (let [multiaccount (assoc multiaccount :derived (get-in db [:intro-wizard :derived]))]
(multiaccounts.create/on-multiaccount-created cofx (multiaccounts.create/on-multiaccount-created cofx
multiaccount multiaccount
password password
{}))))))) {})))))))
(fx/defn store-multiaccount (rf/defn store-multiaccount
{:events [::recover-multiaccount-confirmed]} {:events [::recover-multiaccount-confirmed]}
[{:keys [db]} password] [{:keys [db]} password]
(let [{:keys [root-key]} (:intro-wizard db) (let [{:keys [root-key]} (:intro-wizard db)
{:keys [id key-uid]} root-key {:keys [id key-uid]} root-key
callback #(re-frame/dispatch [::store-multiaccount-success % password]) callback #(re-frame/dispatch [::store-multiaccount-success % password])
hashed-password (ethereum/sha3 (security/safe-unmask-data password))] hashed-password (ethereum/sha3 (security/safe-unmask-data password))]
{:db (assoc-in db [:intro-wizard :processing?] true) {:db (assoc-in db [:intro-wizard :processing?] true)
::multiaccounts.create/store-multiaccount [id key-uid hashed-password callback]})) ::multiaccounts.create/store-multiaccount [id key-uid hashed-password callback]}))
(re-frame/reg-fx (re-frame/reg-fx
@@ -99,17 +105,18 @@
(fn [result] (fn [result]
(let [derived-data (multiaccounts.create/normalize-derived-data-keys (let [derived-data (multiaccounts.create/normalize-derived-data-keys
(types/json->clj result)) (types/json->clj result))
public-key (get-in derived-data [constants/path-whisper-keyword :public-key])] public-key (get-in derived-data [constants/path-whisper-keyword :public-key])]
(status/gfycat-identicon-async (status/gfycat-identicon-async
public-key public-key
(fn [name identicon] (fn [name identicon]
(let [derived-data-extended (let [derived-data-extended
(update derived-data (update derived-data
constants/path-whisper-keyword constants/path-whisper-keyword
merge {:name name :identicon identicon})] merge
{:name name :identicon identicon})]
(re-frame/dispatch [success-event root-data derived-data-extended])))))))))))) (re-frame/dispatch [success-event root-data derived-data-extended]))))))))))))
(fx/defn show-existing-multiaccount-alert (rf/defn show-existing-multiaccount-alert
[_ key-uid] [_ key-uid]
{:utils/show-confirmation {:utils/show-confirmation
{:title (i18n/label :t/multiaccount-exists-title) {:title (i18n/label :t/multiaccount-exists-title)
@@ -121,24 +128,26 @@
[:multiaccounts.login.ui/multiaccount-selected key-uid])) [:multiaccounts.login.ui/multiaccount-selected key-uid]))
:on-cancel #(re-frame/dispatch [:pop-to-root-tab :multiaccounts])}}) :on-cancel #(re-frame/dispatch [:pop-to-root-tab :multiaccounts])}})
(fx/defn on-import-multiaccount-success (rf/defn on-import-multiaccount-success
{:events [::import-multiaccount-success]} {:events [::import-multiaccount-success]}
[{:keys [db] :as cofx} {:keys [key-uid] :as root-data} derived-data] [{:keys [db] :as cofx} {:keys [key-uid] :as root-data} derived-data]
(let [multiaccounts (:multiaccounts/multiaccounts db)] (let [multiaccounts (:multiaccounts/multiaccounts db)]
(fx/merge (rf/merge
cofx cofx
{:db (update db :intro-wizard {:db (update db
assoc :root-key root-data :intro-wizard
:derived derived-data assoc
:step :recovery-success)} :root-key root-data
:derived derived-data
:step :recovery-success)}
(when (existing-account? multiaccounts key-uid) (when (existing-account? multiaccounts key-uid)
(show-existing-multiaccount-alert key-uid)) (show-existing-multiaccount-alert key-uid))
(navigation/navigate-to-cofx :recover-multiaccount-success nil)))) (navigation/navigate-to-cofx :recover-multiaccount-success nil))))
(fx/defn enter-phrase-pressed (rf/defn enter-phrase-pressed
{:events [::enter-phrase-pressed]} {:events [::enter-phrase-pressed]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(fx/merge (rf/merge
cofx cofx
{:db (-> db {:db (-> db
(assoc :intro-wizard (assoc :intro-wizard
@@ -153,7 +162,7 @@
(bottom-sheet/hide-bottom-sheet) (bottom-sheet/hide-bottom-sheet)
(navigation/navigate-to-cofx :recover-multiaccount-enter-phrase nil))) (navigation/navigate-to-cofx :recover-multiaccount-enter-phrase nil)))
(fx/defn proceed-to-import-mnemonic (rf/defn proceed-to-import-mnemonic
{:events [:multiaccounts.recover/phrase-validated]} {:events [:multiaccounts.recover/phrase-validated]}
[{:keys [db] :as cofx} phrase-warnings] [{:keys [db] :as cofx} phrase-warnings]
(let [{:keys [password passphrase]} (:intro-wizard db)] (let [{:keys [password passphrase]} (:intro-wizard db)]
@@ -165,37 +174,41 @@
(security/safe-unmask-data password)) (security/safe-unmask-data password))
:success-event ::import-multiaccount-success}})))) :success-event ::import-multiaccount-success}}))))
(fx/defn seed-phrase-next-pressed (rf/defn seed-phrase-next-pressed
{:events [:multiaccounts.recover/enter-phrase-next-pressed]} {:events [:multiaccounts.recover/enter-phrase-next-pressed]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(let [{:keys [passphrase]} (:intro-wizard db)] (let [{:keys [passphrase]} (:intro-wizard db)]
{::multiaccounts/validate-mnemonic [passphrase #(re-frame/dispatch [:multiaccounts.recover/phrase-validated %])]})) {::multiaccounts/validate-mnemonic [passphrase
#(re-frame/dispatch [:multiaccounts.recover/phrase-validated
%])]}))
(fx/defn continue-to-import-mnemonic (rf/defn continue-to-import-mnemonic
{:events [::continue-pressed]} {:events [::continue-pressed]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(let [{:keys [password passphrase]} (:multiaccounts/recover db)] (let [{:keys [password passphrase]} (:multiaccounts/recover db)]
(fx/merge cofx (rf/merge cofx
{::import-multiaccount {:passphrase passphrase {::import-multiaccount {:passphrase passphrase
:password password :password password
:success-event ::import-multiaccount-success}} :success-event ::import-multiaccount-success}}
(popover/hide-popover)))) (popover/hide-popover))))
(fx/defn dec-step (rf/defn dec-step
{:events [:multiaccounts.recover/dec-step]} {:events [:multiaccounts.recover/dec-step]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(let [step (get-in db [:intro-wizard :step])] (let [step (get-in db [:intro-wizard :step])]
(if (= step :enter-phrase) (if (= step :enter-phrase)
{:db (dissoc db :intro-wizard)} {:db (dissoc db :intro-wizard)}
{:db (update db :intro-wizard assoc :step {:db (update db
:intro-wizard assoc
:step
(case step (case step
:recovery-success :enter-phrase :recovery-success :enter-phrase
:select-key-storage :recovery-success :select-key-storage :recovery-success
:create-code :select-key-storage) :create-code :select-key-storage)
:confirm-failure? false :confirm-failure? false
:key-code nil)}))) :key-code nil)})))
(fx/defn cancel-pressed (rf/defn cancel-pressed
{:events [:multiaccounts.recover/cancel-pressed]} {:events [:multiaccounts.recover/cancel-pressed]}
[{:keys [db] :as cofx} skip-alert?] [{:keys [db] :as cofx} skip-alert?]
;; Workaround for multiple Cancel button clicks ;; Workaround for multiple Cancel button clicks
@@ -207,11 +220,11 @@
(i18n/label :t/are-you-sure-to-cancel) (i18n/label :t/are-you-sure-to-cancel)
(i18n/label :t/you-will-start-from-scratch) (i18n/label :t/you-will-start-from-scratch)
#(re-frame/dispatch [:multiaccounts.recover/cancel-pressed true])) #(re-frame/dispatch [:multiaccounts.recover/cancel-pressed true]))
(fx/merge cofx (rf/merge cofx
dec-step dec-step
navigation/navigate-back))))) navigation/navigate-back)))))
(fx/defn select-storage-next-pressed (rf/defn select-storage-next-pressed
{:events [:multiaccounts.recover/select-storage-next-pressed] {:events [:multiaccounts.recover/select-storage-next-pressed]
:interceptors [(re-frame/inject-cofx :random-guid-generator)]} :interceptors [(re-frame/inject-cofx :random-guid-generator)]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
@@ -219,51 +232,55 @@
(if (= storage-type :advanced) (if (= storage-type :advanced)
;;TODO: fix circular dependency to remove dispatch here ;;TODO: fix circular dependency to remove dispatch here
{:dispatch [:recovery.ui/keycard-option-pressed]} {:dispatch [:recovery.ui/keycard-option-pressed]}
(fx/merge cofx (rf/merge cofx
{:db (update db :intro-wizard assoc :step :create-code)} {:db (update db :intro-wizard assoc :step :create-code)}
(navigation/navigate-to-cofx :create-password nil))))) (navigation/navigate-to-cofx :create-password nil)))))
(fx/defn re-encrypt-pressed (rf/defn re-encrypt-pressed
{:events [:multiaccounts.recover/re-encrypt-pressed]} {:events [:multiaccounts.recover/re-encrypt-pressed]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(fx/merge cofx (rf/merge cofx
{:db (update db :intro-wizard {:db (update db
assoc :step :select-key-storage :intro-wizard
assoc
:step :select-key-storage
:selected-storage-type :default)} :selected-storage-type :default)}
(if (nfc/nfc-supported?) (if (nfc/nfc-supported?)
(navigation/navigate-to-cofx :select-key-storage nil) (navigation/navigate-to-cofx :select-key-storage nil)
(select-storage-next-pressed)))) (select-storage-next-pressed))))
(fx/defn confirm-password-next-button-pressed (rf/defn confirm-password-next-button-pressed
{:events [:multiaccounts.recover/enter-password-next-pressed] {:events [:multiaccounts.recover/enter-password-next-pressed]
:interceptors [(re-frame/inject-cofx :random-guid-generator)]} :interceptors [(re-frame/inject-cofx :random-guid-generator)]}
[cofx key-code] [cofx key-code]
(store-multiaccount cofx key-code)) (store-multiaccount cofx key-code))
(fx/defn count-words (rf/defn count-words
[{:keys [db]}] [{:keys [db]}]
(let [passphrase (get-in db [:intro-wizard :passphrase])] (let [passphrase (get-in db [:intro-wizard :passphrase])]
{:db (assoc-in db [:intro-wizard :passphrase-word-count] {:db (assoc-in db
(mnemonic/words-count passphrase))})) [:intro-wizard :passphrase-word-count]
(mnemonic/words-count passphrase))}))
(fx/defn run-validation (rf/defn run-validation
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(let [passphrase (get-in db [:intro-wizard :passphrase])] (let [passphrase (get-in db [:intro-wizard :passphrase])]
(when (= (last passphrase) " ") (when (= (last passphrase) " ")
(fx/merge cofx (rf/merge cofx
(validate-phrase-for-warnings))))) (validate-phrase-for-warnings)))))
(fx/defn enter-phrase-input-changed (rf/defn enter-phrase-input-changed
{:events [:multiaccounts.recover/enter-phrase-input-changed]} {:events [:multiaccounts.recover/enter-phrase-input-changed]}
[cofx input] [cofx input]
(fx/merge cofx (rf/merge cofx
(set-phrase input) (set-phrase input)
(count-words) (count-words)
(run-validation))) (run-validation)))
(fx/defn enter-passphrase-input-changed (rf/defn enter-passphrase-input-changed
{:events [:multiaccounts.recover/enter-passphrase-input-changed]} {:events [:multiaccounts.recover/enter-passphrase-input-changed]}
[{:keys [db]} masked-passphrase] [{:keys [db]} masked-passphrase]
{:db (update db :intro-wizard assoc {:db (update db
:password masked-passphrase :intro-wizard assoc
:password masked-passphrase
:password-error nil)}) :password-error nil)})
@@ -1,44 +1,44 @@
(ns status-im.multiaccounts.reset-password.core (ns status-im.multiaccounts.reset-password.core
(:require [re-frame.core :as re-frame] (:require [clojure.string :as string]
[status-im.utils.fx :as fx] [re-frame.core :as re-frame]
[status-im.utils.types :as types] [status-im.ethereum.core :as ethereum]
[clojure.string :as string]
[utils.security.core :as security]
[status-im.utils.keychain.core :as keychain]
[status-im.popover.core :as popover]
[status-im.native-module.core :as status] [status-im.native-module.core :as status]
[status-im.ethereum.core :as ethereum])) [status-im.popover.core :as popover]
[status-im.utils.keychain.core :as keychain]
[status-im.utils.types :as types]
[utils.re-frame :as rf]
[utils.security.core :as security]))
(fx/defn on-input-change (rf/defn on-input-change
{:events [::handle-input-change]} {:events [::handle-input-change]}
[{:keys [db]} input-id value] [{:keys [db]} input-id value]
(let [new-password (get-in db [:multiaccount/reset-password-form-vals :new-password]) (let [new-password (get-in db [:multiaccount/reset-password-form-vals :new-password])
error (when (and (= input-id :confirm-new-password) error (when (and (= input-id :confirm-new-password)
(pos? (count new-password)) (pos? (count new-password))
(pos? (count value)) (pos? (count value))
(not= value new-password)) (not= value new-password))
:t/password-mismatch)] :t/password-mismatch)]
{:db (-> db {:db (-> db
(assoc-in [:multiaccount/reset-password-form-vals input-id] value) (assoc-in [:multiaccount/reset-password-form-vals input-id] value)
(assoc-in [:multiaccount/reset-password-errors input-id] error))})) (assoc-in [:multiaccount/reset-password-errors input-id] error))}))
(fx/defn clear-form-vals (rf/defn clear-form-vals
{:events [::clear-form-vals]} {:events [::clear-form-vals]}
[{:keys [db]}] [{:keys [db]}]
{:db (dissoc db :multiaccount/reset-password-form-vals :multiaccount/reset-password-errors)}) {:db (dissoc db :multiaccount/reset-password-form-vals :multiaccount/reset-password-errors)})
(fx/defn set-current-password-error (rf/defn set-current-password-error
{:events [::handle-verification-error ::password-reset-error]} {:events [::handle-verification-error ::password-reset-error]}
[{:keys [db]} error] [{:keys [db]} error]
{:db (assoc-in db [:multiaccount/reset-password-errors :current-password] error)}) {:db (assoc-in db [:multiaccount/reset-password-errors :current-password] error)})
(fx/defn password-reset-success (rf/defn password-reset-success
{:events [::password-reset-success]} {:events [::password-reset-success]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(let [{:keys [key-uid]} (:multiaccount db) (let [{:keys [key-uid]} (:multiaccount db)
auth-method (get db :auth-method keychain/auth-method-none) auth-method (get db :auth-method keychain/auth-method-none)
new-password (get-in db [:multiaccount/reset-password-form-vals :new-password])] new-password (get-in db [:multiaccount/reset-password-form-vals :new-password])]
(fx/merge cofx (rf/merge cofx
{:db (dissoc {:db (dissoc
db db
:multiaccount/reset-password-form-vals :multiaccount/reset-password-form-vals
@@ -49,7 +49,8 @@
(when (= auth-method keychain/auth-method-biometric) (when (= auth-method keychain/auth-method-biometric)
(keychain/save-user-password key-uid new-password))))) (keychain/save-user-password key-uid new-password)))))
(defn change-db-password-cb [res] (defn change-db-password-cb
[res]
(let [{:keys [error]} (types/json->clj res)] (let [{:keys [error]} (types/json->clj res)]
(if (not (string/blank? error)) (if (not (string/blank? error))
(re-frame/dispatch [::password-reset-error error]) (re-frame/dispatch [::password-reset-error error])
@@ -64,18 +65,20 @@
(ethereum/sha3 (security/safe-unmask-data new-password)) (ethereum/sha3 (security/safe-unmask-data new-password))
change-db-password-cb))) change-db-password-cb)))
(fx/defn handle-verification-success (rf/defn handle-verification-success
{:events [::handle-verification-success]} {:events [::handle-verification-success]}
[{:keys [db] :as cofx} form-vals] [{:keys [db] :as cofx} form-vals]
(let [{:keys [key-uid name]} (:multiaccount db)] (let [{:keys [key-uid name]} (:multiaccount db)]
(fx/merge cofx (rf/merge cofx
{::change-db-password [key-uid form-vals] {::change-db-password [key-uid form-vals]
:db (assoc db :db (assoc db
:multiaccount/resetting-password? true)} :multiaccount/resetting-password?
true)}
(popover/show-popover {:view :password-reset-popover (popover/show-popover {:view :password-reset-popover
:prevent-closing? true})))) :prevent-closing? true}))))
(defn handle-verification [form-vals result] (defn handle-verification
[form-vals result]
(let [{:keys [error]} (types/json->clj result)] (let [{:keys [error]} (types/json->clj result)]
(if (not (string/blank? error)) (if (not (string/blank? error))
(re-frame/dispatch [::handle-verification-error :t/wrong-password]) (re-frame/dispatch [::handle-verification-error :t/wrong-password])
@@ -83,12 +86,13 @@
(re-frame/reg-fx (re-frame/reg-fx
::validate-current-password-and-reset ::validate-current-password-and-reset
(fn [{:keys [address current-password] :as form-vals}] (fn [{:keys [address current-password] :as form-vals}]
(let [hashed-pass (ethereum/sha3 (security/safe-unmask-data current-password))] (let [hashed-pass (ethereum/sha3 (security/safe-unmask-data current-password))]
(status/verify address hashed-pass (status/verify address
hashed-pass
(partial handle-verification form-vals))))) (partial handle-verification form-vals)))))
(fx/defn reset (rf/defn reset
{:events [::reset]} {:events [::reset]}
[{:keys [db]} form-vals] [{:keys [db]} form-vals]
{::validate-current-password-and-reset {::validate-current-password-and-reset
+53 -41
View File
@@ -1,18 +1,19 @@
(ns status-im.multiaccounts.update.core (ns status-im.multiaccounts.update.core
(:require [status-im.constants :as constants] (:require [status-im.constants :as constants]
[status-im.ethereum.json-rpc :as json-rpc] [status-im.ethereum.json-rpc :as json-rpc]
[status-im.utils.fx :as fx]
[status-im.utils.types :as types] [status-im.utils.types :as types]
[taoensso.timbre :as log])) [taoensso.timbre :as log]
[utils.re-frame :as rf]))
(fx/defn send-multiaccount-update [{:keys [db] :as cofx}] (rf/defn send-multiaccount-update
(let [multiaccount (:multiaccount db) [{:keys [db] :as cofx}]
(let [multiaccount (:multiaccount db)
{:keys [name preferred-name address]} multiaccount] {:keys [name preferred-name address]} multiaccount]
{::json-rpc/call [{:method "wakuext_sendContactUpdates" {::json-rpc/call [{:method "wakuext_sendContactUpdates"
:params [(or preferred-name name) ""] :params [(or preferred-name name) ""]
:on-success #(log/debug "sent contact update")}]})) :on-success #(log/debug "sent contact update")}]}))
(fx/defn multiaccount-update (rf/defn multiaccount-update
"Takes effects (containing :db) + new multiaccount fields, adds all effects necessary for multiaccount update. "Takes effects (containing :db) + new multiaccount fields, adds all effects necessary for multiaccount update.
Optionally, one can specify a success-event to be dispatched after fields are persisted." Optionally, one can specify a success-event to be dispatched after fields are persisted."
[{:keys [db] :as cofx} [{:keys [db] :as cofx}
@@ -22,20 +23,22 @@
(if (empty? current-multiaccount) (if (empty? current-multiaccount)
;; NOTE: this should never happen, but if it does this is a critical error ;; NOTE: this should never happen, but if it does this is a critical error
;; and it is better to crash than risk having an unstable state ;; and it is better to crash than risk having an unstable state
(throw (js/Error. "Please shake the phone to report this error and restart the app. multiaccount is currently empty, which means something went wrong when trying to update it with")) (throw
(fx/merge cofx (js/Error.
{:db (if setting-value "Please shake the phone to report this error and restart the app. multiaccount is currently empty, which means something went wrong when trying to update it with"))
(assoc-in db [:multiaccount setting] setting-value) (rf/merge cofx
(update db :multiaccount dissoc setting)) {:db (if setting-value
(assoc-in db [:multiaccount setting] setting-value)
(update db :multiaccount dissoc setting))
::json-rpc/call ::json-rpc/call
[{:method "settings_saveSetting" [{:method "settings_saveSetting"
:params [setting setting-value] :params [setting setting-value]
:on-success on-success}]} :on-success on-success}]}
(when (and (not dont-sync?) (when (and (not dont-sync?)
(#{:name :prefered-name} setting)) (#{:name :prefered-name} setting))
(send-multiaccount-update)))))) (send-multiaccount-update))))))
(fx/defn clean-seed-phrase (rf/defn clean-seed-phrase
"A helper function that removes seed phrase from storage." "A helper function that removes seed phrase from storage."
[cofx on-success] [cofx on-success]
(multiaccount-update cofx :mnemonic nil on-success)) (multiaccount-update cofx :mnemonic nil on-success))
@@ -44,38 +47,47 @@
"Add 'url' parameter to stickers that are synchronized from other devices. "Add 'url' parameter to stickers that are synchronized from other devices.
It is not sent from aanother devices but we have it in our db." It is not sent from aanother devices but we have it in our db."
[synced-stickers stickers-from-db] [synced-stickers stickers-from-db]
(mapv #(assoc % :url (->> (get stickers-from-db (or (:packID %) (:pack %))) (mapv #(assoc %
(:stickers) :url
(filter (fn [sticker-db] (= (:hash sticker-db) (:hash %)))) (->> (get stickers-from-db (or (:packID %) (:pack %)))
(first) (:stickers)
(:url))) (filter (fn [sticker-db] (= (:hash sticker-db) (:hash %))))
(first)
(:url)))
synced-stickers)) synced-stickers))
(fx/defn optimistic (rf/defn optimistic
[{:keys [db] :as cofx} setting setting-value] [{:keys [db] :as cofx} setting setting-value]
(let [current-multiaccount (:multiaccount db) (let [current-multiaccount (:multiaccount db)
setting-value (if (= :currency setting) setting-value (if (= :currency setting)
(keyword setting-value) (keyword setting-value)
setting-value) setting-value)
db (case setting db (case setting
:stickers/packs-pending :stickers/packs-pending
(let [packs-pending (keys (js->clj setting-value))] (let [packs-pending (keys (js->clj setting-value))]
(update db :stickers/packs-pending conj packs-pending)) (update db :stickers/packs-pending conj packs-pending))
:stickers/packs-installed :stickers/packs-installed
(let [packs-installed-keys (keys (js->clj setting-value))] (let [packs-installed-keys (keys (js->clj setting-value))]
(reduce #(assoc-in %1 [:stickers/packs %2 :status] constants/sticker-pack-status-installed) db packs-installed-keys)) (reduce #(assoc-in %1
:stickers/recent-stickers [:stickers/packs %2 :status]
(let [recent-stickers-from-remote (augment-synchronized-recent-stickers (types/js->clj setting-value) (:stickers/packs db)) constants/sticker-pack-status-installed)
merged (into recent-stickers-from-remote (:stickers/recent-stickers db))] db
(assoc db :stickers/recent-stickers recent-stickers-from-remote)) packs-installed-keys))
db)] :stickers/recent-stickers
(let [recent-stickers-from-remote (augment-synchronized-recent-stickers
(types/js->clj setting-value)
(:stickers/packs db))
merged (into recent-stickers-from-remote
(:stickers/recent-stickers db))]
(assoc db :stickers/recent-stickers recent-stickers-from-remote))
db)]
{:db (if setting-value {:db (if setting-value
(assoc-in db [:multiaccount setting] setting-value) (assoc-in db [:multiaccount setting] setting-value)
(update db :multiaccount dissoc setting))})) (update db :multiaccount dissoc setting))}))
(fx/defn set-many-js (rf/defn set-many-js
[cofx settings-js] [cofx settings-js]
(apply fx/merge (apply rf/merge
cofx cofx
(map (map
#(optimistic #(optimistic
@@ -83,15 +95,15 @@
(.-value %)) (.-value %))
settings-js))) settings-js)))
(fx/defn toggle-backup-enabled (rf/defn toggle-backup-enabled
{:events [:multiaccounts.ui/switch-backup-enabled]} {:events [:multiaccounts.ui/switch-backup-enabled]}
[cofx enabled?] [cofx enabled?]
(multiaccount-update cofx :backup-enabled? enabled? {})) (multiaccount-update cofx :backup-enabled? enabled? {}))
(fx/defn toggle-opensea-nfts-visibility (rf/defn toggle-opensea-nfts-visibility
{:events [::toggle-opensea-nfts-visiblity]} {:events [::toggle-opensea-nfts-visiblity]}
[cofx visible?] [cofx visible?]
(fx/merge cofx (rf/merge cofx
{:db (assoc-in (:db cofx) [:multiaccount :opensea-enabled?] visible?) {:db (assoc-in (:db cofx) [:multiaccount :opensea-enabled?] visible?)
;; need to add fully qualified namespace to counter circular deps ;; need to add fully qualified namespace to counter circular deps
:dispatch [:status-im.wallet.core/fetch-collectibles-collection]} :dispatch [:status-im.wallet.core/fetch-collectibles-collection]}
+230 -203
View File
@@ -1,31 +1,31 @@
(ns status-im.navigation.core (ns status-im.navigation.core
(:require (:require ["react-native" :as rn]
["react-native" :as rn] ["react-native-gesture-handler" :refer (gestureHandlerRootHOC)]
["react-native-gesture-handler" :refer (gestureHandlerRootHOC)] ["react-native-navigation" :refer (Navigation)]
["react-native-navigation" :refer (Navigation)] [clojure.set :as clojure.set]
[clojure.set :as clojure.set] [quo.components.text-input :as quo.text-input]
[quo.components.text-input :as quo.text-input] [quo.design-system.colors :as quo.colors]
[quo.design-system.colors :as quo.colors] [re-frame.core :as re-frame]
[re-frame.core :as re-frame] [status-im.multiaccounts.login.core :as login-core]
[status-im.multiaccounts.login.core :as login-core] [status-im.navigation.roots :as roots]
[status-im.navigation.roots :as roots] [status-im.navigation.state :as state]
[status-im.navigation.state :as state] [status-im.ui.components.icons.icons :as icons]
[status-im.ui.components.icons.icons :as icons] [status-im.ui.components.react :as react]
[status-im.ui.components.react :as react] [status-im.ui.screens.views :as views]
[status-im.ui.screens.views :as views] [status-im.utils.platform :as platform]
[status-im.utils.fx :as fx] [taoensso.timbre :as log]
[status-im.utils.platform :as platform] [utils.re-frame :as rf]))
[taoensso.timbre :as log]))
(def debug? ^boolean js/goog.DEBUG) (def debug? ^boolean js/goog.DEBUG)
(def splash-screen (-> rn .-NativeModules .-SplashScreen)) (def splash-screen (-> rn .-NativeModules .-SplashScreen))
(defonce set-navigation-default-options (defonce set-navigation-default-options
(.setDefaultOptions Navigation (clj->js {:layout {:orientation "portrait"}}))) (.setDefaultOptions Navigation (clj->js {:layout {:orientation "portrait"}})))
;; REGISTER COMPONENT (LAZY) ;; REGISTER COMPONENT (LAZY)
(defn reg-comp [key] (defn reg-comp
[key]
(log/debug "reg-comp" key) (log/debug "reg-comp" key)
(if-let [comp (get views/components (keyword key))] (if-let [comp (get views/components (keyword key))]
(.registerComponent Navigation key (fn [] (views/component comp))) (.registerComponent Navigation key (fn [] (views/component comp)))
@@ -33,9 +33,10 @@
(.registerComponent Navigation key (fn [] (gestureHandlerRootHOC screen)) (fn [] screen))))) (.registerComponent Navigation key (fn [] (gestureHandlerRootHOC screen)) (fn [] screen)))))
(defonce rset-lazy-reg (defonce rset-lazy-reg
(.setLazyComponentRegistrator Navigation reg-comp)) (.setLazyComponentRegistrator Navigation reg-comp))
(defn dismiss-all-modals [] (defn dismiss-all-modals
[]
(log/debug "dissmiss-all-modals") (log/debug "dissmiss-all-modals")
(when @state/curr-modal (when @state/curr-modal
(reset! state/curr-modal false) (reset! state/curr-modal false)
@@ -45,21 +46,24 @@
(reset! state/modals []))) (reset! state/modals [])))
;; PUSH SCREEN ;; PUSH SCREEN
(defn navigate [comp] (defn navigate
[comp]
(log/debug "NAVIGATE" comp) (log/debug "NAVIGATE" comp)
(let [{:keys [options]} (get views/screens comp)] (let [{:keys [options]} (get views/screens comp)]
(.push Navigation (.push Navigation
(name @state/root-comp-id) (name @state/root-comp-id)
(clj->js {:component {:id comp (clj->js
:name comp {:component {:id comp
:options (merge options :name comp
(roots/status-bar-options) :options (merge options
(roots/merge-top-bar (roots/topbar-options) options))}})) (roots/status-bar-options)
(roots/merge-top-bar (roots/topbar-options) options))}}))
;;if we push the screen from modal, we want to dismiss all modals ;;if we push the screen from modal, we want to dismiss all modals
(dismiss-all-modals))) (dismiss-all-modals)))
;; OPEN MODAL ;; OPEN MODAL
(defn update-modal-topbar-options [options] (defn update-modal-topbar-options
[options]
(log/debug "update-modal-topbar-options" options) (log/debug "update-modal-topbar-options" options)
(merge options (merge options
(roots/merge-top-bar {:elevation 0 (roots/merge-top-bar {:elevation 0
@@ -71,7 +75,8 @@
:icon (icons/icon-source :main-icons/close)}} :icon (icons/icon-source :main-icons/close)}}
options))) options)))
(defn open-modal [comp] (defn open-modal
[comp]
(log/debug "open-modal" comp) (log/debug "open-modal" comp)
(let [{:keys [options]} (get views/screens comp)] (let [{:keys [options]} (get views/screens comp)]
(if @state/dissmissing (if @state/dissmissing
@@ -92,25 +97,27 @@
(re-frame/reg-fx :open-modal-fx open-modal) (re-frame/reg-fx :open-modal-fx open-modal)
;; DISSMISS MODAL ;; DISSMISS MODAL
(defn dissmissModal [] (defn dissmissModal
[]
(log/debug "dissmissModal") (log/debug "dissmissModal")
(reset! state/dissmissing true) (reset! state/dissmissing true)
(.dismissModal Navigation (name (last @state/modals)))) (.dismissModal Navigation (name (last @state/modals))))
(defonce register-nav-button-reg (defonce register-nav-button-reg
(.registerNavigationButtonPressedListener (.registerNavigationButtonPressedListener
(.events Navigation) (.events Navigation)
(fn [^js evn] (fn [^js evn]
(let [id (.-buttonId evn)] (let [id (.-buttonId evn)]
(if (= "dismiss-modal" id) (if (= "dismiss-modal" id)
(do (do
(when-let [event (get-in views/screens [(last @state/modals) :on-dissmiss])] (when-let [event (get-in views/screens [(last @state/modals) :on-dissmiss])]
(re-frame/dispatch event)) (re-frame/dispatch event))
(dissmissModal)) (dissmissModal))
(when-let [handler (get-in views/screens [(keyword id) :right-handler])] (when-let [handler (get-in views/screens [(keyword id) :right-handler])]
(handler))))))) (handler)))))))
(defn set-view-id [view-id] (defn set-view-id
[view-id]
(log/debug "set-view-id" view-id) (log/debug "set-view-id" view-id)
(when-let [{:keys [on-focus]} (get views/screens view-id)] (when-let [{:keys [on-focus]} (get views/screens view-id)]
(re-frame/dispatch [:set-view-id view-id]) (re-frame/dispatch [:set-view-id view-id])
@@ -119,51 +126,53 @@
(re-frame/dispatch on-focus)))) (re-frame/dispatch on-focus))))
(defonce register-modal-reg (defonce register-modal-reg
(.registerModalDismissedListener (.registerModalDismissedListener
(.events Navigation) (.events Navigation)
(fn [_] (fn [_]
(if (> (count @state/modals) 1) (if (> (count @state/modals) 1)
(let [new-modals (butlast @state/modals)] (let [new-modals (butlast @state/modals)]
(reset! state/modals (vec new-modals)) (reset! state/modals (vec new-modals))
(set-view-id (last new-modals))) (set-view-id (last new-modals)))
(do (do
(reset! state/modals []) (reset! state/modals [])
(reset! state/curr-modal false) (reset! state/curr-modal false)
(set-view-id @state/pushed-screen-id))) (set-view-id @state/pushed-screen-id)))
(let [comp @state/dissmissing] (let [comp @state/dissmissing]
(reset! state/dissmissing false) (reset! state/dissmissing false)
(when (keyword? comp) (when (keyword? comp)
(open-modal comp)))))) (open-modal comp))))))
;; SCREEN DID APPEAR ;; SCREEN DID APPEAR
(defonce screen-appear-reg (defonce screen-appear-reg
(.registerComponentDidAppearListener (.registerComponentDidAppearListener
(.events Navigation) (.events Navigation)
(fn [^js evn] (fn [^js evn]
(let [view-id (keyword (.-componentName evn))] (let [view-id (keyword (.-componentName evn))]
(log/debug "screen-appear-reg" view-id) (log/debug "screen-appear-reg" view-id)
(when (get views/screens view-id) (when (get views/screens view-id)
(when (and (not= view-id :bottom-sheet) (when (and (not= view-id :bottom-sheet)
(not= view-id :popover) (not= view-id :popover)
(not= view-id :visibility-status-popover)) (not= view-id :visibility-status-popover))
(set-view-id view-id) (set-view-id view-id)
(when-not @state/curr-modal (when-not @state/curr-modal
(reset! state/pushed-screen-id view-id)))))))) (reset! state/pushed-screen-id view-id))))))))
;; SCREEN DID DISAPPEAR ;; SCREEN DID DISAPPEAR
(defonce screen-disappear-reg (defonce screen-disappear-reg
(.registerComponentDidDisappearListener (.registerComponentDidDisappearListener
(.events Navigation) (.events Navigation)
(fn [^js evn] (fn [^js evn]
(let [view-id (keyword (.-componentName evn))] (let [view-id (keyword (.-componentName evn))]
(when-not (#{"popover" "bottom-sheet" "signing-sheet" "visibility-status-popover" "wallet-connect-sheet" "wallet-connect-success-sheet" "wallet-connect-app-management-sheet"} (when-not (#{"popover" "bottom-sheet" "signing-sheet" "visibility-status-popover"
(.-componentName evn)) "wallet-connect-sheet" "wallet-connect-success-sheet"
(re-frame/dispatch [::view-disappeared view-id]) "wallet-connect-app-management-sheet"}
(doseq [[_ {:keys [ref value]}] @quo.text-input/text-input-refs] (.-componentName evn))
(.setNativeProps ^js ref (clj->js {:text value}))) (re-frame/dispatch [::view-disappeared view-id])
(doseq [[^js text-input default-value] @react/text-input-refs] (doseq [[_ {:keys [ref value]}] @quo.text-input/text-input-refs]
(.setNativeProps text-input (clj->js {:text default-value})))))))) (.setNativeProps ^js ref (clj->js {:text value})))
(doseq [[^js text-input default-value] @react/text-input-refs]
(.setNativeProps text-input (clj->js {:text default-value}))))))))
;; SET ROOT ;; SET ROOT
(re-frame/reg-fx (re-frame/reg-fx
@@ -182,31 +191,34 @@
(reset! state/root-id @state/root-comp-id) (reset! state/root-id @state/root-comp-id)
(.setRoot Navigation (clj->js (get (roots/roots) new-root-id))))) (.setRoot Navigation (clj->js (get (roots/roots) new-root-id)))))
(fx/defn set-multiaccount-root (rf/defn set-multiaccount-root
{:events [::set-multiaccount-root]} {:events [::set-multiaccount-root]}
[{:keys [db]}] [{:keys [db]}]
(log/debug :set-multiaccounts-root) (log/debug :set-multiaccounts-root)
(let [key-uid (get-in db [:multiaccounts/login :key-uid]) (let [key-uid (get-in db [:multiaccounts/login :key-uid])
keycard-account? (boolean (get-in db [:multiaccounts/multiaccounts keycard-account? (boolean (get-in db
key-uid [:multiaccounts/multiaccounts
:keycard-pairing]))] key-uid
:keycard-pairing]))]
{:init-root-fx (if keycard-account? :multiaccounts-keycard :multiaccounts)})) {:init-root-fx (if keycard-account? :multiaccounts-keycard :multiaccounts)}))
(defonce rset-app-launched (defonce
(.registerAppLaunchedListener (.events Navigation) rset-app-launched
(fn [] (.registerAppLaunchedListener (.events Navigation)
(reset! state/curr-modal false) (fn []
(reset! state/dissmissing false) (reset! state/curr-modal false)
(if (or (= @state/root-id :multiaccounts) (reset! state/dissmissing false)
(= @state/root-id :multiaccounts-keycard)) (if (or (= @state/root-id :multiaccounts)
(re-frame/dispatch-sync [::set-multiaccount-root]) (= @state/root-id :multiaccounts-keycard))
(when @state/root-id (re-frame/dispatch-sync [::set-multiaccount-root])
(reset! state/root-comp-id @state/root-id) (when @state/root-id
(.setRoot Navigation (clj->js (get (roots/roots) @state/root-id))) (reset! state/root-comp-id @state/root-id)
(re-frame/dispatch [::login-core/check-last-chat]))) (.setRoot Navigation (clj->js (get (roots/roots) @state/root-id)))
(.hide ^js splash-screen)))) (re-frame/dispatch [::login-core/check-last-chat])))
(.hide ^js splash-screen))))
(defn get-screen-component [comp] (defn get-screen-component
[comp]
(log/debug :get-screen-component comp) (log/debug :get-screen-component comp)
(let [{:keys [options]} (get views/screens comp)] (let [{:keys [options]} (get views/screens comp)]
{:component {:id comp {:component {:id comp
@@ -227,24 +239,28 @@
(get-screen-component comp)))))) (get-screen-component comp))))))
;; BOTTOM TABS ;; BOTTOM TABS
(def tab-root-ids {0 :chat-stack (def tab-root-ids
1 :browser-stack {0 :chat-stack
2 :wallet-stack 1 :browser-stack
3 :status-stack 2 :wallet-stack
4 :profile-stack}) 3 :status-stack
4 :profile-stack})
(def tab-key-idx {:chat 0 (def tab-key-idx
:browser 1 {:chat 0
:wallet 2 :browser 1
:status 3 :wallet 2
:profile 4}) :status 3
:profile 4})
(re-frame/reg-fx (re-frame/reg-fx
:change-tab-fx :change-tab-fx
(fn [tab] (fn [tab]
(log/debug :change-tab-fx) (log/debug :change-tab-fx)
(reset! state/root-comp-id (get tab-root-ids (get tab-key-idx tab))) (reset! state/root-comp-id (get tab-root-ids (get tab-key-idx tab)))
(.mergeOptions Navigation "tabs-stack" (clj->js {:bottomTabs {:currentTabIndex (get tab-key-idx tab)}})) (.mergeOptions Navigation
"tabs-stack"
(clj->js {:bottomTabs {:currentTabIndex (get tab-key-idx tab)}}))
;;when we change tab we want to dismiss all modals ;;when we change tab we want to dismiss all modals
(dismiss-all-modals))) (dismiss-all-modals)))
@@ -253,21 +269,22 @@
:change-tab-count-fx :change-tab-count-fx
(fn [[tab cnt]] (fn [[tab cnt]]
(log/debug :change-tab-count-fx tab cnt) (log/debug :change-tab-count-fx tab cnt)
(.mergeOptions Navigation (.mergeOptions
(name (get tab-root-ids (get tab-key-idx tab))) Navigation
(clj->js {:bottomTab (cond (name (get tab-root-ids (get tab-key-idx tab)))
(or (pos? cnt) (pos? (:other cnt))) (clj->js {:bottomTab (cond
(if (and (= :chat tab) platform/ios?) (or (pos? cnt) (pos? (:other cnt)))
{:dotIndicator {:visible true}} (if (and (= :chat tab) platform/ios?)
{:badge (str (or (:other cnt) cnt)) :dotIndicator {:visible false}}) {:dotIndicator {:visible true}}
(pos? (:public cnt)) {:badge (str (or (:other cnt) cnt)) :dotIndicator {:visible false}})
(if platform/ios? (pos? (:public cnt))
{:dotIndicator {:visible true}} (if platform/ios?
{:badge nil :dotIndicator {:visible true}}) {:dotIndicator {:visible true}}
:else {:badge nil :dotIndicator {:visible true}})
(if (and (= :chat tab) platform/ios?) :else
{:dotIndicator {:visible false}} (if (and (= :chat tab) platform/ios?)
{:dotIndicator {:visible false} :badge ""}))})))) {:dotIndicator {:visible false}}
{:dotIndicator {:visible false} :badge ""}))}))))
(re-frame/reg-fx (re-frame/reg-fx
:pop-to-root-tab-fx :pop-to-root-tab-fx
@@ -277,54 +294,57 @@
(.popToRoot Navigation (name comp)))) (.popToRoot Navigation (name comp))))
(defonce register-bottom-tab-reg (defonce register-bottom-tab-reg
(.registerBottomTabSelectedListener (.registerBottomTabSelectedListener
(.events Navigation) (.events Navigation)
(fn [^js evn] (fn [^js evn]
(let [selected-tab-index (.-selectedTabIndex evn) (let [selected-tab-index (.-selectedTabIndex evn)
comp (get tab-root-ids selected-tab-index) comp (get tab-root-ids selected-tab-index)
tab-key (get (clojure.set/map-invert tab-key-idx) selected-tab-index)] tab-key (get (clojure.set/map-invert tab-key-idx) selected-tab-index)]
(re-frame/dispatch [:set :current-tab tab-key]) (re-frame/dispatch [:set :current-tab tab-key])
(when (= @state/root-comp-id comp) (when (= @state/root-comp-id comp)
(when (= :chat tab-key) (when (= :chat tab-key)
(re-frame/dispatch [:close-chat])) (re-frame/dispatch [:close-chat]))
(when platform/android? (when platform/android?
(.popToRoot Navigation (name comp)))) (.popToRoot Navigation (name comp))))
(reset! state/root-comp-id comp))))) (reset! state/root-comp-id comp)))))
;; OVERLAY (Popover and bottom sheets) ;; OVERLAY (Popover and bottom sheets)
(defn dissmiss-overlay [comp] (defn dissmiss-overlay
[comp]
(.catch (.dismissOverlay Navigation comp) #())) (.catch (.dismissOverlay Navigation comp) #()))
(defn show-overlay [comp] (defn show-overlay
[comp]
(dissmiss-overlay comp) (dissmiss-overlay comp)
(.showOverlay Navigation (.showOverlay
(clj->js Navigation
{:component {:name comp (clj->js
:id comp {:component {:name comp
:options (merge (cond-> (roots/status-bar-options) :id comp
(and platform/android? (not (quo.colors/dark?))) :options (merge (cond-> (roots/status-bar-options)
(assoc-in [:statusBar :backgroundColor] "#99999A")) (and platform/android? (not (quo.colors/dark?)))
{:layout {:componentBackgroundColor (if platform/android? (assoc-in [:statusBar :backgroundColor] "#99999A"))
(:backdrop @quo.colors/theme) {:layout {:componentBackgroundColor (if platform/android?
"transparent")} (:backdrop @quo.colors/theme)
:overlay {:interceptTouchOutside true}})}}))) "transparent")}
:overlay {:interceptTouchOutside true}})}})))
;; POPOVER ;; POPOVER
(defonce popover-reg (defonce popover-reg
(.registerComponent Navigation (.registerComponent Navigation
"popover" "popover"
(fn [] (gestureHandlerRootHOC views/popover-comp)) (fn [] (gestureHandlerRootHOC views/popover-comp))
(fn [] views/popover-comp))) (fn [] views/popover-comp)))
(re-frame/reg-fx :show-popover (fn [] (show-overlay "popover"))) (re-frame/reg-fx :show-popover (fn [] (show-overlay "popover")))
(re-frame/reg-fx :hide-popover (fn [] (dissmiss-overlay "popover"))) (re-frame/reg-fx :hide-popover (fn [] (dissmiss-overlay "popover")))
;; VISIBILITY STATUS POPOVER ;; VISIBILITY STATUS POPOVER
(defonce visibility-status-popover-reg (defonce visibility-status-popover-reg
(.registerComponent Navigation (.registerComponent Navigation
"visibility-status-popover" "visibility-status-popover"
(fn [] (gestureHandlerRootHOC views/visibility-status-popover-comp)) (fn [] (gestureHandlerRootHOC views/visibility-status-popover-comp))
(fn [] views/visibility-status-popover-comp))) (fn [] views/visibility-status-popover-comp)))
(re-frame/reg-fx :show-visibility-status-popover (re-frame/reg-fx :show-visibility-status-popover
(fn [] (show-overlay "visibility-status-popover"))) (fn [] (show-overlay "visibility-status-popover")))
@@ -333,10 +353,10 @@
;; BOTTOM SHEETS ;; BOTTOM SHEETS
(defonce bottom-sheet-reg (defonce bottom-sheet-reg
(.registerComponent Navigation (.registerComponent Navigation
"bottom-sheet" "bottom-sheet"
(fn [] (gestureHandlerRootHOC views/sheet-comp)) (fn [] (gestureHandlerRootHOC views/sheet-comp))
(fn [] views/sheet-comp))) (fn [] views/sheet-comp)))
(re-frame/reg-fx :show-bottom-sheet (fn [] (show-overlay "bottom-sheet"))) (re-frame/reg-fx :show-bottom-sheet (fn [] (show-overlay "bottom-sheet")))
(re-frame/reg-fx :hide-bottom-sheet (fn [] (dissmiss-overlay "bottom-sheet"))) (re-frame/reg-fx :hide-bottom-sheet (fn [] (dissmiss-overlay "bottom-sheet")))
@@ -344,37 +364,41 @@
;; WALLET CONNECT ;; WALLET CONNECT
(defonce wallet-connect-sheet-reg (defonce wallet-connect-sheet-reg
(.registerComponent Navigation (.registerComponent Navigation
"wallet-connect-sheet" "wallet-connect-sheet"
(fn [] (gestureHandlerRootHOC views/wallet-connect-comp)) (fn [] (gestureHandlerRootHOC views/wallet-connect-comp))
(fn [] views/wallet-connect-comp))) (fn [] views/wallet-connect-comp)))
(defonce wallet-connect-success-sheet-reg (defonce wallet-connect-success-sheet-reg
(.registerComponent Navigation (.registerComponent Navigation
"wallet-connect-success-sheet" "wallet-connect-success-sheet"
(fn [] (gestureHandlerRootHOC views/wallet-connect-success-comp)) (fn [] (gestureHandlerRootHOC views/wallet-connect-success-comp))
(fn [] views/wallet-connect-success-comp))) (fn [] views/wallet-connect-success-comp)))
(defonce wallet-connect-app-management-sheet-reg (defonce wallet-connect-app-management-sheet-reg
(.registerComponent Navigation (.registerComponent Navigation
"wallet-connect-app-management-sheet" "wallet-connect-app-management-sheet"
(fn [] (gestureHandlerRootHOC views/wallet-connect-app-management-comp)) (fn [] (gestureHandlerRootHOC views/wallet-connect-app-management-comp))
(fn [] views/wallet-connect-app-management-comp))) (fn [] views/wallet-connect-app-management-comp)))
(re-frame/reg-fx :show-wallet-connect-sheet (fn [] (show-overlay "wallet-connect-sheet"))) (re-frame/reg-fx :show-wallet-connect-sheet (fn [] (show-overlay "wallet-connect-sheet")))
(re-frame/reg-fx :hide-wallet-connect-sheet (fn [] (dissmiss-overlay "wallet-connect-sheet"))) (re-frame/reg-fx :hide-wallet-connect-sheet (fn [] (dissmiss-overlay "wallet-connect-sheet")))
(re-frame/reg-fx :show-wallet-connect-success-sheet (fn [] (show-overlay "wallet-connect-success-sheet"))) (re-frame/reg-fx :show-wallet-connect-success-sheet
(re-frame/reg-fx :hide-wallet-connect-success-sheet (fn [] (dissmiss-overlay "wallet-connect-success-sheet"))) (fn [] (show-overlay "wallet-connect-success-sheet")))
(re-frame/reg-fx :show-wallet-connect-app-management-sheet (fn [] (show-overlay "wallet-connect-app-management-sheet"))) (re-frame/reg-fx :hide-wallet-connect-success-sheet
(re-frame/reg-fx :hide-wallet-connect-app-management-sheet (fn [] (dissmiss-overlay "wallet-connect-app-management-sheet"))) (fn [] (dissmiss-overlay "wallet-connect-success-sheet")))
(re-frame/reg-fx :show-wallet-connect-app-management-sheet
(fn [] (show-overlay "wallet-connect-app-management-sheet")))
(re-frame/reg-fx :hide-wallet-connect-app-management-sheet
(fn [] (dissmiss-overlay "wallet-connect-app-management-sheet")))
;; SIGNING ;; SIGNING
(defonce signing-sheet-reg (defonce signing-sheet-reg
(.registerComponent Navigation (.registerComponent Navigation
"signing-sheet" "signing-sheet"
(fn [] (gestureHandlerRootHOC views/signing-comp)) (fn [] (gestureHandlerRootHOC views/signing-comp))
(fn [] views/signing-comp))) (fn [] views/signing-comp)))
(re-frame/reg-fx :show-signing-sheet (fn [] (show-overlay "signing-sheet"))) (re-frame/reg-fx :show-signing-sheet (fn [] (show-overlay "signing-sheet")))
(re-frame/reg-fx :hide-signing-sheet (fn [] (dissmiss-overlay "signing-sheet"))) (re-frame/reg-fx :hide-signing-sheet (fn [] (dissmiss-overlay "signing-sheet")))
@@ -383,10 +407,10 @@
;; TODO why is this not a regular bottom sheet ? ;; TODO why is this not a regular bottom sheet ?
(defonce select-acc-sheet-reg (defonce select-acc-sheet-reg
(.registerComponent Navigation (.registerComponent Navigation
"select-acc-sheet" "select-acc-sheet"
(fn [] (gestureHandlerRootHOC views/select-acc-comp)) (fn [] (gestureHandlerRootHOC views/select-acc-comp))
(fn [] views/select-acc-comp))) (fn [] views/select-acc-comp)))
(re-frame/reg-fx :show-select-acc-sheet (fn [] (show-overlay "select-acc-sheet"))) (re-frame/reg-fx :show-select-acc-sheet (fn [] (show-overlay "select-acc-sheet")))
(re-frame/reg-fx :hide-select-acc-sheet (fn [] (dissmiss-overlay "select-acc-sheet"))) (re-frame/reg-fx :hide-select-acc-sheet (fn [] (dissmiss-overlay "select-acc-sheet")))
@@ -414,28 +438,31 @@
(.pop Navigation (name @state/root-comp-id)) (.pop Navigation (name @state/root-comp-id))
(navigate view-id))) (navigate view-id)))
(def community-screens '(:community-management (def community-screens
:community-members '(:community-management
:community-requests-to-join :community-members
:create-community-channel :community-requests-to-join
:community-emoji-thumbnail-picker :create-community-channel
:create-community-category :community-emoji-thumbnail-picker
:community-edit-chats :create-community-category
:community-edit :community-edit-chats
:community-reorder-categories)) :community-edit
:community-reorder-categories))
;; change view-id if it is still same after component is disappeared ;; change view-id if it is still same after component is disappeared
;; https://github.com/wix/react-native-navigation/issues/5744#issuecomment-563226820 ;; https://github.com/wix/react-native-navigation/issues/5744#issuecomment-563226820
(fx/defn view-disappeared (rf/defn view-disappeared
{:events [::view-disappeared]} {:events [::view-disappeared]}
[{:keys [db]} view-id] [{:keys [db]} view-id]
(when (= view-id (:view-id db)) (when (= view-id (:view-id db))
{:db (assoc db :view-id (cond {:db (assoc db
(= view-id :community-emoji-thumbnail-picker) :view-id
:create-community-channel (cond
(= view-id :community-emoji-thumbnail-picker)
:create-community-channel
(some #(= view-id %) community-screens) (some #(= view-id %) community-screens)
:community :community
:else :else
:home))})) :home))}))
+68 -56
View File
@@ -5,37 +5,42 @@
[status-im.ethereum.json-rpc :as json-rpc] [status-im.ethereum.json-rpc :as json-rpc]
[status-im.i18n.i18n :as i18n] [status-im.i18n.i18n :as i18n]
[status-im.node.core :as node] [status-im.node.core :as node]
[status-im2.navigation.events :as navigation]
[status-im.utils.fx :as fx]
[status-im.utils.http :as http] [status-im.utils.http :as http]
[status-im.utils.types :as types])) [status-im.utils.types :as types]
[status-im2.navigation.events :as navigation]
[utils.re-frame :as rf]))
(def url-regex (def url-regex
#"https?://(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}(\.[a-z]{2,6})?\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)") #"https?://(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}(\.[a-z]{2,6})?\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)")
(defn valid-rpc-url? [url] (defn valid-rpc-url?
[url]
(boolean (re-matches url-regex (str url)))) (boolean (re-matches url-regex (str url))))
(def default-manage (def default-manage
{:name {:value ""} {:name {:value ""}
:url {:value ""} :url {:value ""}
:symbol {:value ""} :symbol {:value ""}
:chain {:value :mainnet}}) :chain {:value :mainnet}})
(defn validate-string [{:keys [value]}] (defn validate-string
[{:keys [value]}]
{:value value {:value value
:error (string/blank? value)}) :error (string/blank? value)})
(defn validate-network-id [{:keys [value]}] (defn validate-network-id
[{:keys [value]}]
{:value value {:value value
:error (and (not (string/blank? value)) :error (and (not (string/blank? value))
(= (int value) 0))}) (= (int value) 0))})
(defn validate-url [{:keys [value]}] (defn validate-url
[{:keys [value]}]
{:value value {:value value
:error (not (valid-rpc-url? value))}) :error (not (valid-rpc-url? value))})
(defn validate-manage [manage] (defn validate-manage
[manage]
(-> manage (-> manage
(update :url validate-url) (update :url validate-url)
(update :name validate-string) (update :name validate-string)
@@ -43,20 +48,23 @@
(update :chain validate-string) (update :chain validate-string)
(update :network-id validate-network-id))) (update :network-id validate-network-id)))
(defn valid-manage? [manage] (defn valid-manage?
[manage]
(->> (validate-manage manage) (->> (validate-manage manage)
vals vals
(map :error) (map :error)
(not-any? identity))) (not-any? identity)))
(defn chain-id-available? [current-networks network] (defn chain-id-available?
[current-networks network]
(let [chain-id (get-in network [:config :NetworkId])] (let [chain-id (get-in network [:config :NetworkId])]
(every? #(not= chain-id (get-in % [1 :config :NetworkId])) current-networks))) (every? #(not= chain-id (get-in % [1 :config :NetworkId])) current-networks)))
(defn get-network [{:keys [db]} network-id] (defn get-network
[{:keys [db]} network-id]
(get-in db [:networks/networks network-id])) (get-in db [:networks/networks network-id]))
(fx/defn set-input (rf/defn set-input
{:events [::input-changed]} {:events [::input-changed]}
[{:keys [db]} input-key value] [{:keys [db]} input-key value]
{:db (-> db {:db (-> db
@@ -64,13 +72,13 @@
(update-in [:networks/manage] validate-manage))}) (update-in [:networks/manage] validate-manage))})
;; No edit functionality actually implemented ;; No edit functionality actually implemented
(fx/defn edit (rf/defn edit
{:events [::add-network-pressed]} {:events [::add-network-pressed]}
[{db :db}] [{db :db}]
{:db (assoc db :networks/manage (validate-manage default-manage)) {:db (assoc db :networks/manage (validate-manage default-manage))
:dispatch [:navigate-to :edit-network]}) :dispatch [:navigate-to :edit-network]})
(fx/defn connect-success (rf/defn connect-success
{:events [::connect-success]} {:events [::connect-success]}
[_ network-id] [_ network-id]
{:ui/show-confirmation {:ui/show-confirmation
@@ -80,14 +88,14 @@
:on-accept #(re-frame/dispatch [::save-network-settings-pressed network-id]) :on-accept #(re-frame/dispatch [::save-network-settings-pressed network-id])
:on-cancel nil}}) :on-cancel nil}})
(fx/defn connect-failure (rf/defn connect-failure
{:events [::connect-failure]} {:events [::connect-failure]}
[_ reason] [_ reason]
{:utils/show-popup {:utils/show-popup
{:title (i18n/label :t/error) {:title (i18n/label :t/error)
:content (str reason)}}) :content (str reason)}})
(fx/defn connect (rf/defn connect
{:events [::connect-network-pressed]} {:events [::connect-network-pressed]}
[{:keys [db] :as cofx} network-id] [{:keys [db] :as cofx} network-id]
(if-let [config (get-in db [:networks/networks network-id :config])] (if-let [config (get-in db [:networks/networks network-id :config])]
@@ -104,18 +112,20 @@
(js/parseInt res))] (js/parseInt res))]
(if (and network-id (= expected-network-id rpc-network-id)) (if (and network-id (= expected-network-id rpc-network-id))
(re-frame/dispatch [::connect-success network-id]) (re-frame/dispatch [::connect-success network-id])
(re-frame/dispatch [::connect-failure (if (not= expected-network-id rpc-network-id) (re-frame/dispatch [::connect-failure
(i18n/label :t/network-invalid-network-id) (if (not= expected-network-id rpc-network-id)
(i18n/label :t/network-invalid-url))])))) (i18n/label :t/network-invalid-network-id)
(i18n/label :t/network-invalid-url))]))))
:on-error (fn [{:keys [response-body status-code]}] :on-error (fn [{:keys [response-body status-code]}]
(let [reason (if status-code (let [reason (if status-code
(i18n/label :t/network-invalid-status-code {:code status-code}) (i18n/label :t/network-invalid-status-code
{:code status-code})
(str response-body))] (str response-body))]
(re-frame/dispatch [::connect-failure reason])))}} (re-frame/dispatch [::connect-failure reason])))}}
(connect-success cofx network-id)) (connect-success cofx network-id))
(connect-failure cofx "A network with the specified id doesn't exist"))) (connect-failure cofx "A network with the specified id doesn't exist")))
(fx/defn delete (rf/defn delete
{:events [::delete-network-pressed]} {:events [::delete-network-pressed]}
[{:keys [db]} network] [{:keys [db]} network]
(let [current-network? (= (:networks/current-network db) network)] (let [current-network? (= (:networks/current-network db) network)]
@@ -125,26 +135,27 @@
{:ui/show-confirmation {:title (i18n/label :t/delete-network-title) {:ui/show-confirmation {:title (i18n/label :t/delete-network-title)
:content (i18n/label :t/delete-network-confirmation) :content (i18n/label :t/delete-network-confirmation)
:confirm-button-text (i18n/label :t/delete) :confirm-button-text (i18n/label :t/delete)
:on-accept #(re-frame/dispatch [::remove-network-confirmed network]) :on-accept #(re-frame/dispatch [::remove-network-confirmed
network])
:on-cancel nil}}))) :on-cancel nil}})))
(fx/defn save-network-settings (rf/defn save-network-settings
{:events [::save-network-settings-pressed]} {:events [::save-network-settings-pressed]}
[{:keys [db] :as cofx} network] [{:keys [db] :as cofx} network]
(fx/merge cofx (rf/merge cofx
{:db (assoc db :networks/current-network network) {:db (assoc db :networks/current-network network)
::json-rpc/call [{:method "settings_saveSetting" ::json-rpc/call [{:method "settings_saveSetting"
:params [:networks/current-network network] :params [:networks/current-network network]
:on-success #()}]} :on-success #()}]}
(node/prepare-new-config {:on-success #(re-frame/dispatch [:logout])}))) (node/prepare-new-config {:on-success #(re-frame/dispatch [:logout])})))
(fx/defn remove-network (rf/defn remove-network
{:events [::remove-network-confirmed]} {:events [::remove-network-confirmed]}
[{:keys [db] :as cofx} network] [{:keys [db] :as cofx} network]
(let [networks (dissoc (:networks/networks db) network)] (let [networks (dissoc (:networks/networks db) network)]
{:db (assoc db :networks/networks networks) {:db (assoc db :networks/networks networks)
::json-rpc/call [{:method "settings_saveSetting" ::json-rpc/call [{:method "settings_saveSetting"
:params [:networks/networks (vals networks)] :params [:networks/networks (vals networks)]
:on-success #(re-frame/dispatch [:navigate-back])}]})) :on-success #(re-frame/dispatch [:navigate-back])}]}))
(defn new-network (defn new-network
@@ -155,40 +166,41 @@
:DataDir data-dir :DataDir data-dir
:UpstreamConfig {:Enabled true :UpstreamConfig {:Enabled true
:URL upstream-url}}] :URL upstream-url}}]
{:id random-id {:id random-id
:name network-name :name network-name
:symbol symbol :symbol symbol
:config config})) :config config}))
(fx/defn save (rf/defn save
{:events [::save-network-pressed] {:events [::save-network-pressed]
:interceptors [(re-frame/inject-cofx :random-id-generator)]} :interceptors [(re-frame/inject-cofx :random-id-generator)]}
[{{:networks/keys [manage networks] :as db} :db [{{:networks/keys [manage networks] :as db} :db
random-id-generator :random-id-generator :as cofx}] random-id-generator :random-id-generator
:as cofx}]
(if (valid-manage? manage) (if (valid-manage? manage)
;; rename network-id from UI to chain-id ;; rename network-id from UI to chain-id
(let [{:keys [name url chain network-id symbol]} manage (let [{:keys [name url chain network-id symbol]} manage
random-id (string/replace (random-id-generator) "-" "") random-id (string/replace (random-id-generator) "-" "")
network (new-network random-id network (new-network random-id
(:value name) (:value name)
(:value symbol) (:value symbol)
(:value url) (:value url)
(:value chain) (:value chain)
(:value network-id)) (:value network-id))
custom-chain-type? (= :custom (:value chain)) custom-chain-type? (= :custom (:value chain))
new-networks (assoc networks random-id network)] new-networks (assoc networks random-id network)]
(if (or (not custom-chain-type?) (if (or (not custom-chain-type?)
(chain-id-available? networks network)) (chain-id-available? networks network))
{:db (-> db {:db (-> db
(dissoc :networks/manage) (dissoc :networks/manage)
(assoc :networks/networks new-networks)) (assoc :networks/networks new-networks))
::json-rpc/call [{:method "settings_saveSetting" ::json-rpc/call [{:method "settings_saveSetting"
:params [:networks/networks (vals new-networks)] :params [:networks/networks (vals new-networks)]
:on-success #(re-frame/dispatch [:navigate-back])}]} :on-success #(re-frame/dispatch [:navigate-back])}]}
{:ui/show-error "chain-id already defined"})) {:ui/show-error "chain-id already defined"}))
{:ui/show-error "invalid network parameters"})) {:ui/show-error "invalid network parameters"}))
(fx/defn open-network-details (rf/defn open-network-details
{:events [::network-entry-pressed]} {:events [::network-entry-pressed]}
[cofx network] [cofx network]
(navigation/navigate-to-cofx cofx :network-details {:networks/selected-network network})) (navigation/navigate-to-cofx cofx :network-details {:networks/selected-network network}))
+18 -17
View File
@@ -1,15 +1,15 @@
(ns status-im.network.net-info (ns status-im.network.net-info
(:require [re-frame.core :as re-frame] (:require ["@react-native-community/netinfo" :default net-info]
[status-im.native-module.core :as status] [re-frame.core :as re-frame]
[status-im.mobile-sync-settings.core :as mobile-network] [status-im.mobile-sync-settings.core :as mobile-network]
[status-im.utils.fx :as fx] [status-im.native-module.core :as status]
[status-im.wallet.core :as wallet] [status-im.wallet.core :as wallet]
["@react-native-community/netinfo" :default net-info] [taoensso.timbre :as log]
[taoensso.timbre :as log])) [utils.re-frame :as rf]))
(fx/defn change-network-status (rf/defn change-network-status
[{:keys [db] :as cofx} is-connected?] [{:keys [db] :as cofx} is-connected?]
(fx/merge cofx (rf/merge cofx
{:db (assoc db :network-status (if is-connected? :online :offline))} {:db (assoc db :network-status (if is-connected? :online :offline))}
(when (and is-connected? (when (and is-connected?
(or (not= (count (get-in db [:wallet :accounts])) (or (not= (count (get-in db [:wallet :accounts]))
@@ -17,14 +17,14 @@
(wallet/has-empty-balances? db))) (wallet/has-empty-balances? db)))
(wallet/update-balances nil nil)))) (wallet/update-balances nil nil))))
(fx/defn change-network-type (rf/defn change-network-type
[{:keys [db] :as cofx} old-network-type network-type expensive?] [{:keys [db] :as cofx} old-network-type network-type expensive?]
(fx/merge cofx (rf/merge cofx
{:db (assoc db :network/type network-type) {:db (assoc db :network/type network-type)
:network/notify-status-go [network-type expensive?]} :network/notify-status-go [network-type expensive?]}
(mobile-network/on-network-status-change))) (mobile-network/on-network-status-change)))
(fx/defn handle-network-info-change (rf/defn handle-network-info-change
{:events [::network-info-changed]} {:events [::network-info-changed]}
[{:keys [db] :as cofx} {:keys [isConnected type details] :as state}] [{:keys [db] :as cofx} {:keys [isConnected type details] :as state}]
(let [old-network-status (:network-status db) (let [old-network-status (:network-status db)
@@ -33,18 +33,19 @@
status-changed? (= connectivity-status old-network-status) status-changed? (= connectivity-status old-network-status)
type-changed? (= type old-network-type)] type-changed? (= type old-network-type)]
(log/debug "[net-info]" (log/debug "[net-info]"
"old-network-status" old-network-status "old-network-status" old-network-status
"old-network-type" old-network-type "old-network-type" old-network-type
"connectivity-status" connectivity-status "connectivity-status" connectivity-status
"type" type "type" type
"details" details) "details" details)
(fx/merge cofx (rf/merge cofx
(when-not status-changed? (when-not status-changed?
(change-network-status isConnected)) (change-network-status isConnected))
(when-not type-changed? (when-not type-changed?
(change-network-type old-network-type type (:is-connection-expensive details)))))) (change-network-type old-network-type type (:is-connection-expensive details))))))
(defn add-net-info-listener [] (defn add-net-info-listener
[]
(when net-info (when net-info
(.addEventListener ^js net-info (.addEventListener ^js net-info
#(re-frame/dispatch [::network-info-changed #(re-frame/dispatch [::network-info-changed
+59 -49
View File
@@ -3,11 +3,12 @@
[status-im.ethereum.json-rpc :as json-rpc] [status-im.ethereum.json-rpc :as json-rpc]
[status-im.native-module.core :as status] [status-im.native-module.core :as status]
[status-im.utils.config :as config] [status-im.utils.config :as config]
[status-im.utils.fx :as fx]
[status-im.utils.platform :as utils.platform] [status-im.utils.platform :as utils.platform]
[status-im.utils.types :as types])) [status-im.utils.types :as types]
[utils.re-frame :as rf]))
(defn- add-custom-bootnodes [config network all-bootnodes] (defn- add-custom-bootnodes
[config network all-bootnodes]
(let [bootnodes (as-> all-bootnodes $ (let [bootnodes (as-> all-bootnodes $
(get $ network) (get $ network)
(vals $) (vals $)
@@ -16,18 +17,19 @@
(assoc-in config [:ClusterConfig :BootNodes] bootnodes) (assoc-in config [:ClusterConfig :BootNodes] bootnodes)
config))) config)))
(defn- add-log-level [config log-level] (defn- add-log-level
[config log-level]
(if (empty? log-level) (if (empty? log-level)
(assoc config (assoc config
:MaxPeers 20 :MaxPeers 20
:MaxPendingPeers 20 :MaxPendingPeers 20
:LogLevel "ERROR" :LogLevel "ERROR"
:LogEnabled false) :LogEnabled false)
(assoc config (assoc config
:MaxPeers 20 :MaxPeers 20
:MaxPendingPeers 20 :MaxPendingPeers 20
:LogLevel log-level :LogLevel log-level
:LogEnabled true))) :LogEnabled true)))
(defn get-network-genesis-hash-prefix (defn get-network-genesis-hash-prefix
"returns the hex representation of the first 8 bytes of "returns the hex representation of the first 8 bytes of
@@ -50,22 +52,25 @@
(defn get-topics (defn get-topics
[network] [network]
(let [les-topic (get-les-topic network)] (let [les-topic (get-les-topic network)]
(cond-> {"whisper" {:Min 2, :Max 2}} (cond-> {"whisper" {:Min 2 :Max 2}}
les-topic (assoc les-topic {:Min 2, :Max 2})))) les-topic (assoc les-topic {:Min 2 :Max 2}))))
(defn- get-base-node-config [config] (defn- get-base-node-config
[config]
(cond-> (assoc config (cond-> (assoc config
:Name "StatusIM") :Name
"StatusIM")
config/dev-build? config/dev-build?
(assoc :ListenAddr ":30304" (assoc :ListenAddr ":30304"
:DataDir (str (:DataDir config) "_dev")))) :DataDir (str (:DataDir config) "_dev"))))
(def login-node-config (def login-node-config
{:WalletConfig (cond-> {:Enabled true} {:WalletConfig (cond-> {:Enabled true}
(not= config/opensea-api-key "") (not= config/opensea-api-key "")
(assoc :OpenseaAPIKey config/opensea-api-key))}) (assoc :OpenseaAPIKey config/opensea-api-key))})
(defn- get-login-node-config [config] (defn- get-login-node-config
[config]
(merge config login-node-config)) (merge config login-node-config))
(defn- pick-nodes (defn- pick-nodes
@@ -75,14 +80,17 @@
[limit nodes] [limit nodes]
(take limit (shuffle nodes))) (take limit (shuffle nodes)))
(defn fleets [{:keys [custom-fleets]}] (defn fleets
[{:keys [custom-fleets]}]
(as-> [(js/require "./fleets.js")] $ (as-> [(js/require "./fleets.js")] $
(mapv #(:fleets (types/json->clj %)) $) (mapv #(:fleets (types/json->clj %)) $)
(conj $ custom-fleets) (conj $ custom-fleets)
(reduce merge $))) (reduce merge $)))
(defn current-fleet-key [db] (defn current-fleet-key
(keyword (get-in db [:multiaccount :fleet] [db]
(keyword (get-in db
[:multiaccount :fleet]
config/fleet))) config/fleet)))
(defn get-current-fleet (defn get-current-fleet
@@ -93,15 +101,16 @@
(defn- get-multiaccount-node-config (defn- get-multiaccount-node-config
[{:keys [multiaccount :networks/networks :networks/current-network] [{:keys [multiaccount :networks/networks :networks/current-network]
:as db}] :as db}]
(let [wakuv2-config (get multiaccount :wakuv2-config {}) (let [wakuv2-config (get multiaccount :wakuv2-config {})
current-fleet-key (current-fleet-key db) current-fleet-key (current-fleet-key db)
current-fleet (get-current-fleet db) current-fleet (get-current-fleet db)
wakuv2-enabled (boolean (:waku current-fleet)) wakuv2-enabled (boolean (:waku current-fleet))
waku-nodes (:waku-nodes current-fleet) waku-nodes (:waku-nodes current-fleet)
rendezvous-nodes (pick-nodes 3 (vals (:rendezvous current-fleet))) rendezvous-nodes (pick-nodes 3 (vals (:rendezvous current-fleet)))
{:keys [installation-id log-level {:keys [installation-id log-level
waku-bloom-filter-mode waku-bloom-filter-mode
custom-bootnodes custom-bootnodes-enabled?]} multiaccount custom-bootnodes custom-bootnodes-enabled?]}
multiaccount
use-custom-bootnodes (get custom-bootnodes-enabled? current-network)] use-custom-bootnodes (get custom-bootnodes-enabled? current-network)]
(cond-> (get-in networks [current-network :config]) (cond-> (get-in networks [current-network :config])
:always :always
@@ -111,10 +120,10 @@
(get-login-node-config) (get-login-node-config)
current-fleet current-fleet
(assoc :NoDiscovery wakuv2-enabled (assoc :NoDiscovery wakuv2-enabled
:Rendezvous (if wakuv2-enabled false (boolean (seq rendezvous-nodes))) :Rendezvous (if wakuv2-enabled false (boolean (seq rendezvous-nodes)))
:ClusterConfig {:Enabled true :ClusterConfig {:Enabled true
:Fleet (name current-fleet-key) :Fleet (name current-fleet-key)
:DiscV5BootstrapNodes :DiscV5BootstrapNodes
(if wakuv2-enabled (if wakuv2-enabled
waku-nodes waku-nodes
@@ -124,29 +133,30 @@
:TrustedMailServers :TrustedMailServers
(if wakuv2-enabled [] (pick-nodes 6 (vals (:mail current-fleet)))) (if wakuv2-enabled [] (pick-nodes 6 (vals (:mail current-fleet))))
:StaticNodes :StaticNodes
(if wakuv2-enabled [] (if wakuv2-enabled
(into (pick-nodes 2 []
(vals (:whisper current-fleet))) (into (pick-nodes 2
(vals (:static current-fleet)))) (vals (:whisper current-fleet)))
:RendezvousNodes (if wakuv2-enabled [] rendezvous-nodes) (vals (:static current-fleet))))
:WakuNodes (or waku-nodes [])}) :RendezvousNodes (if wakuv2-enabled [] rendezvous-nodes)
:WakuNodes (or waku-nodes [])})
:always :always
(assoc :LocalNotificationsConfig {:Enabled true} (assoc :LocalNotificationsConfig {:Enabled true}
:BrowsersConfig {:Enabled true} :BrowsersConfig {:Enabled true}
:PermissionsConfig {:Enabled true} :PermissionsConfig {:Enabled true}
:MailserversConfig {:Enabled true} :MailserversConfig {:Enabled true}
:EnableNTPSync true :EnableNTPSync true
:WakuConfig :WakuConfig
{:Enabled true {:Enabled true
:BloomFilterMode waku-bloom-filter-mode :BloomFilterMode waku-bloom-filter-mode
:LightClient true :LightClient true
:MinimumPoW 0.000001} :MinimumPoW 0.000001}
:WakuV2Config (assoc wakuv2-config :WakuV2Config (assoc wakuv2-config
:DiscoveryLimit 20 :DiscoveryLimit 20
:EnableDiscV5 true :EnableDiscV5 true
:Enabled wakuv2-enabled :Enabled wakuv2-enabled
:Host "0.0.0.0") :Host "0.0.0.0")
:ShhextConfig :ShhextConfig
{:BackupDisabledDataDir (utils.platform/no-backup-directory) {:BackupDisabledDataDir (utils.platform/no-backup-directory)
:InstallationID installation-id :InstallationID installation-id
@@ -158,8 +168,8 @@
:VerifyTransactionChainID config/verify-transaction-chain-id :VerifyTransactionChainID config/verify-transaction-chain-id
:DataSyncEnabled true :DataSyncEnabled true
:PFSEnabled true} :PFSEnabled true}
:RequireTopics (get-topics current-network) :RequireTopics (get-topics current-network)
:StatusAccountsConfig {:Enabled true}) :StatusAccountsConfig {:Enabled true})
(and (and
config/bootnodes-settings-enabled? config/bootnodes-settings-enabled?
@@ -173,7 +183,7 @@
[db] [db]
(types/clj->json (get-multiaccount-node-config db))) (types/clj->json (get-multiaccount-node-config db)))
(fx/defn save-new-config (rf/defn save-new-config
"Saves a new status-go config for the current account "Saves a new status-go config for the current account
This RPC method is the only way to change the node config of an account. This RPC method is the only way to change the node config of an account.
NOTE: it is better used indirectly through `prepare-new-config`, NOTE: it is better used indirectly through `prepare-new-config`,
@@ -185,7 +195,7 @@ app-db"
:params [:node-config config] :params [:node-config config]
:on-success on-success}]}) :on-success on-success}]})
(fx/defn prepare-new-config (rf/defn prepare-new-config
"Use this function to apply settings to the current account node config" "Use this function to apply settings to the current account node config"
[{:keys [db]} {:keys [on-success]}] [{:keys [db]} {:keys [on-success]}]
(let [key-uid (get-in db [:multiaccount :key-uid])] (let [key-uid (get-in db [:multiaccount :key-uid])]
+105 -70
View File
@@ -1,14 +1,14 @@
(ns status-im.notifications.core (ns status-im.notifications.core
(:require [re-frame.core :as re-frame] (:require ["@react-native-community/push-notification-ios" :default pn-ios]
[taoensso.timbre :as log] [quo.platform :as platform]
[status-im.utils.fx :as fx] [re-frame.core :as re-frame]
[status-im.ethereum.json-rpc :as json-rpc]
[status-im.multiaccounts.update.core :as multiaccounts.update] [status-im.multiaccounts.update.core :as multiaccounts.update]
["@react-native-community/push-notification-ios" :default pn-ios]
[status-im.notifications.android :as pn-android] [status-im.notifications.android :as pn-android]
[status-im.notifications.local :as local] [status-im.notifications.local :as local]
[quo.platform :as platform]
[status-im.utils.config :as config] [status-im.utils.config :as config]
[status-im.ethereum.json-rpc :as json-rpc])) [taoensso.timbre :as log]
[utils.re-frame :as rf]))
(def server-type-default 1) (def server-type-default 1)
(def server-type-custom 2) (def server-type-custom 2)
@@ -16,12 +16,14 @@
(def apn-token-type 1) (def apn-token-type 1)
(def firebase-token-type 2) (def firebase-token-type 2)
(def listeners-added? (atom nil)) (def listeners-added? (atom nil))
(defn server<-rpc [{:keys [type publicKey registered]}] (defn server<-rpc
[{:keys [type publicKey registered]}]
{:public-key publicKey {:public-key publicKey
:type type :type type
:registered registered}) :registered registered})
(defn add-event-listeners [] (defn add-event-listeners
[]
(when-not @listeners-added? (when-not @listeners-added?
(reset! listeners-added? true) (reset! listeners-added? true)
(.addEventListener (.addEventListener
@@ -35,23 +37,27 @@
(fn [error] (fn [error]
(re-frame/dispatch [:notifications/switch-error true error]))))) (re-frame/dispatch [:notifications/switch-error true error])))))
(defn enable-ios-notifications [] (defn enable-ios-notifications
[]
(add-event-listeners) (add-event-listeners)
(-> (.requestPermissions ^js pn-ios) (-> (.requestPermissions ^js pn-ios)
(.then #()) (.then #())
(.catch #()))) (.catch #())))
(defn disable-ios-notifications [] (defn disable-ios-notifications
[]
(.abandonPermissions ^js pn-ios) (.abandonPermissions ^js pn-ios)
(re-frame/dispatch [:notifications/unregistered-from-push-notifications])) (re-frame/dispatch [:notifications/unregistered-from-push-notifications]))
(defn enable-android-notifications [] (defn enable-android-notifications
[]
(pn-android/create-channel (pn-android/create-channel
{:channel-id "status-im-notifications" {:channel-id "status-im-notifications"
:channel-name "Status push notifications"}) :channel-name "Status push notifications"})
(pn-android/enable-notifications)) (pn-android/enable-notifications))
(defn disable-android-notifications [] (defn disable-android-notifications
[]
(pn-android/disable-notifications)) (pn-android/disable-notifications))
;; FIXME: Repalce with request permission from audio messages PR lib ;; FIXME: Repalce with request permission from audio messages PR lib
@@ -59,7 +65,7 @@
::request-permission ::request-permission
identity) identity)
(fx/defn request-permission (rf/defn request-permission
{:events [::request-permission]} {:events [::request-permission]}
[_] [_]
{::request-permission true}) {::request-permission true})
@@ -103,15 +109,16 @@
(doseq [chat-id chat-ids] (doseq [chat-id chat-ids]
(pn-android/clear-message-notifications chat-id)))))) (pn-android/clear-message-notifications chat-id))))))
(fx/defn handle-enable-notifications-event (rf/defn handle-enable-notifications-event
{:events [:notifications/registered-for-push-notifications]} {:events [:notifications/registered-for-push-notifications]}
[cofx token] [cofx token]
{::json-rpc/call [{:method "wakuext_registerForPushNotifications" {::json-rpc/call [{:method "wakuext_registerForPushNotifications"
:params [token (if platform/ios? config/apn-topic) (if platform/ios? apn-token-type firebase-token-type)] :params [token (if platform/ios? config/apn-topic)
(if platform/ios? apn-token-type firebase-token-type)]
:on-success #(log/info "[push-notifications] register-success" %) :on-success #(log/info "[push-notifications] register-success" %)
:on-error #(re-frame/dispatch [:notifications/switch-error true %])}]}) :on-error #(re-frame/dispatch [:notifications/switch-error true %])}]})
(fx/defn handle-disable-notifications-event (rf/defn handle-disable-notifications-event
{:events [:notifications/unregistered-from-push-notifications]} {:events [:notifications/unregistered-from-push-notifications]}
[cofx] [cofx]
{::json-rpc/call [{:method "wakuext_unregisterFromPushNotifications" {::json-rpc/call [{:method "wakuext_unregisterFromPushNotifications"
@@ -119,7 +126,7 @@
:on-success #(log/info "[push-notifications] unregister-success" %) :on-success #(log/info "[push-notifications] unregister-success" %)
:on-error #(re-frame/dispatch [:notifications/switch-error false %])}]}) :on-error #(re-frame/dispatch [:notifications/switch-error false %])}]})
(fx/defn logout-disable (rf/defn logout-disable
[cofx] [cofx]
(merge {::logout-disable nil} (merge {::logout-disable nil}
{::json-rpc/call [{:method "wakuext_unregisterFromPushNotifications" {::json-rpc/call [{:method "wakuext_unregisterFromPushNotifications"
@@ -127,113 +134,132 @@
:on-success #(log/info "[push-notifications] unregister-success" %) :on-success #(log/info "[push-notifications] unregister-success" %)
:on-error #(log/info "[push-notifications] unregister-error" %)}]})) :on-error #(log/info "[push-notifications] unregister-error" %)}]}))
(fx/defn notification-switch-error (rf/defn notification-switch-error
{:events [:notifications/switch-error]} {:events [:notifications/switch-error]}
[cofx enabled?] [cofx enabled?]
(multiaccounts.update/multiaccount-update (multiaccounts.update/multiaccount-update
cofx :remote-push-notifications-enabled? (not enabled?) {})) cofx
:remote-push-notifications-enabled?
(not enabled?)
{}))
(fx/defn notification-switch (rf/defn notification-switch
{:events [::switch]} {:events [::switch]}
[{:keys [db] :as cofx} enabled? remote-push-notifications?] [{:keys [db] :as cofx} enabled? remote-push-notifications?]
(fx/merge cofx (rf/merge cofx
(if enabled? (if enabled?
{::enable remote-push-notifications?} {::enable remote-push-notifications?}
{::disable nil}) {::disable nil})
(multiaccounts.update/multiaccount-update (multiaccounts.update/multiaccount-update
:remote-push-notifications-enabled? (and remote-push-notifications? enabled?) {}) :remote-push-notifications-enabled?
(and remote-push-notifications? enabled?)
{})
(multiaccounts.update/multiaccount-update (multiaccounts.update/multiaccount-update
:notifications-enabled? (and (not remote-push-notifications?) enabled?) {}))) :notifications-enabled?
(and (not remote-push-notifications?) enabled?)
{})))
(fx/defn notification-non-contacts-error (rf/defn notification-non-contacts-error
{:events [::non-contacts-update-error]} {:events [::non-contacts-update-error]}
[cofx enabled?] [cofx enabled?]
(multiaccounts.update/optimistic cofx :push-notifications-from-contacts-only? (not (boolean enabled?)))) (multiaccounts.update/optimistic cofx
:push-notifications-from-contacts-only?
(not (boolean enabled?))))
(fx/defn notification-block-mentions-error (rf/defn notification-block-mentions-error
{:events [::block-mentions-update-error]} {:events [::block-mentions-update-error]}
[cofx enabled?] [cofx enabled?]
(multiaccounts.update/optimistic cofx :push-notifications-block-mentions? (not (boolean enabled?)))) (multiaccounts.update/optimistic cofx :push-notifications-block-mentions? (not (boolean enabled?))))
(fx/defn notification-non-contacts (rf/defn notification-non-contacts
{:events [::switch-non-contacts]} {:events [::switch-non-contacts]}
[{:keys [db] :as cofx} enabled?] [{:keys [db] :as cofx} enabled?]
(let [method (if enabled? (let [method (if enabled?
"wakuext_enablePushNotificationsFromContactsOnly" "wakuext_enablePushNotificationsFromContactsOnly"
"wakuext_disablePushNotificationsFromContactsOnly")] "wakuext_disablePushNotificationsFromContactsOnly")]
(fx/merge cofx (rf/merge
{::json-rpc/call [{:method method cofx
:params [] {::json-rpc/call [{:method method
:on-success #(log/info "[push-notifications] contacts-notification-success" %) :params []
:on-error #(re-frame/dispatch [::non-contacts-update-error enabled? %])}]} :on-success #(log/info "[push-notifications] contacts-notification-success" %)
:on-error #(re-frame/dispatch [::non-contacts-update-error enabled? %])}]}
(multiaccounts.update/optimistic :push-notifications-from-contacts-only? (boolean enabled?))))) (multiaccounts.update/optimistic :push-notifications-from-contacts-only? (boolean enabled?)))))
(fx/defn notification-block-mentions (rf/defn notification-block-mentions
{:events [::switch-block-mentions]} {:events [::switch-block-mentions]}
[{:keys [db] :as cofx} enabled?] [{:keys [db] :as cofx} enabled?]
(let [method (if enabled? (let [method (if enabled?
"wakuext_enablePushNotificationsBlockMentions" "wakuext_enablePushNotificationsBlockMentions"
"wakuext_disablePushNotificationsBlockMentions")] "wakuext_disablePushNotificationsBlockMentions")]
(log/info "USING METHOD" method enabled?) (log/info "USING METHOD" method enabled?)
(fx/merge cofx (rf/merge cofx
{::json-rpc/call [{:method method {::json-rpc/call [{:method method
:params [] :params []
:on-success #(log/info "[push-notifications] block-mentions-success" %) :on-success #(log/info "[push-notifications] block-mentions-success" %)
:on-error #(re-frame/dispatch [::block-mentions-update-error enabled? %])}]} :on-error #(re-frame/dispatch [::block-mentions-update-error enabled?
%])}]}
(multiaccounts.update/optimistic :push-notifications-block-mentions? (boolean enabled?))))) (multiaccounts.update/optimistic :push-notifications-block-mentions? (boolean enabled?)))))
(fx/defn switch-push-notifications-server-enabled (rf/defn switch-push-notifications-server-enabled
{:events [::switch-push-notifications-server-enabled]} {:events [::switch-push-notifications-server-enabled]}
[{:keys [db] :as cofx} enabled?] [{:keys [db] :as cofx} enabled?]
(let [method (if enabled? (let [method (if enabled?
"wakuext_startPushNotificationsServer" "wakuext_startPushNotificationsServer"
"wakuext_stopPushNotificationsServer")] "wakuext_stopPushNotificationsServer")]
(fx/merge cofx (rf/merge
{::json-rpc/call [{:method method cofx
:params [] {::json-rpc/call [{:method method
:on-success #(log/info "[push-notifications] switch-server-enabled successful" %) :params []
:on-error #(re-frame/dispatch [::push-notifications-server-update-error enabled? %])}]} :on-success #(log/info "[push-notifications] switch-server-enabled successful" %)
:on-error #(re-frame/dispatch [::push-notifications-server-update-error
enabled? %])}]}
(multiaccounts.update/optimistic :push-notifications-server-enabled? (boolean enabled?))))) (multiaccounts.update/optimistic :push-notifications-server-enabled? (boolean enabled?)))))
(fx/defn switch-send-notifications (rf/defn switch-send-notifications
{:events [::switch-send-push-notifications]} {:events [::switch-send-push-notifications]}
[{:keys [db] :as cofx} enabled?] [{:keys [db] :as cofx} enabled?]
(let [method (if enabled? (let [method (if enabled?
"wakuext_enableSendingNotifications" "wakuext_enableSendingNotifications"
"wakuext_disableSendingNotifications")] "wakuext_disableSendingNotifications")]
(fx/merge cofx (rf/merge cofx
{::json-rpc/call [{:method method {::json-rpc/call [{:method method
:params [] :params []
:on-success #(log/info "[push-notifications] switch-send-notifications successful" %) :on-success
:on-error #(re-frame/dispatch [::push-notifications-send-update-error enabled? %])}]} #(log/info "[push-notifications] switch-send-notifications successful"
%)
:on-error #(re-frame/dispatch [::push-notifications-send-update-error
enabled? %])}]}
(multiaccounts.update/optimistic :send-push-notifications? (boolean enabled?))))) (multiaccounts.update/optimistic :send-push-notifications? (boolean enabled?)))))
(fx/defn handle-add-server-error (rf/defn handle-add-server-error
{:events [::push-notifications-add-server-error]} {:events [::push-notifications-add-server-error]}
[_ public-key error] [_ public-key error]
(log/error "failed to add server", public-key, error)) (log/error "failed to add server" public-key error))
(fx/defn add-server (rf/defn add-server
{:events [::add-server]} {:events [::add-server]}
[{:keys [db] :as cofx} public-key] [{:keys [db] :as cofx} public-key]
(fx/merge cofx (rf/merge cofx
{::json-rpc/call [{:method "wakuext_addPushNotificationsServer" {::json-rpc/call [{:method "wakuext_addPushNotificationsServer"
:params [public-key] :params [public-key]
:on-success #(do :on-success
(log/info "[push-notifications] switch-send-notifications successful" %) #(do
(re-frame/dispatch [::fetch-servers])) (log/info "[push-notifications] switch-send-notifications successful"
:on-error #(re-frame/dispatch [::push-notifications-add-server-error public-key %])}]})) %)
(re-frame/dispatch [::fetch-servers]))
:on-error #(re-frame/dispatch [::push-notifications-add-server-error
public-key %])}]}))
(fx/defn handle-servers-fetched (rf/defn handle-servers-fetched
{:events [::servers-fetched]} {:events [::servers-fetched]}
[{:keys [db]} servers] [{:keys [db]} servers]
{:db (assoc db :push-notifications/servers (map server<-rpc servers))}) {:db (assoc db :push-notifications/servers (map server<-rpc servers))})
(fx/defn fetch-push-notifications-servers (rf/defn fetch-push-notifications-servers
{:events [::fetch-servers]} {:events [::fetch-servers]}
[cofx] [cofx]
{::json-rpc/call [{:method "wakuext_getPushNotificationsServers" {::json-rpc/call [{:method "wakuext_getPushNotificationsServers"
@@ -244,34 +270,43 @@
;; Wallet transactions ;; Wallet transactions
(fx/defn handle-preferences-load (rf/defn handle-preferences-load
{:events [::preferences-loaded]} {:events [::preferences-loaded]}
[{:keys [db]} preferences] [{:keys [db]} preferences]
{:db (assoc db :push-notifications/preferences preferences)}) {:db (assoc db :push-notifications/preferences preferences)})
(fx/defn load-notification-preferences (rf/defn load-notification-preferences
{:events [::load-notification-preferences]} {:events [::load-notification-preferences]}
[cofx] [cofx]
{::json-rpc/call [{:method "localnotifications_notificationPreferences" {::json-rpc/call [{:method "localnotifications_notificationPreferences"
:params [] :params []
:on-success #(re-frame/dispatch [::preferences-loaded %])}]}) :on-success #(re-frame/dispatch [::preferences-loaded %])}]})
(defn preference= [x y] (defn preference=
[x y]
(and (= (:service x) (:service y)) (and (= (:service x) (:service y))
(= (:event x) (:event y)) (= (:event x) (:event y))
(= (:identifier x) (:identifier y)))) (= (:identifier x) (:identifier y))))
(defn- update-preference [all new] (defn- update-preference
[all new]
(conj (filter (comp not (partial preference= new)) all) new)) (conj (filter (comp not (partial preference= new)) all) new))
(fx/defn switch-transaction-notifications (rf/defn switch-transaction-notifications
{:events [::switch-transaction-notifications]} {:events [::switch-transaction-notifications]}
[{:keys [db] :as cofx} enabled?] [{:keys [db] :as cofx} enabled?]
{:db (update db :push-notifications/preferences update-preference {:enabled (not enabled?) {:db (update db
:service "wallet" :push-notifications/preferences
:event "transaction" update-preference
:identifier "all"}) {:enabled (not enabled?)
:service "wallet"
:event "transaction"
:identifier "all"})
::json-rpc/call [{:method "localnotifications_switchWalletNotifications" ::json-rpc/call [{:method "localnotifications_switchWalletNotifications"
:params [(not enabled?)] :params [(not enabled?)]
:on-success #(log/info "[push-notifications] switch-transaction-notifications successful" %) :on-success #(log/info
:on-error #(log/error "[push-notifications] switch-transaction-notifications error" %)}]}) "[push-notifications] switch-transaction-notifications successful"
%)
:on-error #(log/error
"[push-notifications] switch-transaction-notifications error"
%)}]})
+51 -39
View File
@@ -1,20 +1,20 @@
(ns status-im.notifications.local (ns status-im.notifications.local
(:require [status-im.async-storage.core :as async-storage] (:require ["@react-native-community/push-notification-ios" :default pn-ios]
[status-im.utils.fx :as fx]
[status-im.ethereum.decode :as decode]
["@react-native-community/push-notification-ios" :default pn-ios]
[status-im.notifications.android :as pn-android]
[status-im.ethereum.tokens :as tokens]
[status-im.utils.utils :as utils]
[status-im.utils.types :as types]
[status-im.utils.money :as money]
[status-im.i18n.i18n :as i18n]
[quo.platform :as platform]
[re-frame.core :as re-frame]
[cljs-bean.core :as bean] [cljs-bean.core :as bean]
[clojure.string :as string] [clojure.string :as string]
[quo.platform :as platform]
[re-frame.core :as re-frame]
[status-im.async-storage.core :as async-storage]
[status-im.chat.models :as chat.models] [status-im.chat.models :as chat.models]
[status-im.utils.react-native :as react-native-utils])) [status-im.ethereum.decode :as decode]
[status-im.ethereum.tokens :as tokens]
[status-im.i18n.i18n :as i18n]
[status-im.notifications.android :as pn-android]
[status-im.utils.money :as money]
[status-im.utils.react-native :as react-native-utils]
[status-im.utils.types :as types]
[status-im.utils.utils :as utils]
[utils.re-frame :as rf]))
(def default-erc20-token (def default-erc20-token
{:symbol :ERC20 {:symbol :ERC20
@@ -24,34 +24,39 @@
(def notification-event-ios "localNotification") (def notification-event-ios "localNotification")
(def notification-event-android "remoteNotificationReceived") (def notification-event-android "remoteNotificationReceived")
(defn local-push-ios [{:keys [title message user-info body-type]}] (defn local-push-ios
[{:keys [title message user-info body-type]}]
(when (not= body-type "message") (when (not= body-type "message")
(.presentLocalNotification (.presentLocalNotification
pn-ios pn-ios
#js {:alertBody message #js
:alertTitle title {:alertBody message
;; NOTE: Use a special type to hide in Obj-C code other notifications :alertTitle title
:userInfo (bean/->js (merge user-info ;; NOTE: Use a special type to hide in Obj-C code other notifications
{:notificationType "local-notification"}))}))) :userInfo (bean/->js (merge user-info
{:notificationType "local-notification"}))})))
(defn local-push-android (defn local-push-android
[notification] [notification]
(pn-android/present-local-notification notification)) (pn-android/present-local-notification notification))
(defn handle-notification-press [{{deep-link :deepLink} :userInfo (defn handle-notification-press
interaction :userInteraction}] [{{deep-link :deepLink} :userInfo
interaction :userInteraction}]
(async-storage/set-item! (str :chat-id) nil) (async-storage/set-item! (str :chat-id) nil)
(when (and deep-link (when (and deep-link
(or platform/ios? (or platform/ios?
(and platform/android? interaction))) (and platform/android? interaction)))
(re-frame/dispatch [:universal-links/handle-url deep-link]))) (re-frame/dispatch [:universal-links/handle-url deep-link])))
(defn listen-notifications [] (defn listen-notifications
[]
(if platform/ios? (if platform/ios?
(.addEventListener ^js pn-ios (.addEventListener ^js pn-ios
notification-event-ios notification-event-ios
(fn [notification] (fn [notification]
(handle-notification-press {:userInfo (bean/bean (.getData ^js notification))}))) (handle-notification-press {:userInfo (bean/bean (.getData ^js
notification))})))
(.addListener ^js react-native-utils/device-event-emitter (.addListener ^js react-native-utils/device-event-emitter
notification-event-android notification-event-android
(fn [^js data] (fn [^js data]
@@ -72,21 +77,27 @@
to (or (:name toAccount) (utils/get-shortened-address to)) to (or (:name toAccount) (utils/get-shortened-address to))
from (or (:name fromAccount) (utils/get-shortened-address from)) from (or (:name fromAccount) (utils/get-shortened-address from))
title (case state title (case state
"inbound" (i18n/label :t/push-inbound-transaction {:value amount "inbound" (i18n/label :t/push-inbound-transaction
:currency (:symbol token)}) {:value amount
"outbound" (i18n/label :t/push-outbound-transaction {:value amount :currency (:symbol token)})
:currency (:symbol token)}) "outbound" (i18n/label :t/push-outbound-transaction
"failed" (i18n/label :t/push-failed-transaction {:value amount {:value amount
:currency (:symbol token)}) :currency (:symbol token)})
"failed" (i18n/label :t/push-failed-transaction
{:value amount
:currency (:symbol token)})
nil) nil)
description (case state description (case state
"inbound" (i18n/label :t/push-inbound-transaction-body {:from from "inbound" (i18n/label :t/push-inbound-transaction-body
:to to}) {:from from
"outbound" (i18n/label :t/push-outbound-transaction-body {:from from :to to})
:to to}) "outbound" (i18n/label :t/push-outbound-transaction-body
"failed" (i18n/label :t/push-failed-transaction-body {:value amount {:from from
:currency (:symbol token) :to to})
:to to}) "failed" (i18n/label :t/push-failed-transaction-body
{:value amount
:currency (:symbol token)
:to to})
nil)] nil)]
{:title title {:title title
:icon (get-in token [:icon :source]) :icon (get-in token [:icon :source])
@@ -113,21 +124,22 @@
"message" (when (show-message-pn? cofx notification) notification) "message" (when (show-message-pn? cofx notification) notification)
"transaction" (create-transfer-notification cofx notification) "transaction" (create-transfer-notification cofx notification)
nil) nil)
:body-type bodyType))) :body-type
bodyType)))
(re-frame/reg-fx (re-frame/reg-fx
::local-push-ios ::local-push-ios
(fn [evt] (fn [evt]
(-> evt create-notification local-push-ios))) (-> evt create-notification local-push-ios)))
(fx/defn local-notification-android (rf/defn local-notification-android
{:events [::local-notification-android]} {:events [::local-notification-android]}
[cofx event] [cofx event]
(some->> event (some->> event
(create-notification cofx) (create-notification cofx)
local-push-android)) local-push-android))
(fx/defn process (rf/defn process
[cofx evt] [cofx evt]
(if platform/ios? (if platform/ios?
{::local-push-ios evt} {::local-push-ios evt}
+84 -62
View File
@@ -2,36 +2,40 @@
(:require [re-frame.core :as re-frame] (:require [re-frame.core :as re-frame]
[status-im.ethereum.json-rpc :as json-rpc] [status-im.ethereum.json-rpc :as json-rpc]
[status-im.i18n.i18n :as i18n] [status-im.i18n.i18n :as i18n]
[status-im2.navigation.events :as navigation] [status-im.multiaccounts.update.core :as multiaccounts.update]
[status-im.utils.config :as config] [status-im.utils.config :as config]
[status-im.utils.fx :as fx]
[status-im.utils.platform :as utils.platform] [status-im.utils.platform :as utils.platform]
[status-im2.navigation.events :as navigation]
[taoensso.timbre :as log] [taoensso.timbre :as log]
[status-im.multiaccounts.update.core :as multiaccounts.update])) [utils.re-frame :as rf]))
(defn enable-installation-rpc [installation-id on-success on-error] (defn enable-installation-rpc
[installation-id on-success on-error]
(json-rpc/call {:method "wakuext_enableInstallation" (json-rpc/call {:method "wakuext_enableInstallation"
:params [installation-id] :params [installation-id]
:on-success on-success :on-success on-success
:on-error on-error})) :on-error on-error}))
(defn disable-installation-rpc [installation-id on-success on-error] (defn disable-installation-rpc
[installation-id on-success on-error]
(json-rpc/call {:method "wakuext_disableInstallation" (json-rpc/call {:method "wakuext_disableInstallation"
:params [installation-id] :params [installation-id]
:on-success on-success :on-success on-success
:on-error on-error})) :on-error on-error}))
(defn set-installation-metadata-rpc [installation-id metadata on-success on-error] (defn set-installation-metadata-rpc
[installation-id metadata on-success on-error]
(json-rpc/call {:method "wakuext_setInstallationMetadata" (json-rpc/call {:method "wakuext_setInstallationMetadata"
:params [installation-id metadata] :params [installation-id metadata]
:on-success on-success :on-success on-success
:on-error on-error})) :on-error on-error}))
(defn get-our-installations-rpc [on-success on-error] (defn get-our-installations-rpc
[on-success on-error]
(json-rpc/call {:method "wakuext_getOurInstallations" (json-rpc/call {:method "wakuext_getOurInstallations"
:params [] :params []
:on-success on-success :on-success on-success
:on-error on-error})) :on-error on-error}))
(defn compare-installation (defn compare-installation
"Sort installations, first by our installation-id, then on whether is "Sort installations, first by our installation-id, then on whether is
@@ -52,56 +56,61 @@
[our-installation-id installations] [our-installation-id installations]
(sort (partial compare-installation our-installation-id) installations)) (sort (partial compare-installation our-installation-id) installations))
(fx/defn send-pair-installation (rf/defn send-pair-installation
{:events [:pairing.ui/pair-devices-pressed]} {:events [:pairing.ui/pair-devices-pressed]}
[_] [_]
{::json-rpc/call [{:method "wakuext_sendPairInstallation" {::json-rpc/call [{:method "wakuext_sendPairInstallation"
:params [] :params []
:on-success #(log/info "sent pair installation message")}]}) :on-success #(log/info "sent pair installation message")}]})
(fx/defn prompt-dismissed (rf/defn prompt-dismissed
{:events [:pairing.ui/prompt-dismissed]} {:events [:pairing.ui/prompt-dismissed]}
[{:keys [db]}] [{:keys [db]}]
{:db (assoc-in db [:pairing/prompt-user-pop-up] false)}) {:db (assoc-in db [:pairing/prompt-user-pop-up] false)})
(fx/defn prompt-accepted (rf/defn prompt-accepted
{:events [:pairing.ui/prompt-accepted]} {:events [:pairing.ui/prompt-accepted]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(fx/merge cofx (rf/merge cofx
{:db (assoc-in db [:pairing/prompt-user-pop-up] false)} {:db (assoc-in db [:pairing/prompt-user-pop-up] false)}
(navigation/navigate-to-cofx :installations nil))) (navigation/navigate-to-cofx :installations nil)))
(fx/defn prompt-user-on-new-installation [{:keys [db]}] (rf/defn prompt-user-on-new-installation
[{:keys [db]}]
(when-not config/pairing-popup-disabled? (when-not config/pairing-popup-disabled?
{:db (assoc-in db [:pairing/prompt-user-pop-up] true) {:db (assoc-in db [:pairing/prompt-user-pop-up] true)
:ui/show-confirmation {:title (i18n/label :t/pairing-new-installation-detected-title) :ui/show-confirmation {:title (i18n/label :t/pairing-new-installation-detected-title)
:content (i18n/label :t/pairing-new-installation-detected-content) :content (i18n/label
:t/pairing-new-installation-detected-content)
:confirm-button-text (i18n/label :t/pairing-go-to-installation) :confirm-button-text (i18n/label :t/pairing-go-to-installation)
:cancel-button-text (i18n/label :t/cancel) :cancel-button-text (i18n/label :t/cancel)
:on-cancel #(re-frame/dispatch [:pairing.ui/prompt-dismissed]) :on-cancel #(re-frame/dispatch [:pairing.ui/prompt-dismissed])
:on-accept #(re-frame/dispatch [:pairing.ui/prompt-accepted])}})) :on-accept #(re-frame/dispatch [:pairing.ui/prompt-accepted])}}))
(fx/defn set-name (rf/defn set-name
"Set the name of the device" "Set the name of the device"
{:events [:pairing.ui/set-name-pressed]} {:events [:pairing.ui/set-name-pressed]}
[{:keys [db]} installation-name] [{:keys [db]} installation-name]
(let [our-installation-id (get-in db [:multiaccount :installation-id])] (let [our-installation-id (get-in db [:multiaccount :installation-id])]
{:pairing/set-installation-metadata [our-installation-id {:pairing/set-installation-metadata [our-installation-id
{:name installation-name {:name installation-name
:deviceType utils.platform/os}]})) :deviceType utils.platform/os}]}))
(fx/defn init [cofx] (rf/defn init
[cofx]
{:pairing/get-our-installations nil}) {:pairing/get-our-installations nil})
(fx/defn enable [{:keys [db]} installation-id] (rf/defn enable
[{:keys [db]} installation-id]
{:db (assoc-in db {:db (assoc-in db
[:pairing/installations installation-id :enabled?] [:pairing/installations installation-id :enabled?]
true)}) true)})
(fx/defn disable [{:keys [db]} installation-id] (rf/defn disable
[{:keys [db]} installation-id]
{:db (assoc-in db {:db (assoc-in db
[:pairing/installations installation-id :enabled?] [:pairing/installations installation-id :enabled?]
false)}) false)})
(defn handle-enable-installation-response-success (defn handle-enable-installation-response-success
"Callback to dispatch on enable signature response" "Callback to dispatch on enable signature response"
@@ -123,38 +132,43 @@
[result] [result]
(re-frame/dispatch [:pairing.callback/get-our-installations-success result])) (re-frame/dispatch [:pairing.callback/get-our-installations-success result]))
(defn enable-installation! [installation-id] (defn enable-installation!
[installation-id]
(enable-installation-rpc (enable-installation-rpc
installation-id installation-id
(partial handle-enable-installation-response-success installation-id) (partial handle-enable-installation-response-success installation-id)
nil)) nil))
(defn disable-installation! [installation-id] (defn disable-installation!
[installation-id]
(disable-installation-rpc (disable-installation-rpc
installation-id installation-id
(partial handle-disable-installation-response-success installation-id) (partial handle-disable-installation-response-success installation-id)
nil)) nil))
(defn set-installation-metadata! [installation-id metadata] (defn set-installation-metadata!
[installation-id metadata]
(set-installation-metadata-rpc (set-installation-metadata-rpc
installation-id installation-id
metadata metadata
(partial handle-set-installation-metadata-response-success installation-id metadata) (partial handle-set-installation-metadata-response-success installation-id metadata)
nil)) nil))
(defn get-our-installations [] (defn get-our-installations
[]
(get-our-installations-rpc handle-get-our-installations-response-success nil)) (get-our-installations-rpc handle-get-our-installations-response-success nil))
(fx/defn enable-fx (rf/defn enable-fx
{:events [:pairing.ui/enable-installation-pressed]} {:events [:pairing.ui/enable-installation-pressed]}
[cofx installation-id] [cofx installation-id]
(if (< (count (filter :enabled? (vals (get-in cofx [:db :pairing/installations])))) (inc config/max-installations)) (if (< (count (filter :enabled? (vals (get-in cofx [:db :pairing/installations]))))
(inc config/max-installations))
{:pairing/enable-installation [installation-id]} {:pairing/enable-installation [installation-id]}
{:utils/show-popup {:title (i18n/label :t/pairing-maximum-number-reached-title) {:utils/show-popup {:title (i18n/label :t/pairing-maximum-number-reached-title)
:content (i18n/label :t/pairing-maximum-number-reached-content)}})) :content (i18n/label :t/pairing-maximum-number-reached-content)}}))
(fx/defn disable-fx (rf/defn disable-fx
{:events [:pairing.ui/disable-installation-pressed]} {:events [:pairing.ui/disable-installation-pressed]}
[_ installation-id] [_ installation-id]
{:pairing/disable-installation [installation-id]}) {:pairing/disable-installation [installation-id]})
@@ -178,58 +192,66 @@
:pairing/get-our-installations :pairing/get-our-installations
get-our-installations) get-our-installations)
(fx/defn send-installation-messages (rf/defn send-installation-messages
{:events [:pairing.ui/synchronize-installation-pressed]} {:events [:pairing.ui/synchronize-installation-pressed]}
[{:keys [db]}] [{:keys [db]}]
(let [multiaccount (:multiaccount db) (let [multiaccount (:multiaccount db)
{:keys [name preferred-name identicon]} multiaccount] {:keys [name preferred-name identicon]} multiaccount]
{::json-rpc/call [{:method "wakuext_syncDevices" {::json-rpc/call [{:method "wakuext_syncDevices"
:params [(or preferred-name name) identicon] :params [(or preferred-name name) identicon]
:on-success #(log/debug "successfully synced devices")}]})) :on-success #(log/debug "successfully synced devices")}]}))
(defn installation<-rpc [{:keys [metadata id enabled timestamp]}] (defn installation<-rpc
[{:keys [metadata id enabled timestamp]}]
{:installation-id id {:installation-id id
:name (:name metadata) :name (:name metadata)
:timestamp timestamp :timestamp timestamp
:device-type (:deviceType metadata) :device-type (:deviceType metadata)
:enabled? enabled}) :enabled? enabled})
(fx/defn update-installation (rf/defn update-installation
{:events [:pairing.callback/set-installation-metadata-success]} {:events [:pairing.callback/set-installation-metadata-success]}
[{:keys [db]} installation-id metadata] [{:keys [db]} installation-id metadata]
{:db (update-in db [:pairing/installations installation-id] {:db (update-in db
[:pairing/installations installation-id]
assoc assoc
:installation-id installation-id :installation-id installation-id
:name (:name metadata) :name (:name metadata)
:device-type (:deviceType metadata))}) :device-type (:deviceType metadata))})
(fx/defn handle-installations [{:keys [db]} installations] (rf/defn handle-installations
{:db (update db :pairing/installations #(reduce [{:keys [db]} installations]
(fn [acc {:keys [id] :as i}] {:db (update db
(update acc id merge (installation<-rpc i))) :pairing/installations
% #(reduce
installations))}) (fn [acc {:keys [id] :as i}]
(update acc id merge (installation<-rpc i)))
%
installations))})
(fx/defn load-installations (rf/defn load-installations
{:events [:pairing.callback/get-our-installations-success]} {:events [:pairing.callback/get-our-installations-success]}
[{:keys [db]} installations] [{:keys [db]} installations]
{:db (assoc db :pairing/installations (reduce {:db (assoc db
(fn [acc {:keys [id] :as i}] :pairing/installations
(assoc acc id (reduce
(installation<-rpc i))) (fn [acc {:keys [id] :as i}]
{} (assoc acc
installations))}) id
(installation<-rpc i)))
{}
installations))})
(fx/defn enable-installation-success (rf/defn enable-installation-success
{:events [:pairing.callback/enable-installation-success]} {:events [:pairing.callback/enable-installation-success]}
[cofx installation-id] [cofx installation-id]
(fx/merge cofx (rf/merge cofx
(enable installation-id) (enable installation-id)
(multiaccounts.update/send-multiaccount-update))) (multiaccounts.update/send-multiaccount-update)))
(fx/defn disable-installation-success (rf/defn disable-installation-success
{:events [:pairing.callback/disable-installation-success]} {:events [:pairing.callback/disable-installation-success]}
[cofx installation-id] [cofx installation-id]
(fx/merge cofx (rf/merge cofx
(disable installation-id) (disable installation-id)
(multiaccounts.update/send-multiaccount-update))) (multiaccounts.update/send-multiaccount-update)))
+6 -6
View File
@@ -1,21 +1,21 @@
(ns status-im.popover.core (ns status-im.popover.core
(:require [status-im.utils.fx :as fx])) (:require [utils.re-frame :as rf]))
(fx/defn show-popover (rf/defn show-popover
{:events [:show-popover]} {:events [:show-popover]}
[_ value] [_ value]
{:show-popover nil {:show-popover nil
;;TODO refactor popover just start animation on mount ;;TODO refactor popover just start animation on mount
:dispatch-later [{:ms 250 :dispatch [:show-popover-db value]}] :dispatch-later [{:ms 250 :dispatch [:show-popover-db value]}]
:dismiss-keyboard nil}) :dismiss-keyboard nil})
(fx/defn show-popover-db (rf/defn show-popover-db
{:events [:show-popover-db]} {:events [:show-popover-db]}
[{:keys [db]} value] [{:keys [db]} value]
{:db (assoc db :popover/popover value)}) {:db (assoc db :popover/popover value)})
(fx/defn hide-popover (rf/defn hide-popover
{:events [:hide-popover]} {:events [:hide-popover]}
[{:keys [db]}] [{:keys [db]}]
{:db (dissoc db :popover/popover) {:db (dissoc db :popover/popover)
:hide-popover nil}) :hide-popover nil})
+21 -17
View File
@@ -1,11 +1,11 @@
(ns status-im.profile.core (ns status-im.profile.core
(:require [re-frame.core :as re-frame] (:require [clojure.string :as string]
[status-im.utils.fx :as fx] [re-frame.core :as re-frame]
[status-im.ui.components.list-selection :as list-selection]
[status-im.utils.universal-links.utils :as universal-links]
[status-im.multiaccounts.update.core :as multiaccounts.update] [status-im.multiaccounts.update.core :as multiaccounts.update]
[status-im.ui.components.list-selection :as list-selection]
[status-im.ui.components.react :as react] [status-im.ui.components.react :as react]
[clojure.string :as string])) [status-im.utils.universal-links.utils :as universal-links]
[utils.re-frame :as rf]))
(re-frame/reg-fx (re-frame/reg-fx
:copy-to-clipboard :copy-to-clipboard
@@ -30,7 +30,9 @@
(swap! tooltips dissoc interval-id)) (swap! tooltips dissoc interval-id))
(do (re-frame/dispatch [:set-in [:tooltips tooltip-id] opacity]) (do (re-frame/dispatch [:set-in [:tooltips tooltip-id] opacity])
(when (< 10 cnt) (when (< 10 cnt)
(swap! tooltips assoc-in [tooltip-id :opacity] (- opacity 0.05))))))) (swap! tooltips assoc-in
[tooltip-id :opacity]
(- opacity 0.05)))))))
100)] 100)]
(swap! tooltips assoc tooltip-id {:opacity 1.0 :interval-id interval-id :cnt 0})))))) (swap! tooltips assoc tooltip-id {:opacity 1.0 :interval-id interval-id :cnt 0}))))))
@@ -40,43 +42,45 @@
(let [link (universal-links/generate-link :user :external contact-code)] (let [link (universal-links/generate-link :user :external contact-code)]
(list-selection/open-share {:message link})))) (list-selection/open-share {:message link}))))
(fx/defn finish-success (rf/defn finish-success
{:events [:my-profile/finish-success]} {:events [:my-profile/finish-success]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
{:db (update db :my-profile/seed assoc :step :finish :error nil :word nil)}) {:db (update db :my-profile/seed assoc :step :finish :error nil :word nil)})
(fx/defn finish (rf/defn finish
{:events [:my-profile/finish]} {:events [:my-profile/finish]}
[cofx] [cofx]
(multiaccounts.update/clean-seed-phrase (multiaccounts.update/clean-seed-phrase
cofx cofx
{:on-success #(re-frame/dispatch [:my-profile/finish-success])})) {:on-success #(re-frame/dispatch [:my-profile/finish-success])}))
(fx/defn enter-two-random-words (rf/defn enter-two-random-words
{:events [:my-profile/enter-two-random-words]} {:events [:my-profile/enter-two-random-words]}
[{:keys [db]}] [{:keys [db]}]
(let [{:keys [mnemonic]} (:multiaccount db) (let [{:keys [mnemonic]} (:multiaccount db)
shuffled-mnemonic (shuffle (map-indexed vector (string/split mnemonic #" ")))] shuffled-mnemonic (shuffle (map-indexed vector (string/split mnemonic #" ")))]
{:db (assoc db :my-profile/seed {:step :first-word {:db (assoc db
:first-word (first shuffled-mnemonic) :my-profile/seed
:second-word (second shuffled-mnemonic)})})) {:step :first-word
:first-word (first shuffled-mnemonic)
:second-word (second shuffled-mnemonic)})}))
(fx/defn set-step (rf/defn set-step
{:events [:my-profile/set-step]} {:events [:my-profile/set-step]}
[{:keys [db]} step] [{:keys [db]} step]
{:db (update db :my-profile/seed assoc :step step :error nil :word nil)}) {:db (update db :my-profile/seed assoc :step step :error nil :word nil)})
(fx/defn copy-to-clipboard (rf/defn copy-to-clipboard
{:events [:copy-to-clipboard]} {:events [:copy-to-clipboard]}
[_ value] [_ value]
{:copy-to-clipboard value}) {:copy-to-clipboard value})
(fx/defn show-tooltip (rf/defn show-tooltip
{:events [:show-tooltip]} {:events [:show-tooltip]}
[_ tooltip-id] [_ tooltip-id]
{:show-tooltip tooltip-id}) {:show-tooltip tooltip-id})
(fx/defn share-profile-link (rf/defn share-profile-link
{:events [:profile/share-profile-link]} {:events [:profile/share-profile-link]}
[_ value] [_ value]
{:profile/share-profile-link value}) {:profile/share-profile-link value})
+34 -29
View File
@@ -1,18 +1,18 @@
(ns status-im.qr-scanner.core (ns status-im.qr-scanner.core
(:require [re-frame.core :as re-frame] (:require [clojure.string :as string]
[status-im.i18n.i18n :as i18n] [re-frame.core :as re-frame]
[status-im.chat.models :as chat]
[status-im.router.core :as router]
[status-im2.navigation.events :as navigation]
[status-im.utils.utils :as utils]
[status-im.ethereum.core :as ethereum]
[status-im.add-new.db :as new-chat.db] [status-im.add-new.db :as new-chat.db]
[status-im.utils.fx :as fx] [status-im.chat.models :as chat]
[status-im.ethereum.core :as ethereum]
[status-im.group-chats.core :as group-chats] [status-im.group-chats.core :as group-chats]
[clojure.string :as string] [status-im.i18n.i18n :as i18n]
[taoensso.timbre :as log])) [status-im.router.core :as router]
[status-im.utils.utils :as utils]
[status-im2.navigation.events :as navigation]
[taoensso.timbre :as log]
[utils.re-frame :as rf]))
(fx/defn scan-qr-code (rf/defn scan-qr-code
{:events [::scan-code]} {:events [::scan-code]}
[_ opts] [_ opts]
{:request-permissions-fx {:request-permissions-fx
@@ -24,48 +24,52 @@
(i18n/label :t/camera-access-error)) (i18n/label :t/camera-access-error))
50))}}) 50))}})
(fx/defn set-qr-code (rf/defn set-qr-code
{:events [:qr-scanner.callback/scan-qr-code-success]} {:events [:qr-scanner.callback/scan-qr-code-success]}
[{:keys [db]} opts data] [{:keys [db]} opts data]
(when-let [handler (:handler opts)] (when-let [handler (:handler opts)]
{:dispatch [handler data opts]})) {:dispatch [handler data opts]}))
(fx/defn set-qr-code-cancel (rf/defn set-qr-code-cancel
{:events [:qr-scanner.callback/scan-qr-code-cancel]} {:events [:qr-scanner.callback/scan-qr-code-cancel]}
[cofx opts] [cofx opts]
(fx/merge cofx (rf/merge cofx
(navigation/navigate-back) (navigation/navigate-back)
(when-let [handler (:cancel-handler opts)] (when-let [handler (:cancel-handler opts)]
(fn [] {:dispatch [handler opts]})))) (fn [] {:dispatch [handler opts]}))))
(fx/defn handle-browse [cofx {:keys [url]}] (rf/defn handle-browse
(fx/merge cofx [cofx {:keys [url]}]
(rf/merge cofx
{:browser/show-browser-selection url} {:browser/show-browser-selection url}
(navigation/navigate-back))) (navigation/navigate-back)))
(fx/defn handle-private-chat [{:keys [db] :as cofx} {:keys [chat-id]}] (rf/defn handle-private-chat
[{:keys [db] :as cofx} {:keys [chat-id]}]
(if-not (new-chat.db/own-public-key? db chat-id) (if-not (new-chat.db/own-public-key? db chat-id)
(chat/start-chat cofx chat-id nil) (chat/start-chat cofx chat-id nil)
{:utils/show-popup {:title (i18n/label :t/unable-to-read-this-code) {:utils/show-popup {:title (i18n/label :t/unable-to-read-this-code)
:content (i18n/label :t/can-not-add-yourself)}})) :content (i18n/label :t/can-not-add-yourself)}}))
(fx/defn handle-public-chat [cofx {:keys [topic]}] (rf/defn handle-public-chat
[cofx {:keys [topic]}]
(when (seq topic) (when (seq topic)
(chat/start-public-chat cofx topic))) (chat/start-public-chat cofx topic)))
(fx/defn handle-group-chat [cofx params] (rf/defn handle-group-chat
[cofx params]
(group-chats/create-from-link cofx params)) (group-chats/create-from-link cofx params))
(fx/defn handle-view-profile (rf/defn handle-view-profile
[{:keys [db] :as cofx} {:keys [public-key ens-name]}] [{:keys [db] :as cofx} {:keys [public-key ens-name]}]
(let [own (new-chat.db/own-public-key? db public-key)] (let [own (new-chat.db/own-public-key? db public-key)]
(cond (cond
(and public-key own) (and public-key own)
{:change-tab-fx :profile {:change-tab-fx :profile
:pop-to-root-tab-fx :profile-stack} :pop-to-root-tab-fx :profile-stack}
(and public-key (not own)) (and public-key (not own))
(fx/merge cofx (rf/merge cofx
{:dispatch [:chat.ui/show-profile public-key ens-name]} {:dispatch [:chat.ui/show-profile public-key ens-name]}
(navigation/navigate-back)) (navigation/navigate-back))
@@ -74,13 +78,14 @@
:content (i18n/label :t/ens-name-not-found) :content (i18n/label :t/ens-name-not-found)
:on-dismiss #(re-frame/dispatch [:pop-to-root-tab :chat-stack])}}))) :on-dismiss #(re-frame/dispatch [:pop-to-root-tab :chat-stack])}})))
(fx/defn handle-eip681 [cofx data] (rf/defn handle-eip681
(fx/merge cofx [cofx data]
(rf/merge cofx
{:dispatch [:wallet/parse-eip681-uri-and-resolve-ens data]} {:dispatch [:wallet/parse-eip681-uri-and-resolve-ens data]}
(navigation/change-tab :wallet) (navigation/change-tab :wallet)
(navigation/pop-to-root-tab :wallet-stack))) (navigation/pop-to-root-tab :wallet-stack)))
(fx/defn handle-wallet-connect (rf/defn handle-wallet-connect
{:events [::handle-wallet-connect-uri]} {:events [::handle-wallet-connect-uri]}
[cofx data] [cofx data]
(let [wc-version (last (string/split (first (string/split data "?")) "@"))] (let [wc-version (last (string/split (first (string/split data "?")) "@"))]
@@ -88,12 +93,12 @@
{:dispatch [:wallet-connect-legacy/pair data]} {:dispatch [:wallet-connect-legacy/pair data]}
{:dispatch [:wallet-connect/pair data]}))) {:dispatch [:wallet-connect/pair data]})))
(fx/defn handle-local-pairing (rf/defn handle-local-pairing
{:events [::handle-local-pairing-uri]} {:events [::handle-local-pairing-uri]}
[_ data] [_ data]
{:dispatch [:syncing/input-connection-string-for-bootstrapping data]}) {:dispatch [:syncing/input-connection-string-for-bootstrapping data]})
(fx/defn match-scan (rf/defn match-scan
{:events [::match-scanned-value]} {:events [::match-scanned-value]}
[cofx {:keys [type] :as data}] [cofx {:keys [type] :as data}]
(case type (case type
@@ -109,11 +114,11 @@
(log/info "Unable to find matcher for scanned value" (log/info "Unable to find matcher for scanned value"
{:type type {:type type
:event ::match-scanned-value}) :event ::match-scanned-value})
{:dispatch [:navigate-back] {:dispatch [:navigate-back]
:utils/show-popup {:title (i18n/label :t/unable-to-read-this-code) :utils/show-popup {:title (i18n/label :t/unable-to-read-this-code)
:on-dismiss #(re-frame/dispatch [:pop-to-root-tab :chat-stack])}}))) :on-dismiss #(re-frame/dispatch [:pop-to-root-tab :chat-stack])}})))
(fx/defn on-scan (rf/defn on-scan
{:events [::on-scan-success]} {:events [::on-scan-success]}
[{:keys [db]} uri] [{:keys [db]} uri]
{::router/handle-uri {:chain (ethereum/chain-keyword db) {::router/handle-uri {:chain (ethereum/chain-keyword db)
+6 -6
View File
@@ -1,22 +1,22 @@
(ns status-im.search.core (ns status-im.search.core
(:require [status-im.utils.fx :as fx])) (:require [utils.re-frame :as rf]))
(fx/defn home-filter-changed (rf/defn home-filter-changed
{:events [:search/home-filter-changed]} {:events [:search/home-filter-changed]}
[cofx search-filter] [cofx search-filter]
{:db (assoc-in (:db cofx) [:ui/search :home-filter] search-filter)}) {:db (assoc-in (:db cofx) [:ui/search :home-filter] search-filter)})
(fx/defn currency-filter-changed (rf/defn currency-filter-changed
{:events [:search/currency-filter-changed]} {:events [:search/currency-filter-changed]}
[cofx search-filter] [cofx search-filter]
{:db (assoc-in (:db cofx) [:ui/search :currency-filter] search-filter)}) {:db (assoc-in (:db cofx) [:ui/search :currency-filter] search-filter)})
(fx/defn token-filter-changed (rf/defn token-filter-changed
{:events [:search/token-filter-changed]} {:events [:search/token-filter-changed]}
[cofx search-filter] [cofx search-filter]
{:db (assoc-in (:db cofx) [:ui/search :token-filter] search-filter)}) {:db (assoc-in (:db cofx) [:ui/search :token-filter] search-filter)})
(fx/defn recipient-filter-changed (rf/defn recipient-filter-changed
{:events [:search/recipient-filter-changed]} {:events [:search/recipient-filter-changed]}
[cofx search-filter] [cofx search-filter]
{:db (assoc-in (:db cofx) [:ui/search :recipient-filter] search-filter)}) {:db (assoc-in (:db cofx) [:ui/search :recipient-filter] search-filter)})
+78 -49
View File
@@ -1,85 +1,114 @@
(ns status-im.signals.core (ns status-im.signals.core
(:require [status-im.ethereum.subscriptions :as ethereum.subscriptions] (:require [status-im.chat.models.link-preview :as link.preview]
[status-im.chat.models.message :as models.message]
[status-im.ethereum.subscriptions :as ethereum.subscriptions]
[status-im.i18n.i18n :as i18n] [status-im.i18n.i18n :as i18n]
[status-im.mailserver.core :as mailserver] [status-im.mailserver.core :as mailserver]
[status-im.multiaccounts.login.core :as login] [status-im.multiaccounts.login.core :as login]
[status-im.transport.message.core :as transport.message]
[status-im.notifications.local :as local-notifications] [status-im.notifications.local :as local-notifications]
[status-im.chat.models.message :as models.message] [status-im.transport.message.core :as transport.message]
[status-im.chat.models.link-preview :as link.preview]
[status-im.visibility-status-updates.core :as visibility-status-updates] [status-im.visibility-status-updates.core :as visibility-status-updates]
[status-im.utils.fx :as fx] [taoensso.timbre :as log]
[taoensso.timbre :as log])) [utils.re-frame :as rf]))
(fx/defn status-node-started (rf/defn status-node-started
[{db :db :as cofx} {:keys [error]}] [{db :db :as cofx} {:keys [error]}]
(log/debug "[signals] status-node-started" (log/debug "[signals] status-node-started"
"error" error) "error"
error)
(if error (if error
(cond-> {:db (-> db (cond->
(update :multiaccounts/login dissoc :processing) {:db (-> db
(assoc-in [:multiaccounts/login :error] (update :multiaccounts/login dissoc :processing)
;; NOTE: the only currently known error is (assoc-in [:multiaccounts/login :error]
;; "file is not a database" which occurs ;; NOTE: the only currently known error is
;; when the user inputs the wrong password ;; "file is not a database" which occurs
;; if that is the error that is found ;; when the user inputs the wrong password
;; we show the i18n label for wrong password ;; if that is the error that is found
;; to the user ;; we show the i18n label for wrong password
;; in case of an unknown error we show the ;; to the user
;; error ;; in case of an unknown error we show the
(if (= error "file is not a database") ;; error
(i18n/label :t/wrong-password) (if (= error "file is not a database")
error)))} (i18n/label :t/wrong-password)
error)))}
(= (:view-id db) :progress) (= (:view-id db) :progress)
(assoc :dispatch [:navigate-to :login])) (assoc :dispatch [:navigate-to :login]))
(login/multiaccount-login-success cofx))) (login/multiaccount-login-success cofx)))
(fx/defn summary (rf/defn summary
[{:keys [db] :as cofx} peers-summary] [{:keys [db] :as cofx} peers-summary]
(let [previous-summary (:peers-summary db) (let [previous-summary (:peers-summary db)
peers-count (count peers-summary)] peers-count (count peers-summary)]
(fx/merge cofx (rf/merge cofx
{:db (assoc db {:db (assoc db
:peers-summary peers-summary :peers-summary peers-summary
:peers-count peers-count)} :peers-count peers-count)}
(visibility-status-updates/peers-summary-change peers-count)))) (visibility-status-updates/peers-summary-change peers-count))))
(fx/defn wakuv2-peer-stats (rf/defn wakuv2-peer-stats
[{:keys [db]} peer-stats] [{:keys [db]} peer-stats]
(let [previous-stats (:peer-stats db)] (let [previous-stats (:peer-stats db)]
{:db (assoc db :peer-stats peer-stats {:db (assoc db
:peer-stats peer-stats
:peers-count (count (:peers peer-stats)))})) :peers-count (count (:peers peer-stats)))}))
(defn handle-local-pairing-signals [signal-type] (defn handle-local-pairing-signals
[signal-type]
(log/info "local pairing signal received" (log/info "local pairing signal received"
{:signal-type signal-type})) {:signal-type signal-type}))
(fx/defn process (rf/defn process
{:events [:signals/signal-received]} {:events [:signals/signal-received]}
[{:keys [db] :as cofx} event-str] [{:keys [db] :as cofx} event-str]
;; We only convert to clojure when strictly necessary or we know it ;; We only convert to clojure when strictly necessary or we know it
;; won't impact performance, as it is a fairly costly operation on large-ish ;; won't impact performance, as it is a fairly costly operation on large-ish
;; data structures ;; data structures
(let [^js data (.parse js/JSON event-str) (let [^js data (.parse js/JSON event-str)
^js event-js (.-event data) ^js event-js (.-event data)
type (.-type data)] type (.-type data)]
(case type (case type
"node.login" (status-node-started cofx (js->clj event-js :keywordize-keys true)) "node.login" (status-node-started cofx (js->clj event-js :keywordize-keys true))
"backup.performed" {:db (assoc-in db [:multiaccount :last-backup] (.-lastBackup event-js))} "backup.performed" {:db (assoc-in db [:multiaccount :last-backup] (.-lastBackup event-js))}
"envelope.sent" (transport.message/update-envelopes-status cofx (:ids (js->clj event-js :keywordize-keys true)) :sent) "envelope.sent" (transport.message/update-envelopes-status cofx
"envelope.expired" (transport.message/update-envelopes-status cofx (:ids (js->clj event-js :keywordize-keys true)) :not-sent) (:ids
"message.delivered" (let [{:keys [chatID messageID]} (js->clj event-js :keywordize-keys true)] (js->clj event-js
(models.message/update-db-message-status cofx chatID messageID :delivered)) :keywordize-keys
"mailserver.changed" (mailserver/handle-mailserver-changed cofx (.-id event-js)) true))
"mailserver.available" (mailserver/handle-mailserver-available cofx (.-id event-js)) :sent)
"mailserver.not.working" (mailserver/handle-mailserver-not-working cofx) "envelope.expired" (transport.message/update-envelopes-status cofx
"discovery.summary" (summary cofx (js->clj event-js :keywordize-keys true)) (:ids
"mediaserver.started" {:db (assoc db :mediaserver/port (.-port event-js))} (js->clj event-js
"wakuv2.peerstats" (wakuv2-peer-stats cofx (js->clj event-js :keywordize-keys true)) :keywordize-keys
"messages.new" (transport.message/sanitize-messages-and-process-response cofx event-js true) true))
"wallet" (ethereum.subscriptions/new-wallet-event cofx (js->clj event-js :keywordize-keys true)) :not-sent)
"local-notifications" (local-notifications/process cofx (js->clj event-js :keywordize-keys true)) "message.delivered" (let [{:keys [chatID messageID]} (js->clj event-js
"community.found" (link.preview/cache-community-preview-data (js->clj event-js :keywordize-keys true)) :keywordize-keys
"status.updates.timedout" (visibility-status-updates/handle-visibility-status-updates cofx (js->clj event-js :keywordize-keys true)) true)]
"localPairing" (handle-local-pairing-signals event-str) (models.message/update-db-message-status cofx
chatID
messageID
:delivered))
"mailserver.changed" (mailserver/handle-mailserver-changed cofx (.-id event-js))
"mailserver.available" (mailserver/handle-mailserver-available cofx (.-id event-js))
"mailserver.not.working" (mailserver/handle-mailserver-not-working cofx)
"discovery.summary" (summary cofx (js->clj event-js :keywordize-keys true))
"mediaserver.started" {:db (assoc db :mediaserver/port (.-port event-js))}
"wakuv2.peerstats" (wakuv2-peer-stats cofx (js->clj event-js :keywordize-keys true))
"messages.new" (transport.message/sanitize-messages-and-process-response cofx
event-js
true)
"wallet" (ethereum.subscriptions/new-wallet-event cofx
(js->clj event-js
:keywordize-keys
true))
"local-notifications" (local-notifications/process cofx
(js->clj event-js :keywordize-keys true))
"community.found" (link.preview/cache-community-preview-data (js->clj event-js
:keywordize-keys
true))
"status.updates.timedout" (visibility-status-updates/handle-visibility-status-updates
cofx
(js->clj event-js :keywordize-keys true))
"localPairing" (handle-local-pairing-signals event-str)
(log/debug "Event " type " not handled")))) (log/debug "Event " type " not handled"))))
+221 -192
View File
@@ -1,27 +1,27 @@
(ns status-im.signing.core (ns status-im.signing.core
(:require [clojure.string :as string] (:require [clojure.set :as clojure.set]
[clojure.string :as string]
[re-frame.core :as re-frame] [re-frame.core :as re-frame]
[status-im.constants :as constants] [status-im.constants :as constants]
[status-im.ethereum.core :as ethereum] [status-im.ethereum.core :as ethereum]
[status-im.ethereum.eip55 :as eip55] [status-im.ethereum.eip55 :as eip55]
[status-im.ethereum.json-rpc :as json-rpc] [status-im.ethereum.json-rpc :as json-rpc]
[status-im.ethereum.tokens :as tokens] [status-im.ethereum.tokens :as tokens]
[status-im.keycard.common :as keycard.common]
[status-im.i18n.i18n :as i18n] [status-im.i18n.i18n :as i18n]
[status-im.keycard.card :as keycard.card] [status-im.keycard.card :as keycard.card]
[status-im.keycard.common :as keycard.common]
[status-im.native-module.core :as status] [status-im.native-module.core :as status]
[status-im.signing.eip1559 :as eip1559]
[status-im.signing.keycard :as signing.keycard] [status-im.signing.keycard :as signing.keycard]
[status-im.utils.fx :as fx]
[status-im.utils.hex :as utils.hex] [status-im.utils.hex :as utils.hex]
[status-im.utils.money :as money] [status-im.utils.money :as money]
[utils.security.core :as security]
[status-im.utils.types :as types] [status-im.utils.types :as types]
[status-im.utils.utils :as utils] [status-im.utils.utils :as utils]
[status-im.wallet.prices :as prices]
[status-im.wallet.core :as wallet] [status-im.wallet.core :as wallet]
[status-im.wallet.prices :as prices]
[taoensso.timbre :as log] [taoensso.timbre :as log]
[clojure.set :as clojure.set] [utils.re-frame :as rf]
[status-im.signing.eip1559 :as eip1559])) [utils.security.core :as security]))
(re-frame/reg-fx (re-frame/reg-fx
:signing/send-transaction-fx :signing/send-transaction-fx
@@ -59,42 +59,46 @@
(status/sign-typed-data-v4 data account hashed-password on-completed) (status/sign-typed-data-v4 data account hashed-password on-completed)
(status/sign-typed-data data account hashed-password on-completed)))) (status/sign-typed-data data account hashed-password on-completed))))
(defn get-contact [db to] (defn get-contact
[db to]
(let [to (utils.hex/normalize-hex to)] (let [to (utils.hex/normalize-hex to)]
(or (or
(get-in db [:contacts/contacts to]) (get-in db [:contacts/contacts to])
{:address (ethereum/normalized-hex to)}))) {:address (ethereum/normalized-hex to)})))
(fx/defn change-password (rf/defn change-password
{:events [:signing.ui/password-is-changed]} {:events [:signing.ui/password-is-changed]}
[{db :db} password] [{db :db} password]
(let [unmasked-pass (security/safe-unmask-data password)] (let [unmasked-pass (security/safe-unmask-data password)]
{:db (update db :signing/sign assoc {:db (update db
:password password :signing/sign assoc
:error nil :password password
:enabled? (and unmasked-pass (> (count unmasked-pass) 5)))})) :error nil
:enabled? (and unmasked-pass (> (count unmasked-pass) 5)))}))
(fx/defn sign-message (rf/defn sign-message
[{{:signing/keys [sign tx] :as db} :db}] [{{:signing/keys [sign tx] :as db} :db}]
(let [{{:keys [data typed? from v4]} :message} tx (let [{{:keys [data typed? from v4]} :message} tx
{:keys [in-progress? password]} sign {:keys [in-progress? password]} sign
from (or from (ethereum/default-address db)) from (or from (ethereum/default-address db))
hashed-password (ethereum/sha3 (security/safe-unmask-data password))] hashed-password (ethereum/sha3 (security/safe-unmask-data password))]
(when-not in-progress? (when-not in-progress?
(merge (merge
{:db (update db :signing/sign assoc :error nil :in-progress? true)} {:db (update db :signing/sign assoc :error nil :in-progress? true)}
(if typed? (if typed?
{:signing.fx/sign-typed-data {:v4 v4 {:signing.fx/sign-typed-data {:v4 v4
:data data :data data
:account from :account from
:hashed-password hashed-password :hashed-password hashed-password
:on-completed #(re-frame/dispatch [:signing/sign-message-completed %])}} :on-completed #(re-frame/dispatch
[:signing/sign-message-completed %])}}
{:signing.fx/sign-message {:params {:data data {:signing.fx/sign-message {:params {:data data
:password hashed-password :password hashed-password
:account from} :account from}
:on-completed #(re-frame/dispatch [:signing/sign-message-completed %])}}))))) :on-completed #(re-frame/dispatch [:signing/sign-message-completed
%])}})))))
(fx/defn send-transaction (rf/defn send-transaction
{:events [:signing.ui/sign-is-pressed]} {:events [:signing.ui/sign-is-pressed]}
[{{:signing/keys [sign tx] :as db} :db :as cofx}] [{{:signing/keys [sign tx] :as db} :db :as cofx}]
(let [{:keys [in-progress? password]} sign (let [{:keys [in-progress? password]} sign
@@ -110,57 +114,62 @@
(when nonce (when nonce
{:nonce (str "0x" (status/number-to-hex nonce))}) {:nonce (str "0x" (status/number-to-hex nonce))})
(when maxPriorityFeePerGas (when maxPriorityFeePerGas
{:maxPriorityFeePerGas (str "0x" (status/number-to-hex {:maxPriorityFeePerGas (str "0x"
(js/parseInt maxPriorityFeePerGas)))}) (status/number-to-hex
(js/parseInt maxPriorityFeePerGas)))})
(when maxFeePerGas (when maxFeePerGas
{:maxFeePerGas (str "0x" (status/number-to-hex {:maxFeePerGas (str "0x"
(js/parseInt maxFeePerGas)))}))] (status/number-to-hex
(js/parseInt maxFeePerGas)))}))]
(when-not in-progress? (when-not in-progress?
{:db (update db :signing/sign assoc :error nil :in-progress? true) {:db (update db :signing/sign assoc :error nil :in-progress? true)
:signing/send-transaction-fx {:tx-obj tx-obj-to-send :signing/send-transaction-fx {:tx-obj tx-obj-to-send
:hashed-password hashed-password :hashed-password hashed-password
:cb #(re-frame/dispatch [:signing/transaction-completed % tx-obj-to-send hashed-password])}}))))) :cb #(re-frame/dispatch
[:signing/transaction-completed %
tx-obj-to-send hashed-password])}})))))
(fx/defn prepare-unconfirmed-transaction (rf/defn prepare-unconfirmed-transaction
[{:keys [db now]} new-tx-hash [{:keys [db now]} new-tx-hash
{:keys [value gasPrice maxFeePerGas maxPriorityFeePerGas gas data to from hash]} symbol amount] {:keys [value gasPrice maxFeePerGas maxPriorityFeePerGas gas data to from hash]} symbol amount]
(let [token (tokens/symbol->token (:wallet/all-tokens db) symbol) (let [token (tokens/symbol->token (:wallet/all-tokens db) symbol)
from (eip55/address->checksum from) from (eip55/address->checksum from)
;;if there is a hash in the tx object that means we resending transaction ;;if there is a hash in the tx object that means we resending transaction
old-tx-hash hash old-tx-hash hash
gas-price (money/to-fixed (money/bignumber gasPrice)) gas-price (money/to-fixed (money/bignumber gasPrice))
gas-limit (money/to-fixed (money/bignumber gas)) gas-limit (money/to-fixed (money/bignumber gas))
tx {:timestamp now tx {:timestamp now
:to to :to to
:from from :from from
:type :pending :type :pending
:hash new-tx-hash :hash new-tx-hash
:data data :data data
:token token :token token
:symbol symbol :symbol symbol
:value (if token :value (if token
(money/to-fixed (money/unit->token amount (:decimals token))) (money/to-fixed (money/unit->token amount (:decimals token)))
(money/to-fixed (money/bignumber value))) (money/to-fixed (money/bignumber value)))
:gas-price gas-price :gas-price gas-price
:fee-cap maxFeePerGas :fee-cap maxFeePerGas
:tip-cap maxPriorityFeePerGas :tip-cap maxPriorityFeePerGas
:gas-limit gas-limit}] :gas-limit gas-limit}]
(log/info "[signing] prepare-unconfirmed-transaction" tx) (log/info "[signing] prepare-unconfirmed-transaction" tx)
{:db (-> db {:db (-> db
;;remove old transaction, because we replace it with the new one ;;remove old transaction, because we replace it with the new one
(update-in [:wallet :accounts from :transactions] dissoc old-tx-hash) (update-in [:wallet :accounts from :transactions] dissoc old-tx-hash)
(assoc-in [:wallet :accounts from :transactions new-tx-hash] tx)) (assoc-in [:wallet :accounts from :transactions new-tx-hash] tx))
::json-rpc/call [{:method "wallet_storePendingTransaction" ::json-rpc/call [{:method "wallet_storePendingTransaction"
:params [(-> tx :params [(-> tx
(dissoc :gas-price :gas-limit) (dissoc :gas-price :gas-limit)
(assoc :gasPrice (assoc :gasPrice
(money/to-fixed (money/bignumber gasPrice)) (money/to-fixed (money/bignumber gasPrice))
:gasLimit (money/to-fixed (money/bignumber gas))) :gasLimit (money/to-fixed (money/bignumber gas)))
clj->js)] clj->js)]
:on-success #(log/info "pending transfer is saved") :on-success #(log/info "pending transfer is saved")
:on-error #(log/info "pending transfer was not saved" %)}]})) :on-error #(log/info "pending transfer was not saved" %)}]}))
(defn get-method-type [data] (defn get-method-type
[data]
(cond (cond
(string/starts-with? data constants/method-id-transfer) (string/starts-with? data constants/method-id-transfer)
:transfer :transfer
@@ -169,13 +178,16 @@
(string/starts-with? data constants/method-id-approve-and-call) (string/starts-with? data constants/method-id-approve-and-call)
:approve-and-call)) :approve-and-call))
(defn get-transfer-token [db to data] (defn get-transfer-token
[db to data]
(let [{:keys [symbol decimals] :as token} (tokens/address->token (:wallet/all-tokens db) to)] (let [{:keys [symbol decimals] :as token} (tokens/address->token (:wallet/all-tokens db) to)]
(when (and token data (string? data)) (when (and token data (string? data))
(when-let [type (get-method-type data)] (when-let [type (get-method-type data)]
(let [[address value _] (status/decode-parameters (let [[address value _] (status/decode-parameters
(str "0x" (subs data 10)) (str "0x" (subs data 10))
(if (= type :approve-and-call) ["address" "uint256" "bytes"] ["address" "uint256"]))] (if (= type :approve-and-call)
["address" "uint256" "bytes"]
["address" "uint256"]))]
(when (and address value) (when (and address value)
{:to address {:to address
:contact (get-contact db address) :contact (get-contact db address)
@@ -186,7 +198,8 @@
:token token :token token
:symbol symbol})))))) :symbol symbol}))))))
(defn parse-tx-obj [db {:keys [from to value data cancel? hash]}] (defn parse-tx-obj
[db {:keys [from to value data cancel? hash]}]
(merge {:from {:address from} (merge {:from {:address from}
:cancel? cancel? :cancel? cancel?
:hash hash} :hash hash}
@@ -196,20 +209,23 @@
eth-amount (when eth-value (money/to-fixed (money/wei->ether eth-value))) eth-amount (when eth-value (money/to-fixed (money/wei->ether eth-value)))
token (get-transfer-token db to data)] token (get-transfer-token db to data)]
(cond (cond
(and eth-amount (or (not (.equals ^js (money/bignumber 0) ^js eth-amount)) (nil? data))) (and eth-amount (or (not (.equals ^js (money/bignumber 0) ^js eth-amount)) (nil? data)))
{:to to {:to to
:contact (get-contact db to) :contact (get-contact db to)
:symbol :ETH :symbol :ETH
:value value :value value
:amount (str eth-amount) :amount (str eth-amount)
:token (tokens/asset-for (:wallet/all-tokens db) (ethereum/get-current-network db) :ETH)} :token (tokens/asset-for (:wallet/all-tokens db)
(ethereum/get-current-network db)
:ETH)}
(not (nil? token)) (not (nil? token))
token token
:else :else
{:to to {:to to
:contact {:address (ethereum/normalized-hex to)}}))))) :contact {:address (ethereum/normalized-hex to)}})))))
(defn prepare-tx [db {{:keys [data gas gasPrice maxFeePerGas maxPriorityFeePerGas] :as tx-obj} :tx-obj :as tx}] (defn prepare-tx
[db {{:keys [data gas gasPrice maxFeePerGas maxPriorityFeePerGas] :as tx-obj} :tx-obj :as tx}]
(merge (merge
tx tx
(parse-tx-obj db tx-obj) (parse-tx-obj db tx-obj)
@@ -221,45 +237,52 @@
:maxPriorityFeePerGas (when maxPriorityFeePerGas :maxPriorityFeePerGas (when maxPriorityFeePerGas
(money/bignumber maxPriorityFeePerGas))})) (money/bignumber maxPriorityFeePerGas))}))
(fx/defn show-sign [{:keys [db] :as cofx}] (rf/defn show-sign
[{:keys [db] :as cofx}]
(let [{:signing/keys [queue]} db (let [{:signing/keys [queue]} db
{{:keys [gas gasPrice maxFeePerGas] :as tx-obj} :tx-obj {:keys [data typed? pinless?] :as message} :message :as tx} (last queue) {{:keys [gas gasPrice maxFeePerGas] :as tx-obj} :tx-obj
{:keys [data typed? pinless?] :as message} :message
:as tx}
(last queue)
keycard-multiaccount? (boolean (get-in db [:multiaccount :keycard-pairing])) keycard-multiaccount? (boolean (get-in db [:multiaccount :keycard-pairing]))
wallet-set-up-passed? (get-in db [:multiaccount :wallet-set-up-passed?])] wallet-set-up-passed? (get-in db [:multiaccount :wallet-set-up-passed?])]
(if message (if message
(fx/merge (rf/merge
cofx cofx
{:db (assoc db {:db (assoc db
:signing/queue (drop-last queue) :signing/queue (drop-last queue)
:signing/tx tx :signing/tx tx
:signing/sign {:type (cond pinless? :pinless :signing/sign {:type (cond pinless? :pinless
keycard-multiaccount? :keycard keycard-multiaccount? :keycard
:else :password) :else :password)
:formatted-data (if typed? :formatted-data (if typed?
(types/js->pretty-json (types/json->js data)) (types/js->pretty-json
(ethereum/hex->text data)) (types/json->js data))
:keycard-step (when pinless? :connect)}) (ethereum/hex->text data))
:keycard-step (when pinless? :connect)})
:show-signing-sheet nil} :show-signing-sheet nil}
#(when-not wallet-set-up-passed? #(when-not wallet-set-up-passed?
{:dispatch-later [{:dispatch [:show-popover {:view :signing-phrase}] :ms 200}]}) {:dispatch-later [{:dispatch [:show-popover {:view :signing-phrase}] :ms 200}]})
(when pinless? (when pinless?
(keycard.card/start-nfc {:on-success #(re-frame/dispatch [:keycard.callback/start-nfc-success]) (keycard.card/start-nfc {:on-success #(re-frame/dispatch [:keycard.callback/start-nfc-success])
:on-failure #(re-frame/dispatch [:keycard.callback/start-nfc-failure])}) :on-failure #(re-frame/dispatch
(signing.keycard/hash-message {:data data [:keycard.callback/start-nfc-failure])})
:typed? true (signing.keycard/hash-message
:on-completed #(re-frame/dispatch [:keycard/store-hash-and-sign-typed %])}))) {:data data
(fx/merge :typed? true
:on-completed #(re-frame/dispatch [:keycard/store-hash-and-sign-typed %])})))
(rf/merge
cofx cofx
{:db (assoc db {:db (assoc db
:signing/queue (drop-last queue) :signing/queue (drop-last queue)
:signing/tx (prepare-tx db tx)) :signing/tx (prepare-tx db tx))
:show-signing-sheet nil :show-signing-sheet nil
:dismiss-keyboard nil} :dismiss-keyboard nil}
#(when-not wallet-set-up-passed? #(when-not wallet-set-up-passed?
{:dispatch-later [{:dispatch [:show-popover {:view :signing-phrase}] :ms 200}]}) {:dispatch-later [{:dispatch [:show-popover {:view :signing-phrase}] :ms 200}]})
(prices/update-prices) (prices/update-prices)
#(when-not gas #(when-not gas
{:db (assoc-in (:db %) [:signing/edit-fee :gas-loading?] true) {:db (assoc-in (:db %) [:signing/edit-fee :gas-loading?] true)
:signing/update-estimated-gas {:obj (-> tx-obj :signing/update-estimated-gas {:obj (-> tx-obj
(dissoc :gasPrice) (dissoc :gasPrice)
(update :maxFeePerGas (update :maxFeePerGas
@@ -269,9 +292,9 @@
(money/mul 2) (money/mul 2)
money/to-hex)))) money/to-hex))))
:success-event :signing/update-estimated-gas-success :success-event :signing/update-estimated-gas-success
:error-event :signing/update-estimated-gas-error}}) :error-event :signing/update-estimated-gas-error}})
(fn [cofx] (fn [cofx]
{:db (assoc-in (:db cofx) [:signing/edit-fee :gas-price-loading?] true) {:db (assoc-in (:db cofx) [:signing/edit-fee :gas-price-loading?] true)
:signing/update-gas-price :signing/update-gas-price
{:success-callback #(re-frame/dispatch {:success-callback #(re-frame/dispatch
[:wallet.send/update-gas-price-success :signing/tx % tx-obj]) [:wallet.send/update-gas-price-success :signing/tx % tx-obj])
@@ -279,40 +302,41 @@
:network-id (get-in (ethereum/current-network db) :network-id (get-in (ethereum/current-network db)
[:config :NetworkId])}}))))) [:config :NetworkId])}})))))
(fx/defn check-queue [{:keys [db] :as cofx}] (rf/defn check-queue
[{:keys [db] :as cofx}]
(let [{:signing/keys [tx queue]} db] (let [{:signing/keys [tx queue]} db]
(when (and (not tx) (seq queue)) (when (and (not tx) (seq queue))
(show-sign cofx)))) (show-sign cofx))))
(fx/defn send-transaction-message (rf/defn send-transaction-message
{:events [:sign/send-transaction-message]} {:events [:sign/send-transaction-message]}
[cofx chat-id value contract transaction-hash signature] [cofx chat-id value contract transaction-hash signature]
{::json-rpc/call [{:method "wakuext_sendTransaction" {::json-rpc/call [{:method "wakuext_sendTransaction"
;; We make sure `value` is serialized as string, and not ;; We make sure `value` is serialized as string, and not
;; as an integer or big-int ;; as an integer or big-int
:params [chat-id (str value) contract transaction-hash :params [chat-id (str value) contract transaction-hash
(or (:result (types/json->clj signature)) (or (:result (types/json->clj signature))
(ethereum/normalized-hex signature))] (ethereum/normalized-hex signature))]
:js-response true :js-response true
:on-success :on-success
#(re-frame/dispatch [:transport/message-sent %])}]}) #(re-frame/dispatch [:transport/message-sent %])}]})
(fx/defn send-accept-request-transaction-message (rf/defn send-accept-request-transaction-message
{:events [:sign/send-accept-transaction-message]} {:events [:sign/send-accept-transaction-message]}
[cofx message-id transaction-hash signature] [cofx message-id transaction-hash signature]
{::json-rpc/call [{:method "wakuext_acceptRequestTransaction" {::json-rpc/call [{:method "wakuext_acceptRequestTransaction"
:params [transaction-hash message-id :params [transaction-hash message-id
(or (:result (types/json->clj signature)) (or (:result (types/json->clj signature))
(ethereum/normalized-hex signature))] (ethereum/normalized-hex signature))]
:js-response true :js-response true
:on-success :on-success
#(re-frame/dispatch [:transport/message-sent %])}]}) #(re-frame/dispatch [:transport/message-sent %])}]})
(fx/defn transaction-result (rf/defn transaction-result
[{:keys [db] :as cofx} result tx-obj] [{:keys [db] :as cofx} result tx-obj]
(let [{:keys [on-result symbol amount from]} (get db :signing/tx)] (let [{:keys [on-result symbol amount from]} (get db :signing/tx)]
(fx/merge cofx (rf/merge cofx
{:db (dissoc db :signing/tx :signing/sign) {:db (dissoc db :signing/tx :signing/sign)
:signing/show-transaction-result nil} :signing/show-transaction-result nil}
(prepare-unconfirmed-transaction result tx-obj symbol amount) (prepare-unconfirmed-transaction result tx-obj symbol amount)
(check-queue) (check-queue)
@@ -320,35 +344,35 @@
#(when on-result #(when on-result
{:dispatch (conj on-result result)})))) {:dispatch (conj on-result result)}))))
(fx/defn command-transaction-result (rf/defn command-transaction-result
[{:keys [db] :as cofx} transaction-hash hashed-password [{:keys [db] :as cofx} transaction-hash hashed-password
{:keys [message-id chat-id from] :as tx-obj}] {:keys [message-id chat-id from] :as tx-obj}]
(let [{:keys [on-result symbol amount contract value]} (get db :signing/tx) (let [{:keys [on-result symbol amount contract value]} (get db :signing/tx)
data (str (get-in db [:multiaccount :public-key]) data (str (get-in db [:multiaccount :public-key])
(subs transaction-hash 2))] (subs transaction-hash 2))]
(fx/merge (rf/merge
cofx cofx
{:db (dissoc db :signing/tx :signing/sign)} {:db (dissoc db :signing/tx :signing/sign)}
(wallet/watch-tx (get from :address) transaction-hash) (wallet/watch-tx (get from :address) transaction-hash)
(if (keycard.common/keycard-multiaccount? db) (if (keycard.common/keycard-multiaccount? db)
(signing.keycard/hash-message (signing.keycard/hash-message
{:data data {:data data
:on-completed :on-completed
(fn [hash] (fn [hash]
(re-frame/dispatch (re-frame/dispatch
[:keycard/sign-message [:keycard/sign-message
{:tx-hash transaction-hash {:tx-hash transaction-hash
:message-id message-id :message-id message-id
:chat-id chat-id :chat-id chat-id
:value value :value value
:contract contract :contract contract
:data data} :data data}
hash]))}) hash]))})
(fn [_] (fn [_]
{:signing.fx/sign-message {:signing.fx/sign-message
{:params {:data data {:params {:data data
:password hashed-password :password hashed-password
:account from} :account from}
:on-completed :on-completed
(fn [res] (fn [res]
(re-frame/dispatch (re-frame/dispatch
@@ -362,7 +386,7 @@
#(when on-result #(when on-result
{:dispatch (conj on-result transaction-hash)})))) {:dispatch (conj on-result transaction-hash)}))))
(fx/defn transaction-error (rf/defn transaction-error
[{:keys [db]} {:keys [code message]}] [{:keys [db]} {:keys [code message]}]
(let [on-error (get-in db [:signing/tx :on-error])] (let [on-error (get-in db [:signing/tx :on-error])]
(if (= code constants/send-transaction-err-decrypt) (if (= code constants/send-transaction-err-decrypt)
@@ -373,34 +397,36 @@
(when on-error (when on-error
{:dispatch (conj on-error message)}))))) {:dispatch (conj on-error message)})))))
(fx/defn dissoc-signing-db-entries-and-check-queue (rf/defn dissoc-signing-db-entries-and-check-queue
{:events [:signing/dissoc-entries-and-check-queue]} {:events [:signing/dissoc-entries-and-check-queue]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(fx/merge cofx (rf/merge cofx
{:db (dissoc db :signing/tx :signing/sign)} {:db (dissoc db :signing/tx :signing/sign)}
check-queue)) check-queue))
(fx/defn sign-message-completed (rf/defn sign-message-completed
{:events [:signing/sign-message-completed]} {:events [:signing/sign-message-completed]}
[{:keys [db] :as cofx} result] [{:keys [db] :as cofx} result]
(let [{:keys [result error]} (types/json->clj result) (let [{:keys [result error]} (types/json->clj result)
on-result (get-in db [:signing/tx :on-result])] on-result (get-in db [:signing/tx :on-result])]
(if error (if error
{:db (update db :signing/sign {:db (update db
assoc :error (if (= 5 (:code error)) :signing/sign
assoc
:error (if (= 5 (:code error))
(i18n/label :t/wrong-password) (i18n/label :t/wrong-password)
(:message error)) (:message error))
:in-progress? false)} :in-progress? false)}
(fx/merge cofx (rf/merge cofx
(when-not (= (-> db :signing/sign :type) :pinless) (when-not (= (-> db :signing/sign :type) :pinless)
(dissoc-signing-db-entries-and-check-queue)) (dissoc-signing-db-entries-and-check-queue))
#(when (= (-> db :signing/sign :type) :pinless) #(when (= (-> db :signing/sign :type) :pinless)
{:dispatch-later [{:ms 3000 {:dispatch-later [{:ms 3000
:dispatch [:signing/dissoc-entries-and-check-queue]}]}) :dispatch [:signing/dissoc-entries-and-check-queue]}]})
#(when on-result #(when on-result
{:dispatch (conj on-result result)}))))) {:dispatch (conj on-result result)})))))
(fx/defn transaction-completed (rf/defn transaction-completed
{:events [:signing/transaction-completed] {:events [:signing/transaction-completed]
:interceptors [(re-frame/inject-cofx :random-id-generator)]} :interceptors [(re-frame/inject-cofx :random-id-generator)]}
[cofx response tx-obj hashed-password] [cofx response tx-obj hashed-password]
@@ -412,15 +438,15 @@
(command-transaction-result cofx-in-progress-false result hashed-password tx-obj) (command-transaction-result cofx-in-progress-false result hashed-password tx-obj)
(transaction-result cofx-in-progress-false result tx-obj))))) (transaction-result cofx-in-progress-false result tx-obj)))))
(fx/defn discard (rf/defn discard
"Discrad transaction signing" "Discrad transaction signing"
{:events [:signing.ui/cancel-is-pressed]} {:events [:signing.ui/cancel-is-pressed]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(let [{:keys [on-error]} (get-in db [:signing/tx])] (let [{:keys [on-error]} (get-in db [:signing/tx])]
(fx/merge cofx (rf/merge cofx
{:db (-> db {:db (-> db
(assoc-in [:keycard :pin :status] nil) (assoc-in [:keycard :pin :status] nil)
(dissoc :signing/tx :signing/sign)) (dissoc :signing/tx :signing/sign))
:hide-signing-sheet nil} :hide-signing-sheet nil}
(check-queue) (check-queue)
(keycard.common/hide-connection-sheet) (keycard.common/hide-connection-sheet)
@@ -428,10 +454,11 @@
#(when on-error #(when on-error
{:dispatch (conj on-error "transaction was cancelled by user")})))) {:dispatch (conj on-error "transaction was cancelled by user")}))))
(defn normalize-tx-obj [db tx] (defn normalize-tx-obj
[db tx]
(update-in tx [:tx-obj :from] #(eip55/address->checksum (or % (ethereum/default-address db))))) (update-in tx [:tx-obj :from] #(eip55/address->checksum (or % (ethereum/default-address db)))))
(fx/defn sign (rf/defn sign
"Signing transaction or message, shows signing sheet "Signing transaction or message, shows signing sheet
tx tx
{:tx-obj - transaction object to send https://github.com/ethereum/wiki/wiki/JavaScript-API#parameters-25 {:tx-obj - transaction object to send https://github.com/ethereum/wiki/wiki/JavaScript-API#parameters-25
@@ -440,80 +467,80 @@
:on-error - re-frame event vector}" :on-error - re-frame event vector}"
{:events [:signing.ui/sign]} {:events [:signing.ui/sign]}
[{:keys [db] :as cofx} tx] [{:keys [db] :as cofx} tx]
(fx/merge cofx (rf/merge cofx
{:db (update db :signing/queue conj (normalize-tx-obj db tx))} {:db (update db :signing/queue conj (normalize-tx-obj db tx))}
(check-queue))) (check-queue)))
(fx/defn sign-transaction-button-clicked-from-chat (rf/defn sign-transaction-button-clicked-from-chat
{:events [:wallet.ui/sign-transaction-button-clicked-from-chat]} {:events [:wallet.ui/sign-transaction-button-clicked-from-chat]}
[{:keys [db] :as cofx} {:keys [to amount from token]}] [{:keys [db] :as cofx} {:keys [to amount from token]}]
(let [{:keys [symbol address]} token (let [{:keys [symbol address]} token
amount-hex (str "0x" (status/number-to-hex amount)) amount-hex (str "0x" (status/number-to-hex amount))
to-norm (ethereum/normalized-hex (if (string? to) to (:address to))) to-norm (ethereum/normalized-hex (if (string? to) to (:address to)))
from-address (:address from) from-address (:address from)
identity (:current-chat-id db) identity (:current-chat-id db)
db (dissoc db :wallet/prepare-transaction :signing/edit-fee)] db (dissoc db :wallet/prepare-transaction :signing/edit-fee)]
(if to-norm (if to-norm
(fx/merge (rf/merge
cofx cofx
{:db db} {:db db}
(sign {:tx-obj (if (= symbol :ETH) (sign {:tx-obj (if (= symbol :ETH)
{:to to-norm {:to to-norm
:from from-address :from from-address
:chat-id identity :chat-id identity
:command? true :command? true
:value amount-hex} :value amount-hex}
{:to (ethereum/normalized-hex address) {:to (ethereum/normalized-hex address)
:from from-address :from from-address
:chat-id identity :chat-id identity
:command? true :command? true
:data (status/encode-transfer to-norm amount-hex)})})) :data (status/encode-transfer to-norm amount-hex)})}))
{:db db {:db db
::json-rpc/call ::json-rpc/call
[{:method "wakuext_requestAddressForTransaction" [{:method "wakuext_requestAddressForTransaction"
:params [(:current-chat-id db) :params [(:current-chat-id db)
from-address from-address
amount amount
(when-not (= symbol :ETH) (when-not (= symbol :ETH)
address)] address)]
:js-response true :js-response true
:on-success #(re-frame/dispatch [:transport/message-sent %])}]}))) :on-success #(re-frame/dispatch [:transport/message-sent %])}]})))
(fx/defn sign-transaction-button-clicked-from-request (rf/defn sign-transaction-button-clicked-from-request
{:events [:wallet.ui/sign-transaction-button-clicked-from-request]} {:events [:wallet.ui/sign-transaction-button-clicked-from-request]}
[{:keys [db] :as cofx} {:keys [amount from token]}] [{:keys [db] :as cofx} {:keys [amount from token]}]
(let [{:keys [request-parameters chat-id]} (:wallet/prepare-transaction db) (let [{:keys [request-parameters chat-id]} (:wallet/prepare-transaction db)
{:keys [symbol address]} token {:keys [symbol address]} token
amount-hex (str "0x" (status/number-to-hex amount)) amount-hex (str "0x" (status/number-to-hex amount))
to-norm (:address request-parameters) to-norm (:address request-parameters)
from-address (:address from)] from-address (:address from)]
(fx/merge cofx (rf/merge cofx
{:db (dissoc db :wallet/prepare-transaction :signing/edit-fee)} {:db (dissoc db :wallet/prepare-transaction :signing/edit-fee)}
(fn [cofx] (fn [cofx]
(sign (sign
cofx cofx
{:tx-obj (if (= symbol :ETH) {:tx-obj (if (= symbol :ETH)
{:to to-norm {:to to-norm
:from from-address :from from-address
:message-id (:id request-parameters) :message-id (:id request-parameters)
:chat-id chat-id :chat-id chat-id
:command? true :command? true
:value amount-hex} :value amount-hex}
{:to (ethereum/normalized-hex address) {:to (ethereum/normalized-hex address)
:from from-address :from from-address
:command? true :command? true
:message-id (:id request-parameters) :message-id (:id request-parameters)
:chat-id chat-id :chat-id chat-id
:data (status/encode-transfer to-norm amount-hex)})}))))) :data (status/encode-transfer to-norm amount-hex)})})))))
(fx/defn sign-transaction-button-clicked (rf/defn sign-transaction-button-clicked
{:events [:wallet.ui/sign-transaction-button-clicked]} {:events [:wallet.ui/sign-transaction-button-clicked]}
[{:keys [db] :as cofx} {:keys [to amount from token gas gasPrice maxFeePerGas maxPriorityFeePerGas]}] [{:keys [db] :as cofx} {:keys [to amount from token gas gasPrice maxFeePerGas maxPriorityFeePerGas]}]
(let [{:keys [symbol address]} token (let [{:keys [symbol address]} token
amount-hex (str "0x" (status/number-to-hex amount)) amount-hex (str "0x" (status/number-to-hex amount))
to-norm (ethereum/normalized-hex (if (string? to) to (:address to))) to-norm (ethereum/normalized-hex (if (string? to) to (:address to)))
from-address (:address from)] from-address (:address from)]
(fx/merge cofx (rf/merge cofx
{:db (dissoc db :wallet/prepare-transaction :signing/edit-fee)} {:db (dissoc db :wallet/prepare-transaction :signing/edit-fee)}
(sign (sign
{:tx-obj (merge (if (eip1559/sync-enabled?) {:tx-obj (merge (if (eip1559/sync-enabled?)
@@ -540,31 +567,33 @@
:params [hash] :params [hash]
:on-success handler}))) :on-success handler})))
(fx/defn cancel-transaction-pressed (rf/defn cancel-transaction-pressed
{:events [:signing.ui/cancel-transaction-pressed]} {:events [:signing.ui/cancel-transaction-pressed]}
[_ hash] [_ hash]
{:signing/get-transaction-by-hash-fx [hash #(re-frame/dispatch [:signing/cancel-transaction %])]}) {:signing/get-transaction-by-hash-fx [hash #(re-frame/dispatch [:signing/cancel-transaction %])]})
(fx/defn increase-gas-pressed (rf/defn increase-gas-pressed
{:events [:signing.ui/increase-gas-pressed]} {:events [:signing.ui/increase-gas-pressed]}
[_ hash] [_ hash]
{:signing/get-transaction-by-hash-fx [hash #(re-frame/dispatch [:signing/increase-gas %])]}) {:signing/get-transaction-by-hash-fx [hash #(re-frame/dispatch [:signing/increase-gas %])]})
(fx/defn cancel-transaction (rf/defn cancel-transaction
{:events [:signing/cancel-transaction]} {:events [:signing/cancel-transaction]}
[cofx {:keys [from nonce hash]}] [cofx {:keys [from nonce hash]}]
(when (and from nonce hash) (when (and from nonce hash)
(sign cofx {:tx-obj {:from from (sign cofx
:to from {:tx-obj {:from from
:nonce nonce :to from
:value "0x0" :nonce nonce
:cancel? true :value "0x0"
:hash hash}}))) :cancel? true
:hash hash}})))
(fx/defn increase-gas (rf/defn increase-gas
{:events [:signing/increase-gas]} {:events [:signing/increase-gas]}
[cofx {:keys [from nonce] :as tx}] [cofx {:keys [from nonce] :as tx}]
(when (and from nonce) (when (and from nonce)
(sign cofx {:tx-obj (-> tx (sign cofx
(select-keys [:from :to :value :input :gas :nonce :hash]) {:tx-obj (-> tx
(clojure.set/rename-keys {:input :data}))}))) (select-keys [:from :to :value :input :gas :nonce :hash])
(clojure.set/rename-keys {:input :data}))})))
+130 -96
View File
@@ -1,15 +1,15 @@
(ns status-im.signing.gas (ns status-im.signing.gas
(:require [re-frame.core :as re-frame] (:require [clojure.string :as string]
[re-frame.core :as re-frame]
[status-im.bottom-sheet.core :as bottom-sheet]
[status-im.ethereum.core :as ethereum]
[status-im.ethereum.json-rpc :as json-rpc] [status-im.ethereum.json-rpc :as json-rpc]
[status-im.i18n.i18n :as i18n] [status-im.i18n.i18n :as i18n]
[status-im.bottom-sheet.core :as bottom-sheet]
[status-im.utils.fx :as fx]
[status-im.utils.money :as money]
[status-im.signing.eip1559 :as eip1559]
[taoensso.timbre :as log]
[status-im.popover.core :as popover] [status-im.popover.core :as popover]
[status-im.ethereum.core :as ethereum] [status-im.signing.eip1559 :as eip1559]
[clojure.string :as string])) [status-im.utils.money :as money]
[taoensso.timbre :as log]
[utils.re-frame :as rf]))
(def min-gas-price-wei ^js (money/bignumber 1)) (def min-gas-price-wei ^js (money/bignumber 1))
@@ -17,19 +17,22 @@
(defmulti get-error-label-key (fn [type _] type)) (defmulti get-error-label-key (fn [type _] type))
(defmethod get-error-label-key :gasPrice [_ value] (defmethod get-error-label-key :gasPrice
[_ value]
(cond (cond
(not value) :t/invalid-number (not value) :t/invalid-number
(.lt ^js (money/->wei :gwei value) min-gas-price-wei) :t/wallet-send-min-wei (.lt ^js (money/->wei :gwei value) min-gas-price-wei) :t/wallet-send-min-wei
(-> (money/->wei :gwei value) .decimalPlaces pos?) :t/invalid-number)) (-> (money/->wei :gwei value) .decimalPlaces pos?) :t/invalid-number))
(defmethod get-error-label-key :gas [_ ^js value] (defmethod get-error-label-key :gas
[_ ^js value]
(cond (cond
(not value) :t/invalid-number (not value) :t/invalid-number
(.lt value min-gas-units) :t/wallet-send-min-units (.lt value min-gas-units) :t/wallet-send-min-units
(-> value .decimalPlaces pos?) :t/invalid-number)) (-> value .decimalPlaces pos?) :t/invalid-number))
(defmethod get-error-label-key :default [_ value] (defmethod get-error-label-key :default
[_ value]
(when (or (not value) (when (or (not value)
(<= value 0)) (<= value 0))
:t/invalid-number)) :t/invalid-number))
@@ -40,7 +43,8 @@
(money/to-fixed (money/wei->ether (.times gas gasPrice))) (money/to-fixed (money/wei->ether (.times gas gasPrice)))
"0")) "0"))
(defn edit-max-fee [edit] (defn edit-max-fee
[edit]
(let [gasPrice (get-in edit [:gasPrice :value-number]) (let [gasPrice (get-in edit [:gasPrice :value-number])
gas (get-in edit [:gas :value-number])] gas (get-in edit [:gas :value-number])]
(assoc edit :max-fee (calculate-max-fee gas gasPrice)))) (assoc edit :max-fee (calculate-max-fee gas gasPrice))))
@@ -50,7 +54,7 @@
Wei for gas, and gwei for gas price. Wei for gas, and gwei for gas price.
Validates them and sets max fee" Validates them and sets max fee"
[edit-value key value] [edit-value key value]
(let [^js bn-value (money/bignumber value) (let [^js bn-value (money/bignumber value)
error-label-key (get-error-label-key key bn-value) error-label-key (get-error-label-key key bn-value)
data (if error-label-key data (if error-label-key
{:value value {:value value
@@ -69,17 +73,20 @@
(def minimum-priority-fee-gwei (def minimum-priority-fee-gwei
(money/bignumber 0.3)) (money/bignumber 0.3))
(defn get-suggested-tip [latest-priority-fee] (defn get-suggested-tip
[latest-priority-fee]
(money/bignumber latest-priority-fee)) (money/bignumber latest-priority-fee))
(defn get-minimum-priority-fee [latest-priority-fee] (defn get-minimum-priority-fee
[latest-priority-fee]
(let [latest-priority-fee-bn (money/bignumber latest-priority-fee) (let [latest-priority-fee-bn (money/bignumber latest-priority-fee)
suggested-tip-gwei (money/wei->gwei (get-suggested-tip latest-priority-fee-bn))] suggested-tip-gwei (money/wei->gwei (get-suggested-tip latest-priority-fee-bn))]
(if (money/greater-than minimum-priority-fee-gwei suggested-tip-gwei) (if (money/greater-than minimum-priority-fee-gwei suggested-tip-gwei)
(money/div suggested-tip-gwei 2) (money/div suggested-tip-gwei 2)
minimum-priority-fee-gwei))) minimum-priority-fee-gwei)))
(defn get-suggestions-range [latest-priority-fee] (defn get-suggestions-range
[latest-priority-fee]
(let [current-minimum-fee (get-minimum-priority-fee latest-priority-fee)] (let [current-minimum-fee (get-minimum-priority-fee latest-priority-fee)]
[(if (money/greater-than minimum-priority-fee-gwei current-minimum-fee) [(if (money/greater-than minimum-priority-fee-gwei current-minimum-fee)
current-minimum-fee current-minimum-fee
@@ -89,47 +96,52 @@
(def average-priority-fee (def average-priority-fee
(money/wei->gwei (money/->wei :gwei 1.5))) (money/wei->gwei (money/->wei :gwei 1.5)))
(defn validate-max-fee [db] (defn validate-max-fee
[db]
(let [{:keys [maxFeePerGas maxPriorityFeePerGas]} (get db :signing/edit-fee) (let [{:keys [maxFeePerGas maxPriorityFeePerGas]} (get db :signing/edit-fee)
latest-base-fee (money/wei->gwei latest-base-fee (money/wei->gwei
(money/bignumber (money/bignumber
(get db :wallet/current-base-fee))) (get db :wallet/current-base-fee)))
fee-error (cond fee-error (cond
(or (:error maxFeePerGas) (or (:error maxFeePerGas)
(:error maxPriorityFeePerGas)) (:error maxPriorityFeePerGas))
nil nil
(money/greater-than latest-base-fee (money/greater-than latest-base-fee
(:value-number maxFeePerGas)) (:value-number maxFeePerGas))
{:label (i18n/label :t/below-base-fee) {:label (i18n/label :t/below-base-fee)
:severity :error})] :severity :error})]
(if fee-error (if fee-error
(assoc-in db [:signing/edit-fee :maxFeePerGas :fee-error] fee-error) (assoc-in db [:signing/edit-fee :maxFeePerGas :fee-error] fee-error)
(update-in db [:signing/edit-fee :maxFeePerGas] dissoc :fee-error)))) (update-in db [:signing/edit-fee :maxFeePerGas] dissoc :fee-error))))
(defn validate-max-priority-fee [db] (defn validate-max-priority-fee
[db]
(let [{:keys [maxPriorityFeePerGas]} (get db :signing/edit-fee) (let [{:keys [maxPriorityFeePerGas]} (get db :signing/edit-fee)
latest-priority-fee (get db :wallet/current-priority-fee) latest-priority-fee (get db :wallet/current-priority-fee)
fee-error (cond fee-error (cond
(:error maxPriorityFeePerGas) (:error maxPriorityFeePerGas)
nil nil
(money/greater-than (get-minimum-priority-fee (money/greater-than (get-minimum-priority-fee
(money/div (money/div
(money/wei->gwei (money/bignumber latest-priority-fee)) 2)) (money/wei->gwei (money/bignumber
(:value-number maxPriorityFeePerGas)) latest-priority-fee))
{:label (i18n/label :t/low-tip) 2))
:severity :error} (:value-number maxPriorityFeePerGas))
{:label (i18n/label :t/low-tip)
:severity :error}
#_(money/greater-than average-priority-fee #_(money/greater-than average-priority-fee
(:value-number maxPriorityFeePerGas)) (:value-number maxPriorityFeePerGas))
#_{:label (i18n/label :t/lower-than-average-tip) #_{:label (i18n/label :t/lower-than-average-tip)
:severity :error})] :severity :error})]
(if fee-error (if fee-error
(assoc-in db [:signing/edit-fee :maxPriorityFeePerGas :fee-error] fee-error) (assoc-in db [:signing/edit-fee :maxPriorityFeePerGas :fee-error] fee-error)
(update-in db [:signing/edit-fee :maxPriorityFeePerGas] dissoc :fee-error)))) (update-in db [:signing/edit-fee :maxPriorityFeePerGas] dissoc :fee-error))))
(defn validate-eip1559-fees [db] (defn validate-eip1559-fees
[db]
(if (eip1559/sync-enabled?) (if (eip1559/sync-enabled?)
(reduce (reduce
(fn [acc f] (fn [acc f]
@@ -139,7 +151,7 @@
validate-max-priority-fee]) validate-max-priority-fee])
db)) db))
(fx/defn edit-value (rf/defn edit-value
{:events [:signing.edit-fee.ui/edit-value]} {:events [:signing.edit-fee.ui/edit-value]}
[{:keys [db]} key value] [{:keys [db]} key value]
{:db (-> db {:db (-> db
@@ -147,7 +159,8 @@
(update :signing/edit-fee build-edit key value) (update :signing/edit-fee build-edit key value)
validate-eip1559-fees)}) validate-eip1559-fees)})
(defn get-fee-options [tip slow normal fast] (defn get-fee-options
[tip slow normal fast]
(let [tip-bn (money/bignumber tip) (let [tip-bn (money/bignumber tip)
normal-bn (money/bignumber normal) normal-bn (money/bignumber normal)
slow-bn (money/bignumber slow) slow-bn (money/bignumber slow)
@@ -165,7 +178,7 @@
:tip tip-bn :tip tip-bn
:fee (money/add fast-bn tip-bn)}})) :fee (money/add fast-bn tip-bn)}}))
(fx/defn set-fee-option (rf/defn set-fee-option
{:events [:signing.edit-fee.ui/set-option]} {:events [:signing.edit-fee.ui/set-option]}
[{:keys [db] :as cofx} option] [{:keys [db] :as cofx} option]
(let [tip (get db :wallet/current-priority-fee) (let [tip (get db :wallet/current-priority-fee)
@@ -176,21 +189,23 @@
{:db (-> db {:db (-> db
(assoc-in [:signing/edit-fee :selected-fee-option] option) (assoc-in [:signing/edit-fee :selected-fee-option] option)
(update :signing/edit-fee (update :signing/edit-fee
build-edit :maxFeePerGas (money/wei->gwei fee)) build-edit
:maxFeePerGas (money/wei->gwei fee))
(update :signing/edit-fee (update :signing/edit-fee
build-edit :maxPriorityFeePerGas (money/wei->gwei tip)))})) build-edit
:maxPriorityFeePerGas (money/wei->gwei tip)))}))
(fx/defn set-priority-fee (rf/defn set-priority-fee
{:events [:signing.edit-fee.ui/set-priority-fee]} {:events [:signing.edit-fee.ui/set-priority-fee]}
[{:keys [db]} value] [{:keys [db]} value]
(let [{:keys [maxFeePerGas maxPriorityFeePerGas]} (let [{:keys [maxFeePerGas maxPriorityFeePerGas]}
(get db :signing/edit-fee) (get db :signing/edit-fee)
latest-base-fee (get db :wallet/current-base-fee) latest-base-fee (get db :wallet/current-base-fee)
max-fee-value (:value-number maxFeePerGas) max-fee-value (:value-number maxFeePerGas)
max-priority-fee-value (:value-number maxPriorityFeePerGas) max-priority-fee-value (:value-number maxPriorityFeePerGas)
new-value (money/bignumber value) new-value (money/bignumber value)
fee-without-tip (money/sub max-fee-value max-priority-fee-value) fee-without-tip (money/sub max-fee-value max-priority-fee-value)
base-fee (money/wei->gwei (money/bignumber latest-base-fee)) base-fee (money/wei->gwei (money/bignumber latest-base-fee))
new-max-fee-value new-max-fee-value
(money/to-fixed (money/to-fixed
(if (money/greater-than base-fee fee-without-tip) (if (money/greater-than base-fee fee-without-tip)
@@ -201,19 +216,21 @@
(update :signing/edit-fee build-edit :maxFeePerGas new-max-fee-value) (update :signing/edit-fee build-edit :maxFeePerGas new-max-fee-value)
validate-eip1559-fees)})) validate-eip1559-fees)}))
(fx/defn update-estimated-gas-success (rf/defn update-estimated-gas-success
{:events [:signing/update-estimated-gas-success]} {:events [:signing/update-estimated-gas-success]}
[{db :db} gas] [{db :db} gas]
{:db (-> db {:db (-> db
(assoc-in [:signing/tx :gas] gas) (assoc-in [:signing/tx :gas] gas)
(assoc-in [:signing/edit-fee :gas-loading?] false))}) (assoc-in [:signing/edit-fee :gas-loading?] false))})
(fx/defn update-gas-price-success (rf/defn update-gas-price-success
{:events [:signing/update-gas-price-success]} {:events [:signing/update-gas-price-success]}
[{db :db} price] [{db :db} price]
(if (eip1559/sync-enabled?) (if (eip1559/sync-enabled?)
(let [{:keys [normal-base-fee max-priority-fee]} price (let [{:keys [normal-base-fee max-priority-fee]} price
max-priority-fee-bn (money/with-precision (get-suggested-tip max-priority-fee) 0)] max-priority-fee-bn (money/with-precision (get-suggested-tip
max-priority-fee)
0)]
{:db (-> db {:db (-> db
(assoc-in [:signing/tx :maxFeePerGas] (assoc-in [:signing/tx :maxFeePerGas]
(money/to-hex (money/add max-priority-fee-bn (money/to-hex (money/add max-priority-fee-bn
@@ -225,7 +242,7 @@
(assoc-in [:signing/tx :gasPrice] price) (assoc-in [:signing/tx :gasPrice] price)
(assoc-in [:signing/edit-fee :gas-price-loading?] false))})) (assoc-in [:signing/edit-fee :gas-price-loading?] false))}))
(fx/defn update-estimated-gas-error (rf/defn update-estimated-gas-error
{:events [:signing/update-estimated-gas-error]} {:events [:signing/update-estimated-gas-error]}
[{db :db} {:keys [message]}] [{db :db} {:keys [message]}]
(log/warn "signing/update-estimated-gas-error" message) (log/warn "signing/update-estimated-gas-error" message)
@@ -233,28 +250,36 @@
(assoc-in [:signing/edit-fee :gas-loading?] false) (assoc-in [:signing/edit-fee :gas-loading?] false)
(assoc-in [:signing/tx :gas-error-message] message))}) (assoc-in [:signing/tx :gas-error-message] message))})
(fx/defn update-gas-price-error (rf/defn update-gas-price-error
{:events [:signing/update-gas-price-error]} {:events [:signing/update-gas-price-error]}
[{db :db}] [{db :db}]
{:db (assoc-in db [:signing/edit-fee :gas-price-loading?] false)}) {:db (assoc-in db [:signing/edit-fee :gas-price-loading?] false)})
(fx/defn open-fee-sheet (rf/defn open-fee-sheet
{:events [:signing.ui/open-fee-sheet]} {:events [:signing.ui/open-fee-sheet]}
[{{:signing/keys [tx] :as db} :db :as cofx} sheet-opts] [{{:signing/keys [tx] :as db} :db :as cofx} sheet-opts]
(let [{:keys [gas gasPrice maxFeePerGas maxPriorityFeePerGas]} tx (let [{:keys [gas gasPrice maxFeePerGas maxPriorityFeePerGas]} tx
max-fee (money/to-fixed (money/wei->gwei maxFeePerGas)) max-fee (money/to-fixed (money/wei->gwei
max-priority-fee (money/to-fixed (money/wei->gwei maxPriorityFeePerGas)) maxFeePerGas))
edit-fee (reduce (partial apply build-edit) max-priority-fee (money/to-fixed (money/wei->gwei
{:selected-fee-option (get-in db [:signing/edit-fee :selected-fee-option])} maxPriorityFeePerGas))
{:gas (money/to-fixed gas) edit-fee (reduce
:gasPrice (money/to-fixed (money/wei->gwei gasPrice)) (partial apply build-edit)
:maxFeePerGas max-fee {:selected-fee-option
:maxPriorityFeePerGas max-priority-fee})] (get-in db
(fx/merge cofx [:signing/edit-fee
:selected-fee-option])}
{:gas (money/to-fixed gas)
:gasPrice (money/to-fixed
(money/wei->gwei gasPrice))
:maxFeePerGas max-fee
:maxPriorityFeePerGas
max-priority-fee})]
(rf/merge cofx
{:db (assoc db :signing/edit-fee edit-fee)} {:db (assoc db :signing/edit-fee edit-fee)}
(bottom-sheet/show-bottom-sheet {:view sheet-opts})))) (bottom-sheet/show-bottom-sheet {:view sheet-opts}))))
(fx/defn submit-fee (rf/defn submit-fee
{:events [:signing.edit-fee.ui/submit]} {:events [:signing.edit-fee.ui/submit]}
[{{:signing/keys [edit-fee] :as db} :db :as cofx} force?] [{{:signing/keys [edit-fee] :as db} :db :as cofx} force?]
(let [{:keys [gas gasPrice maxFeePerGas maxPriorityFeePerGas]} edit-fee (let [{:keys [gas gasPrice maxFeePerGas maxPriorityFeePerGas]} edit-fee
@@ -267,18 +292,20 @@
(if (and (seq errors?) (if (and (seq errors?)
(not force?)) (not force?))
(popover/show-popover cofx {:view :fees-warning}) (popover/show-popover cofx {:view :fees-warning})
(fx/merge cofx (rf/merge cofx
{:db (update db :signing/tx assoc {:db (update db
:gas (:value-number gas) :signing/tx assoc
:gasPrice (:value-number gasPrice) :gas (:value-number gas)
:maxFeePerGas (money/->wei :gwei (:value-number maxFeePerGas)) :gasPrice (:value-number gasPrice)
:maxPriorityFeePerGas (money/->wei :gwei (:value-number maxPriorityFeePerGas)))} :maxFeePerGas (money/->wei :gwei (:value-number maxFeePerGas))
:maxPriorityFeePerGas (money/->wei :gwei
(:value-number maxPriorityFeePerGas)))}
(bottom-sheet/hide-bottom-sheet))))) (bottom-sheet/hide-bottom-sheet)))))
(fx/defn submit-nonce (rf/defn submit-nonce
{:events [:signing.nonce/submit]} {:events [:signing.nonce/submit]}
[{db :db :as cofx} nonce] [{db :db :as cofx} nonce]
(fx/merge cofx (rf/merge cofx
{:db (assoc-in db [:signing/tx :nonce] (if (string/blank? nonce) nil nonce))} {:db (assoc-in db [:signing/tx :nonce] (if (string/blank? nonce) nil nonce))}
(bottom-sheet/hide-bottom-sheet))) (bottom-sheet/hide-bottom-sheet)))
@@ -289,10 +316,10 @@
network-id network-id
(fn [] (fn []
(json-rpc/call (json-rpc/call
{:method "eth_feeHistory" {:method "eth_feeHistory"
;; NOTE(rasom): We don't need `reward` atm but if the last parameter is ;; NOTE(rasom): We don't need `reward` atm but if the last parameter is
;; `nil` request fails on some chains (particularly on xDai). ;; `nil` request fails on some chains (particularly on xDai).
:params [101 "latest" [100]] :params [101 "latest" [100]]
:on-success #(re-frame/dispatch [::header-fetched :on-success #(re-frame/dispatch [::header-fetched
(assoc params :fee-history %)])})) (assoc params :fee-history %)])}))
(fn [] (fn []
@@ -303,7 +330,8 @@
(def london-block-gas-limit (money/bignumber 30000000)) (def london-block-gas-limit (money/bignumber 30000000))
(defn calc-percentiles [v ps] (defn calc-percentiles
[v ps]
(let [sorted-v (sort-by (let [sorted-v (sort-by
identity identity
(fn [a b] (fn [a b]
@@ -319,7 +347,8 @@
;; fee might be very small and using this value might slow transaction ;; fee might be very small and using this value might slow transaction
(def minimum-base-fee (money/->wei :gwei (money/bignumber 1))) (def minimum-base-fee (money/->wei :gwei (money/bignumber 1)))
(defn recommended-base-fee [current perc20] (defn recommended-base-fee
[current perc20]
(let [fee (let [fee
(cond (money/greater-than-or-equals current perc20) (cond (money/greater-than-or-equals current perc20)
current current
@@ -332,13 +361,15 @@
(defn slow-base-fee [_ perc10] perc10) (defn slow-base-fee [_ perc10] perc10)
(defn fast-base-fee [current] (defn fast-base-fee
[current]
(let [fee (money/mul current 2)] (let [fee (money/mul current 2)]
(if (money/greater-than minimum-base-fee fee) (if (money/greater-than minimum-base-fee fee)
(money/mul minimum-base-fee 2) (money/mul minimum-base-fee 2)
fee))) fee)))
(defn check-base-fee [{:keys [baseFeePerGas testnet?]}] (defn check-base-fee
[{:keys [baseFeePerGas testnet?]}]
(let [all-base-fees (mapv money/bignumber baseFeePerGas) (let [all-base-fees (mapv money/bignumber baseFeePerGas)
next-base-fee (peek all-base-fees) next-base-fee (peek all-base-fees)
previous-fees (subvec all-base-fees 0 101) previous-fees (subvec all-base-fees 0 101)
@@ -357,12 +388,13 @@
:fast-base-fee (money/to-hex (fast-base-fee next-base-fee)) :fast-base-fee (money/to-hex (fast-base-fee next-base-fee))
:current-base-fee (money/to-hex current-base-fee)})) :current-base-fee (money/to-hex current-base-fee)}))
(defn max-priority-fee-hex [gas-price base-fee] (defn max-priority-fee-hex
[gas-price base-fee]
(money/to-hex (money/to-hex
(money/sub (money/bignumber gas-price) (money/sub (money/bignumber gas-price)
(money/bignumber base-fee)))) (money/bignumber base-fee))))
(fx/defn header-fetched (rf/defn header-fetched
{:events [::header-fetched]} {:events [::header-fetched]}
[{{:networks/keys [current-network networks]} :db} [{{:networks/keys [current-network networks]} :db}
{:keys [error-callback success-callback fee-history] :as params}] {:keys [error-callback success-callback fee-history] :as params}]
@@ -375,8 +407,9 @@
(let [{:keys [current-base-fee] :as base-fees} (let [{:keys [current-base-fee] :as base-fees}
(check-base-fee (check-base-fee
(assoc fee-history (assoc fee-history
:testnet? (ethereum/testnet? :testnet?
(get-in networks [current-network :config :NetworkId]))))] (ethereum/testnet?
(get-in networks [current-network :config :NetworkId]))))]
(merge {:max-priority-fee (merge {:max-priority-fee
(max-priority-fee-hex (money/bignumber %) current-base-fee)} (max-priority-fee-hex (money/bignumber %) current-base-fee)}
base-fees))) base-fees)))
@@ -390,5 +423,6 @@
(json-rpc/call (json-rpc/call
{:method "eth_estimateGas" {:method "eth_estimateGas"
:params [obj] :params [obj]
:on-success #(re-frame/dispatch [success-event (money/bignumber (if (= (int %) 21000) % (int (* % 1.2))))]) :on-success #(re-frame/dispatch [success-event
(money/bignumber (if (= (int %) 21000) % (int (* % 1.2))))])
:on-error #(re-frame/dispatch [error-event %])}))) :on-error #(re-frame/dispatch [error-event %])})))
+23 -18
View File
@@ -1,10 +1,10 @@
(ns status-im.signing.keycard (ns status-im.signing.keycard
(:require [re-frame.core :as re-frame] (:require [re-frame.core :as re-frame]
[status-im.native-module.core :as status]
[status-im.utils.fx :as fx]
[status-im.i18n.i18n :as i18n] [status-im.i18n.i18n :as i18n]
[status-im.native-module.core :as status]
[status-im.utils.types :as types] [status-im.utils.types :as types]
[taoensso.timbre :as log])) [taoensso.timbre :as log]
[utils.re-frame :as rf]))
(re-frame/reg-fx (re-frame/reg-fx
::hash-transaction ::hash-transaction
@@ -27,7 +27,7 @@
[{:keys [gas gasPrice data nonce tx-obj] :as params}] [{:keys [gas gasPrice data nonce tx-obj] :as params}]
(let [{:keys [from to value chat-id message-id command? maxPriorityFeePerGas maxFeePerGas]} tx-obj (let [{:keys [from to value chat-id message-id command? maxPriorityFeePerGas maxFeePerGas]} tx-obj
maxPriorityFeePerGas (or maxPriorityFeePerGas (get params :maxPriorityFeePerGas)) maxPriorityFeePerGas (or maxPriorityFeePerGas (get params :maxPriorityFeePerGas))
maxFeePerGas (or maxFeePerGas (get params :maxFeePerGas))] maxFeePerGas (or maxFeePerGas (get params :maxFeePerGas))]
(cond-> {:from from (cond-> {:from from
:to to :to to
:value value :value value
@@ -35,11 +35,15 @@
:message-id message-id :message-id message-id
:command? command?} :command? command?}
maxPriorityFeePerGas maxPriorityFeePerGas
(assoc :maxPriorityFeePerGas (str "0x" (status/number-to-hex (assoc :maxPriorityFeePerGas
(js/parseInt maxPriorityFeePerGas)))) (str "0x"
(status/number-to-hex
(js/parseInt maxPriorityFeePerGas))))
maxFeePerGas maxFeePerGas
(assoc :maxFeePerGas (str "0x" (status/number-to-hex (assoc :maxFeePerGas
(js/parseInt maxFeePerGas)))) (str "0x"
(status/number-to-hex
(js/parseInt maxFeePerGas))))
gas gas
(assoc :gas (str "0x" (status/number-to-hex gas))) (assoc :gas (str "0x" (status/number-to-hex gas)))
gasPrice gasPrice
@@ -49,7 +53,7 @@
nonce nonce
(assoc :nonce nonce)))) (assoc :nonce nonce))))
(fx/defn hash-message (rf/defn hash-message
[_ {:keys [v4 data typed? on-completed]}] [_ {:keys [v4 data typed? on-completed]}]
(if typed? (if typed?
{::hash-typed-data {::hash-typed-data
@@ -68,7 +72,7 @@
[:signing.keycard.callback/hash-message-completed [:signing.keycard.callback/hash-message-completed
data typed? %]))}})) data typed? %]))}}))
(fx/defn hash-message-completed (rf/defn hash-message-completed
{:events [:signing.keycard.callback/hash-message-completed]} {:events [:signing.keycard.callback/hash-message-completed]}
[{:keys [db]} data typed? result] [{:keys [db]} data typed? result]
(let [{:keys [result error]} (types/json->clj result)] (let [{:keys [result error]} (types/json->clj result)]
@@ -76,12 +80,13 @@
{:dispatch [:signing.ui/cancel-is-pressed] {:dispatch [:signing.ui/cancel-is-pressed]
:utils/show-popup {:title (i18n/label :t/sign-request-failed) :utils/show-popup {:title (i18n/label :t/sign-request-failed)
:content (:message error)}} :content (:message error)}}
{:db (update db :keycard assoc {:db (update db
:hash result :keycard assoc
:typed? typed? :hash result
:data data)}))) :typed? typed?
:data data)})))
(fx/defn hash-transaction (rf/defn hash-transaction
[{:keys [db]}] [{:keys [db]}]
(let [tx (prepare-transaction (:signing/tx db))] (let [tx (prepare-transaction (:signing/tx db))]
(log/debug "hash-transaction" tx) (log/debug "hash-transaction" tx)
@@ -90,7 +95,7 @@
:on-completed #(re-frame/dispatch :on-completed #(re-frame/dispatch
[:signing.keycard.callback/hash-transaction-completed tx %])}})) [:signing.keycard.callback/hash-transaction-completed tx %])}}))
(fx/defn hash-transaction-completed (rf/defn hash-transaction-completed
{:events [:signing.keycard.callback/hash-transaction-completed]} {:events [:signing.keycard.callback/hash-transaction-completed]}
[{:keys [db]} original-tx result] [{:keys [db]} original-tx result]
(let [{:keys [transaction hash]} (:result (types/json->clj result))] (let [{:keys [transaction hash]} (:result (types/json->clj result))]
@@ -99,11 +104,11 @@
(merge original-tx transaction)) (merge original-tx transaction))
(assoc-in [:keycard :hash] hash))})) (assoc-in [:keycard :hash] hash))}))
(fx/defn sign-with-keycard (rf/defn sign-with-keycard
{:events [:signing.ui/sign-with-keycard-pressed]} {:events [:signing.ui/sign-with-keycard-pressed]}
[{:keys [db] :as cofx}] [{:keys [db] :as cofx}]
(let [{:keys [message maxPriorityFeePerGas maxFeePerGas]} (get db :signing/tx)] (let [{:keys [message maxPriorityFeePerGas maxFeePerGas]} (get db :signing/tx)]
(fx/merge (rf/merge
cofx cofx
{:db (-> db {:db (-> db
(assoc-in [:keycard :pin :enter-step] :sign) (assoc-in [:keycard :pin :enter-step] :sign)
+25 -23
View File
@@ -1,13 +1,13 @@
(ns status-im.stickers.core (ns status-im.stickers.core
(:require [clojure.string :as string] (:require [clojure.string :as string]
[re-frame.core :as re-frame] [re-frame.core :as re-frame]
[status-im.constants :as constants]
[status-im.ethereum.core :as ethereum] [status-im.ethereum.core :as ethereum]
[status-im.ethereum.json-rpc :as json-rpc] [status-im.ethereum.json-rpc :as json-rpc]
[status-im2.navigation.events :as navigation]
[status-im.utils.fx :as fx]
[status-im.utils.utils :as utils]
[status-im.utils.config :as config] [status-im.utils.config :as config]
[status-im.constants :as constants])) [status-im.utils.utils :as utils]
[status-im2.navigation.events :as navigation]
[utils.re-frame :as rf]))
(re-frame/reg-fx (re-frame/reg-fx
:stickers/set-pending-timeout-fx :stickers/set-pending-timeout-fx
@@ -15,17 +15,17 @@
(utils/set-timeout #(re-frame/dispatch [:stickers/pending-timeout]) (utils/set-timeout #(re-frame/dispatch [:stickers/pending-timeout])
10000))) 10000)))
(fx/defn install-stickers-pack (rf/defn install-stickers-pack
{:events [:stickers/install-pack]} {:events [:stickers/install-pack]}
[{db :db :as cofx} id] [{db :db :as cofx} id]
(fx/merge (rf/merge
cofx cofx
{:db (assoc-in db [:stickers/packs id :status] constants/sticker-pack-status-installed) {:db (assoc-in db [:stickers/packs id :status] constants/sticker-pack-status-installed)
::json-rpc/call [{:method "stickers_install" ::json-rpc/call [{:method "stickers_install"
:params [(ethereum/chain-id db) id] :params [(ethereum/chain-id db) id]
:on-success #()}]})) :on-success #()}]}))
(fx/defn load-packs (rf/defn load-packs
{:events [:stickers/load-packs]} {:events [:stickers/load-packs]}
[{:keys [db]}] [{:keys [db]}]
{::json-rpc/call [{:method "stickers_market" {::json-rpc/call [{:method "stickers_market"
@@ -41,7 +41,7 @@
:params [] :params []
:on-success #(re-frame/dispatch [:stickers/stickers-recent-success %])}]}) :on-success #(re-frame/dispatch [:stickers/stickers-recent-success %])}]})
(fx/defn buy-pack (rf/defn buy-pack
{:events [:stickers/buy-pack]} {:events [:stickers/buy-pack]}
[{db :db} pack-id] [{db :db} pack-id]
{::json-rpc/call [{:method "stickers_buyPrepareTx" {::json-rpc/call [{:method "stickers_buyPrepareTx"
@@ -50,34 +50,36 @@
{:tx-obj % {:tx-obj %
:on-result [:stickers/pending-pack pack-id]}])}]}) :on-result [:stickers/pending-pack pack-id]}])}]})
(fx/defn pending-pack (rf/defn pending-pack
{:events [:stickers/pending-pack]} {:events [:stickers/pending-pack]}
[{db :db} id] [{db :db} id]
{:db (-> db {:db (-> db
(assoc-in [:stickers/packs id :status] constants/sticker-pack-status-pending) (assoc-in [:stickers/packs id :status]
constants/sticker-pack-status-pending)
(update :stickers/packs-pending conj id)) (update :stickers/packs-pending conj id))
:stickers/set-pending-timeout-fx nil :stickers/set-pending-timeout-fx nil
::json-rpc/call [{:method "stickers_addPending" ::json-rpc/call [{:method "stickers_addPending"
:params [(ethereum/chain-id db) (int id)] :params [(ethereum/chain-id db) (int id)]
:on-success #()}]}) :on-success #()}]})
(fx/defn pending-timeout (rf/defn pending-timeout
{:events [:stickers/pending-timeout]} {:events [:stickers/pending-timeout]}
[{{:stickers/keys [packs-pending] :as db} :db}] [{{:stickers/keys [packs-pending] :as db} :db}]
(when (seq packs-pending) (when (seq packs-pending)
{::json-rpc/call [{:method "stickers_processPending" {::json-rpc/call [{:method "stickers_processPending"
:params [(ethereum/chain-id db)] :params [(ethereum/chain-id db)]
:on-success #(re-frame/dispatch [:stickers/stickers-process-pending-success %])}]})) :on-success #(re-frame/dispatch [:stickers/stickers-process-pending-success
%])}]}))
(fx/defn stickers-process-pending-success (rf/defn stickers-process-pending-success
{:events [:stickers/stickers-process-pending-success]} {:events [:stickers/stickers-process-pending-success]}
[{{:stickers/keys [packs-pending packs] :as db} :db} purchased] [{{:stickers/keys [packs-pending packs] :as db} :db} purchased]
(let [purchased-ids (map :id (vals purchased)) (let [purchased-ids (map :id (vals purchased))
packs-pending (apply disj packs-pending purchased-ids) packs-pending (apply disj packs-pending purchased-ids)
packs (reduce (fn [packs id] packs (reduce (fn [packs id]
(assoc-in packs [id :status] constants/sticker-pack-status-owned)) (assoc-in packs [id :status] constants/sticker-pack-status-owned))
packs packs
purchased-ids)] purchased-ids)]
(merge (merge
{:db (-> db {:db (-> db
(assoc :stickers/packs packs) (assoc :stickers/packs packs)
@@ -85,19 +87,19 @@
(when (seq packs-pending) (when (seq packs-pending)
{:stickers/set-pending-timeout-fx nil})))) {:stickers/set-pending-timeout-fx nil}))))
(fx/defn stickers-market-success (rf/defn stickers-market-success
{:events [:stickers/stickers-market-success]} {:events [:stickers/stickers-market-success]}
[{:keys [db]} packs] [{:keys [db]} packs]
(let [packs (reduce (fn [acc pack] (assoc acc (:id pack) pack)) {} packs)] (let [packs (reduce (fn [acc pack] (assoc acc (:id pack) pack)) {} packs)]
{:db (update db :stickers/packs merge packs)})) {:db (update db :stickers/packs merge packs)}))
(fx/defn stickers-installed-success (rf/defn stickers-installed-success
{:events [:stickers/stickers-installed-success]} {:events [:stickers/stickers-installed-success]}
[{:keys [db]} packs] [{:keys [db]} packs]
(let [packs (reduce (fn [acc [_ pack]] (assoc acc (:id pack) pack)) {} packs)] (let [packs (reduce (fn [acc [_ pack]] (assoc acc (:id pack) pack)) {} packs)]
{:db (update db :stickers/packs merge packs)})) {:db (update db :stickers/packs merge packs)}))
(fx/defn stickers-pending-success (rf/defn stickers-pending-success
{:events [:stickers/stickers-pending-success]} {:events [:stickers/stickers-pending-success]}
[{:keys [db]} packs] [{:keys [db]} packs]
(let [packs (reduce (fn [acc [_ pack]] (assoc acc (:id pack) pack)) {} packs)] (let [packs (reduce (fn [acc [_ pack]] (assoc acc (:id pack) pack)) {} packs)]
@@ -108,18 +110,18 @@
(when (seq packs) (when (seq packs)
{:stickers/set-pending-timeout-fx nil})))) {:stickers/set-pending-timeout-fx nil}))))
(fx/defn stickers-recent-success (rf/defn stickers-recent-success
{:events [:stickers/stickers-recent-success]} {:events [:stickers/stickers-recent-success]}
[{:keys [db]} packs] [{:keys [db]} packs]
{:db (assoc db :stickers/recent-stickers packs)}) {:db (assoc db :stickers/recent-stickers packs)})
(fx/defn open-sticker-pack (rf/defn open-sticker-pack
{:events [:stickers/open-sticker-pack]} {:events [:stickers/open-sticker-pack]}
[{{:networks/keys [current-network]} :db :as cofx} id] [{{:networks/keys [current-network]} :db :as cofx} id]
(when (and id (or config/stickers-test-enabled? (string/starts-with? current-network "mainnet"))) (when (and id (or config/stickers-test-enabled? (string/starts-with? current-network "mainnet")))
(navigation/open-modal cofx :stickers-pack {:id id}))) (navigation/open-modal cofx :stickers-pack {:id id})))
(fx/defn select-pack (rf/defn select-pack
{:events [:stickers/select-pack]} {:events [:stickers/select-pack]}
[{:keys [db]} id] [{:keys [db]} id]
{:db (assoc db :stickers/selected-pack id)}) {:db (assoc db :stickers/selected-pack id)})
+45 -20
View File
@@ -1,25 +1,30 @@
(ns status-im.test-runner (ns status-im.test-runner
{:dev/always true} {:dev/always true}
(:require (:require [cljs.test :as ct]
[cljs.test :as ct] [clojure.string :as string]
[clojure.string :as string] [shadow.test :as st]
[shadow.test :as st] [shadow.test.env :as env]
[shadow.test.env :as env])) [status-im.ethereum.json-rpc :as json-rpc]
[utils.re-frame :as rf]))
(defonce repl? (atom false)) (defonce repl? (atom false))
(defmethod ct/report [::ct/default :end-run-tests] [m] (defmethod ct/report [::ct/default :end-run-tests]
[m]
(when-not @repl? (when-not @repl?
(if (ct/successful? m) (if (ct/successful? m)
(js/process.exit 0) (js/process.exit 0)
(js/process.exit 1)))) (js/process.exit 1))))
;; get-test-data is a macro so this namespace REQUIRES :dev/always hint ns so that it is always recompiled ;; get-test-data is a macro so this namespace REQUIRES :dev/always hint ns so that it is always
(defn ^:dev/after-load reset-test-data! [] ;; recompiled
(defn ^:dev/after-load reset-test-data!
[]
(-> (env/get-test-data) (-> (env/get-test-data)
(env/reset-test-data!))) (env/reset-test-data!)))
(defn parse-args [args] (defn parse-args
[args]
(reduce (reduce
(fn [opts arg] (fn [opts arg]
(cond (cond
@@ -33,7 +38,7 @@
(assoc opts :repl true) (assoc opts :repl true)
(string/starts-with? arg "--test=") (string/starts-with? arg "--test=")
(let [test-arg (subs arg 7) (let [test-arg (subs arg 7)
test-syms test-syms
(->> (string/split test-arg ",") (->> (string/split test-arg ",")
(map symbol))] (map symbol))]
@@ -45,12 +50,17 @@
{:test-syms []} {:test-syms []}
args)) args))
(defn find-matching-test-vars [test-syms] (defn find-matching-test-vars
[test-syms]
;; FIXME: should have some kind of wildcard support ;; FIXME: should have some kind of wildcard support
(let [test-namespaces (let [test-namespaces
(->> test-syms (filter simple-symbol?) (set)) (->> test-syms
(filter simple-symbol?)
(set))
test-var-syms test-var-syms
(->> test-syms (filter qualified-symbol?) (set))] (->> test-syms
(filter qualified-symbol?)
(set))]
(->> (env/get-test-vars) (->> (env/get-test-vars)
(filter (fn [the-var] (filter (fn [the-var]
@@ -58,7 +68,8 @@
(or (contains? test-namespaces ns) (or (contains? test-namespaces ns)
(contains? test-var-syms (symbol ns name))))))))) (contains? test-var-syms (symbol ns name)))))))))
(defn execute-cli [{:keys [test-syms help list repl] :as _opts}] (defn execute-cli
[{:keys [test-syms help list repl] :as _opts}]
(let [test-env (let [test-env
(-> (ct/empty-env) (-> (ct/empty-env)
;; can't think of a proper way to let CLI specify custom reporter? ;; can't think of a proper way to let CLI specify custom reporter?
@@ -70,17 +81,19 @@
(cond (cond
help help
(do (println "Usage:") (do
(println " --list (list known test names)") (println "Usage:")
(println " --test=<ns-to-test>,<fqn-symbol-to-test> (run test for namespace or single var, separated by comma)") (println " --list (list known test names)")
(println " --repl (start node without automatically running tests)")) (println
" --test=<ns-to-test>,<fqn-symbol-to-test> (run test for namespace or single var, separated by comma)")
(println " --repl (start node without automatically running tests)"))
list list
(doseq [[ns ns-info] (doseq [[ns ns-info]
(->> (env/get-tests) (->> (env/get-tests)
(sort-by first))] (sort-by first))]
(println "Namespace:" ns) (println "Namespace:" ns)
(doseq [var (:vars ns-info) (doseq [var (:vars ns-info)
:let [m (meta var)]] :let [m (meta var)]]
(println (str " " (:ns m) "/" (:name m)))) (println (str " " (:ns m) "/" (:name m))))
(println "---------------------------------")) (println "---------------------------------"))
@@ -97,8 +110,20 @@
:else :else
(st/run-all-tests test-env nil)))) (st/run-all-tests test-env nil))))
(defn ^:export main [& args] (defn ^:export main
[& args]
(reset-test-data!) (reset-test-data!)
(rf/set-mergeable-keys #{:filters/load-filters
:pairing/set-installation-metadata
:dispatch-n
:status-im.ens.core/verify-names
:shh/send-direct-message
:shh/remove-filter
:transport/confirm-messages-processed
:group-chats/extract-membership-signature
:utils/dispatch-later
::json-rpc/call})
(let [opts (parse-args args)] (let [opts (parse-args args)]
(execute-cli opts))) (execute-cli opts)))
+16 -16
View File
@@ -1,23 +1,23 @@
(ns ^{:doc "API to init and stop whisper messaging"} (ns ^{:doc "API to init and stop whisper messaging"} status-im.transport.core
status-im.transport.core
(:require [re-frame.core :as re-frame] (:require [re-frame.core :as re-frame]
[status-im.ethereum.json-rpc :as json-rpc] [status-im.ethereum.json-rpc :as json-rpc]
[status-im.pairing.core :as pairing] [status-im.pairing.core :as pairing]
[status-im.utils.fx :as fx] [status-im.stickers.core :as stickers]
status-im.transport.shh status-im.transport.shh
[taoensso.timbre :as log]
[status-im.utils.universal-links.core :as universal-links] [status-im.utils.universal-links.core :as universal-links]
[status-im.stickers.core :as stickers])) [taoensso.timbre :as log]
[utils.re-frame :as rf]))
(fx/defn set-node-info (rf/defn set-node-info
{:events [:transport.callback/node-info-fetched]} {:events [:transport.callback/node-info-fetched]}
[{:keys [db]} node-info] [{:keys [db]} node-info]
{:db (assoc db :node-info node-info)}) {:db (assoc db :node-info node-info)})
(fx/defn fetch-node-info-fx [_] (rf/defn fetch-node-info-fx
{::json-rpc/call [{:method "admin_nodeInfo" [_]
{::json-rpc/call [{:method "admin_nodeInfo"
:on-success #(re-frame/dispatch [:transport.callback/node-info-fetched %]) :on-success #(re-frame/dispatch [:transport.callback/node-info-fetched %])
:on-error #(log/error "node-info: failed error" %)}]}) :on-error #(log/error "node-info: failed error" %)}]})
(defn add-mailservers (defn add-mailservers
[db mailservers] [db mailservers]
@@ -28,25 +28,25 @@
(assoc :name (if (seq name) name id)) (assoc :name (if (seq name) name id))
(dissoc :fleet))] (dissoc :fleet))]
(assoc-in db (assoc-in db
[:mailserver/mailservers (keyword fleet) (keyword id)] [:mailserver/mailservers (keyword fleet) (keyword id)]
updated-mailserver))) updated-mailserver)))
db db
mailservers)) mailservers))
(fx/defn start-messenger (rf/defn start-messenger
"We should only start receiving messages/processing topics once all the "We should only start receiving messages/processing topics once all the
initializiation is completed, otherwise we might receive messages/topics initializiation is completed, otherwise we might receive messages/topics
when the state has not been properly initialized." when the state has not been properly initialized."
[_] [_]
{::json-rpc/call [{:method "wakuext_startMessenger" {::json-rpc/call [{:method "wakuext_startMessenger"
:on-success #(re-frame/dispatch [::messenger-started %]) :on-success #(re-frame/dispatch [::messenger-started %])
:on-error #(log/error "failed to start messenger")}]}) :on-error #(log/error "failed to start messenger")}]})
(fx/defn messenger-started (rf/defn messenger-started
{:events [::messenger-started]} {:events [::messenger-started]}
[{:keys [db] :as cofx} {:keys [mailservers] :as response}] [{:keys [db] :as cofx} {:keys [mailservers] :as response}]
(log/info "Messenger started") (log/info "Messenger started")
(fx/merge cofx (rf/merge cofx
{:db (-> db {:db (-> db
(assoc :messenger/started? true) (assoc :messenger/started? true)
(add-mailservers mailservers))} (add-mailservers mailservers))}
+98 -89
View File
@@ -1,37 +1,36 @@
(ns ^{:doc "Definition of the StatusMessage protocol"} (ns ^{:doc "Definition of the StatusMessage protocol"} status-im.transport.message.core
status-im.transport.message.core (:require [clojure.string :as string]
(:require [status-im2.contexts.activity-center.events :as activity-center]
[status-im.chat.models.message :as models.message]
[status-im2.contexts.chat.messages.pin.events :as messages.pin]
[status-im.chat.models :as models.chat]
[status-im.chat.models.reactions :as models.reactions]
[status-im.contact.core :as models.contact]
[status-im.communities.core :as models.communities]
[status-im.pairing.core :as models.pairing]
[status-im.data-store.reactions :as data-store.reactions]
[status-im.data-store.contacts :as data-store.contacts]
[status-im.data-store.chats :as data-store.chats]
[status-im.data-store.invitations :as data-store.invitations]
[status-im.data-store.activities :as data-store.activities]
[status-im.data-store.messages :as data-store.messages]
[status-im.group-chats.core :as models.group]
[status-im.multiaccounts.update.core :as update.core]
[status-im.utils.fx :as fx]
[status-im.utils.types :as types]
[status-im.constants :as constants]
[status-im.multiaccounts.model :as multiaccounts.model]
[status-im.multiaccounts.login.core :as multiaccounts.login]
[status-im.visibility-status-updates.core :as models.visibility-status-updates]
[status-im.browser.core :as browser] [status-im.browser.core :as browser]
[clojure.string :as string])) [status-im.chat.models :as models.chat]
[status-im.chat.models.message :as models.message]
[status-im.chat.models.reactions :as models.reactions]
[status-im.communities.core :as models.communities]
[status-im.constants :as constants]
[status-im.contact.core :as models.contact]
[status-im.data-store.activities :as data-store.activities]
[status-im.data-store.chats :as data-store.chats]
[status-im.data-store.contacts :as data-store.contacts]
[status-im.data-store.invitations :as data-store.invitations]
[status-im.data-store.messages :as data-store.messages]
[status-im.data-store.reactions :as data-store.reactions]
[status-im.group-chats.core :as models.group]
[status-im.multiaccounts.login.core :as multiaccounts.login]
[status-im.multiaccounts.model :as multiaccounts.model]
[status-im.multiaccounts.update.core :as update.core]
[status-im.pairing.core :as models.pairing]
[status-im.utils.types :as types]
[status-im.visibility-status-updates.core :as models.visibility-status-updates]
[status-im2.contexts.activity-center.events :as activity-center]
[status-im2.contexts.chat.messages.pin.events :as messages.pin]
[utils.re-frame :as rf]))
(fx/defn process-next (rf/defn process-next
[cofx ^js response-js sync-handler] [cofx ^js response-js sync-handler]
(if sync-handler (if sync-handler
(sync-handler cofx response-js true) (sync-handler cofx response-js true)
{:utils/dispatch-later [{:ms 20 :dispatch [:process-response response-js]}]})) {:utils/dispatch-later [{:ms 20 :dispatch [:process-response response-js]}]}))
(fx/defn process-response (rf/defn process-response
{:events [:process-response]} {:events [:process-response]}
[{:keys [db] :as cofx} ^js response-js process-async] [{:keys [db] :as cofx} ^js response-js process-async]
(let [^js communities (.-communities response-js) (let [^js communities (.-communities response-js)
@@ -59,7 +58,7 @@
(seq chats) (seq chats)
(do (do
(js-delete response-js "chats") (js-delete response-js "chats")
(fx/merge cofx (rf/merge cofx
(process-next response-js sync-handler) (process-next response-js sync-handler)
(models.chat/ensure-chats (map data-store.chats/<-rpc (types/js->clj chats))))) (models.chat/ensure-chats (map data-store.chats/<-rpc (types/js->clj chats)))))
@@ -69,7 +68,7 @@
(seq activity-notifications) (seq activity-notifications)
(do (do
(js-delete response-js "activityCenterNotifications") (js-delete response-js "activityCenterNotifications")
(fx/merge cofx (rf/merge cofx
(->> activity-notifications (->> activity-notifications
types/js->clj types/js->clj
(map data-store.activities/<-rpc) (map data-store.activities/<-rpc)
@@ -79,86 +78,87 @@
(seq installations) (seq installations)
(let [installations-clj (types/js->clj installations)] (let [installations-clj (types/js->clj installations)]
(js-delete response-js "installations") (js-delete response-js "installations")
(fx/merge cofx (rf/merge cofx
(process-next response-js sync-handler) (process-next response-js sync-handler)
(models.pairing/handle-installations installations-clj))) (models.pairing/handle-installations installations-clj)))
(seq contacts) (seq contacts)
(let [contacts-clj (types/js->clj contacts) (let [contacts-clj (types/js->clj contacts)
^js chats (.-chatsForContacts response-js)] ^js chats (.-chatsForContacts response-js)]
(js-delete response-js "contacts") (js-delete response-js "contacts")
(js-delete response-js "chatsForContacts") (js-delete response-js "chatsForContacts")
(fx/merge cofx (rf/merge cofx
(process-next response-js sync-handler) (process-next response-js sync-handler)
(models.contact/ensure-contacts (models.contact/ensure-contacts
(map data-store.contacts/<-rpc contacts-clj) chats))) (map data-store.contacts/<-rpc contacts-clj)
chats)))
(seq communities) (seq communities)
(let [communities-clj (types/js->clj communities)] (let [communities-clj (types/js->clj communities)]
(js-delete response-js "communities") (js-delete response-js "communities")
(fx/merge cofx (rf/merge cofx
(process-next response-js sync-handler) (process-next response-js sync-handler)
(models.communities/handle-communities communities-clj))) (models.communities/handle-communities communities-clj)))
(seq bookmarks) (seq bookmarks)
(let [bookmarks-clj (types/js->clj bookmarks)] (let [bookmarks-clj (types/js->clj bookmarks)]
(js-delete response-js "bookmarks") (js-delete response-js "bookmarks")
(fx/merge cofx (rf/merge cofx
(process-next response-js sync-handler) (process-next response-js sync-handler)
(browser/handle-bookmarks bookmarks-clj))) (browser/handle-bookmarks bookmarks-clj)))
(seq pin-messages) (seq pin-messages)
(let [pin-messages (types/js->clj pin-messages)] (let [pin-messages (types/js->clj pin-messages)]
(js-delete response-js "pinMessages") (js-delete response-js "pinMessages")
(fx/merge cofx (rf/merge cofx
(process-next response-js sync-handler) (process-next response-js sync-handler)
(messages.pin/receive-signal (map data-store.messages/<-rpc pin-messages)))) (messages.pin/receive-signal (map data-store.messages/<-rpc pin-messages))))
(seq removed-chats) (seq removed-chats)
(let [removed-chats-clj (types/js->clj removed-chats)] (let [removed-chats-clj (types/js->clj removed-chats)]
(js-delete response-js "removedChats") (js-delete response-js "removedChats")
(fx/merge cofx (rf/merge cofx
(process-next response-js sync-handler) (process-next response-js sync-handler)
(models.communities/handle-removed-chats removed-chats-clj))) (models.communities/handle-removed-chats removed-chats-clj)))
(seq requests-to-join-community) (seq requests-to-join-community)
(let [request-js (types/js->clj (.pop requests-to-join-community))] (let [request-js (types/js->clj (.pop requests-to-join-community))]
(fx/merge cofx (rf/merge cofx
(process-next response-js sync-handler) (process-next response-js sync-handler)
(models.communities/handle-request-to-join request-js))) (models.communities/handle-request-to-join request-js)))
(seq emoji-reactions) (seq emoji-reactions)
(let [reactions (types/js->clj emoji-reactions)] (let [reactions (types/js->clj emoji-reactions)]
(js-delete response-js "emojiReactions") (js-delete response-js "emojiReactions")
(fx/merge cofx (rf/merge cofx
(process-next response-js sync-handler) (process-next response-js sync-handler)
(models.reactions/receive-signal (map data-store.reactions/<-rpc reactions)))) (models.reactions/receive-signal (map data-store.reactions/<-rpc reactions))))
(seq invitations) (seq invitations)
(let [invitations (types/js->clj invitations)] (let [invitations (types/js->clj invitations)]
(js-delete response-js "invitations") (js-delete response-js "invitations")
(fx/merge cofx (rf/merge cofx
(process-next response-js sync-handler) (process-next response-js sync-handler)
(models.group/handle-invitations (map data-store.invitations/<-rpc invitations)))) (models.group/handle-invitations (map data-store.invitations/<-rpc invitations))))
(seq removed-messages) (seq removed-messages)
(let [removed-messages-clj (types/js->clj removed-messages)] (let [removed-messages-clj (types/js->clj removed-messages)]
(js-delete response-js "removedMessages") (js-delete response-js "removedMessages")
(fx/merge cofx (rf/merge cofx
(process-next response-js sync-handler) (process-next response-js sync-handler)
(models.message/handle-removed-messages removed-messages-clj))) (models.message/handle-removed-messages removed-messages-clj)))
(seq cleared-histories) (seq cleared-histories)
(let [cleared-histories-clj (types/js->clj cleared-histories)] (let [cleared-histories-clj (types/js->clj cleared-histories)]
(js-delete response-js "clearedHistories") (js-delete response-js "clearedHistories")
(fx/merge cofx (rf/merge cofx
(process-next response-js sync-handler) (process-next response-js sync-handler)
(models.message/handle-cleared-histories-messages cleared-histories-clj))) (models.message/handle-cleared-histories-messages cleared-histories-clj)))
(seq visibility-status-updates) (seq visibility-status-updates)
(let [visibility-status-updates-clj (types/js->clj visibility-status-updates)] (let [visibility-status-updates-clj (types/js->clj visibility-status-updates)]
(js-delete response-js "statusUpdates") (js-delete response-js "statusUpdates")
(fx/merge cofx (rf/merge cofx
(process-next response-js sync-handler) (process-next response-js sync-handler)
(models.visibility-status-updates/handle-visibility-status-updates (models.visibility-status-updates/handle-visibility-status-updates
visibility-status-updates-clj))) visibility-status-updates-clj)))
@@ -166,28 +166,28 @@
(seq accounts) (seq accounts)
(do (do
(js-delete response-js "accounts") (js-delete response-js "accounts")
(fx/merge cofx (rf/merge cofx
(process-next response-js sync-handler) (process-next response-js sync-handler)
(multiaccounts.login/update-wallet-accounts (types/js->clj accounts)))) (multiaccounts.login/update-wallet-accounts (types/js->clj accounts))))
(seq settings) (seq settings)
(do (do
(js-delete response-js "settings") (js-delete response-js "settings")
(fx/merge cofx (rf/merge cofx
(process-next response-js sync-handler) (process-next response-js sync-handler)
(update.core/set-many-js settings))) (update.core/set-many-js settings)))
(seq identity-images) (seq identity-images)
(let [images-clj (map types/js->clj identity-images)] (let [images-clj (map types/js->clj identity-images)]
(js-delete response-js "identityImages") (js-delete response-js "identityImages")
(fx/merge cofx (rf/merge cofx
(process-next response-js sync-handler) (process-next response-js sync-handler)
(update.core/optimistic :images images-clj))) (update.core/optimistic :images images-clj)))
(some? current-visibility-status) (some? current-visibility-status)
(let [current-visibility-status-clj (types/js->clj current-visibility-status)] (let [current-visibility-status-clj (types/js->clj current-visibility-status)]
(js-delete response-js "currentStatus") (js-delete response-js "currentStatus")
(fx/merge cofx (rf/merge cofx
(process-next response-js sync-handler) (process-next response-js sync-handler)
(models.visibility-status-updates/sync-visibility-status-update (models.visibility-status-updates/sync-visibility-status-update
current-visibility-status-clj)))))) current-visibility-status-clj))))))
@@ -195,19 +195,21 @@
(defn group-by-and-update-unviewed-counts (defn group-by-and-update-unviewed-counts
"group messages by current chat, profile updates, transactions and update unviewed counters in db for not curent chats" "group messages by current chat, profile updates, transactions and update unviewed counters in db for not curent chats"
[{:keys [current-chat-id db] :as acc} ^js message-js] [{:keys [current-chat-id db] :as acc} ^js message-js]
(let [chat-id (.-localChatId message-js) (let [chat-id (.-localChatId message-js)
message-type (.-messageType message-js) message-type (.-messageType message-js)
from (.-from message-js) from (.-from message-js)
mentioned (.-mentioned message-js) mentioned (.-mentioned message-js)
profile (models.chat/profile-chat? {:db db} chat-id) profile (models.chat/profile-chat? {:db db} chat-id)
new (.-new message-js) new (.-new message-js)
current (= current-chat-id chat-id) current (= current-chat-id chat-id)
should-update-unviewed? (and (not current) should-update-unviewed? (and (not current)
new new
(not profile) (not profile)
(not (= message-type constants/message-type-private-group-system-message)) (not (= message-type
constants/message-type-private-group-system-message))
(not (= from (multiaccounts.model/current-public-key {:db db})))) (not (= from (multiaccounts.model/current-public-key {:db db}))))
tx-hash (and (.-commandParameters message-js) (.-commandParameters.transactionHash message-js))] tx-hash (and (.-commandParameters message-js)
(.-commandParameters.transactionHash message-js))]
(cond-> acc (cond-> acc
current current
(update :messages conj message-js) (update :messages conj message-js)
@@ -235,46 +237,52 @@
[response-js messages] [response-js messages]
(if (seq messages) (if (seq messages)
(set! (.-messages response-js) (set! (.-messages response-js)
(.sort (to-array messages) (.sort (to-array messages)
(fn [a b] (fn [a b]
(- (.-clock b) (.-clock a))))) (- (.-clock b) (.-clock a)))))
(js-delete response-js "messages"))) (js-delete response-js "messages")))
(fx/defn sanitize-messages-and-process-response (rf/defn sanitize-messages-and-process-response
"before processing we want to filter and sort messages, so we can process first only messages which will be showed" "before processing we want to filter and sort messages, so we can process first only messages which will be showed"
{:events [:sanitize-messages-and-process-response]} {:events [:sanitize-messages-and-process-response]}
[{:keys [db] :as cofx} ^js response-js process-async] [{:keys [db] :as cofx} ^js response-js process-async]
(when response-js (when response-js
(let [current-chat-id (:current-chat-id db) (let [current-chat-id (:current-chat-id db)
{:keys [db messages transactions chats statuses]} {:keys [db messages transactions chats statuses]}
(reduce group-by-and-update-unviewed-counts (reduce group-by-and-update-unviewed-counts
{:db db :chats #{} :transactions #{} :statuses [] :messages [] {:db db
:chats #{}
:transactions #{}
:statuses []
:messages []
:current-chat-id current-chat-id} :current-chat-id current-chat-id}
(.-messages response-js))] (.-messages response-js))]
(sort-js-messages! response-js messages) (sort-js-messages! response-js messages)
(fx/merge cofx (rf/merge cofx
{:db db {:db db
:utils/dispatch-later (concat [] :utils/dispatch-later (concat []
(when (seq statuses) (when (seq statuses)
[{:ms 100 :dispatch [:process-statuses statuses]}]) [{:ms 100 :dispatch [:process-statuses statuses]}])
(when (seq transactions) (when (seq transactions)
(for [transaction-hash transactions] (for [transaction-hash transactions]
{:ms 100 :dispatch [:watch-tx nil transaction-hash]})))} {:ms 100
:dispatch [:watch-tx nil transaction-hash]})))}
(process-response response-js process-async))))) (process-response response-js process-async)))))
(fx/defn remove-hash (rf/defn remove-hash
[{:keys [db]} envelope-hash] [{:keys [db]} envelope-hash]
{:db (update db :transport/message-envelopes dissoc envelope-hash)}) {:db (update db :transport/message-envelopes dissoc envelope-hash)})
(fx/defn check-confirmations (rf/defn check-confirmations
[{:keys [db] :as cofx} status chat-id message-id] [{:keys [db] :as cofx} status chat-id message-id]
(when-let [{:keys [pending-confirmations not-sent]} (when-let [{:keys [pending-confirmations not-sent]}
(get-in db [:transport/message-ids->confirmations message-id])] (get-in db [:transport/message-ids->confirmations message-id])]
(if (zero? (dec pending-confirmations)) (if (zero? (dec pending-confirmations))
(fx/merge cofx (rf/merge cofx
{:db (update db {:db (update db
:transport/message-ids->confirmations :transport/message-ids->confirmations
dissoc message-id)} dissoc
message-id)}
(models.message/update-message-status chat-id (models.message/update-message-status chat-id
message-id message-id
(if not-sent (if not-sent
@@ -282,13 +290,13 @@
status)) status))
(remove-hash message-id)) (remove-hash message-id))
(let [confirmations {:pending-confirmations (dec pending-confirmations) (let [confirmations {:pending-confirmations (dec pending-confirmations)
:not-sent (or not-sent :not-sent (or not-sent
(= :not-sent status))}] (= :not-sent status))}]
{:db (assoc-in db {:db (assoc-in db
[:transport/message-ids->confirmations message-id] [:transport/message-ids->confirmations message-id]
confirmations)})))) confirmations)}))))
(fx/defn update-envelope-status (rf/defn update-envelope-status
[{:keys [db] :as cofx} message-id status] [{:keys [db] :as cofx} message-id status]
(if-let [{:keys [chat-id]} (if-let [{:keys [chat-id]}
(get-in db [:transport/message-envelopes message-id])] (get-in db [:transport/message-envelopes message-id])]
@@ -298,37 +306,38 @@
;; came too early ;; came too early
{:db (update-in db [:transport/message-confirmations message-id] conj status)})) {:db (update-in db [:transport/message-confirmations message-id] conj status)}))
(fx/defn update-envelopes-status (rf/defn update-envelopes-status
[{:keys [db] :as cofx} message-id status] [{:keys [db] :as cofx} message-id status]
(when (or (not= status :not-sent) (when (or (not= status :not-sent)
(= :online (:network db))) (= :online (:network db)))
(apply fx/merge cofx (map #(update-envelope-status % status) message-id)))) (apply rf/merge cofx (map #(update-envelope-status % status) message-id))))
(fx/defn set-message-envelope-hash (rf/defn set-message-envelope-hash
"message-type is used for tracking" "message-type is used for tracking"
[{:keys [db] :as cofx} chat-id message-id message-type] [{:keys [db] :as cofx} chat-id message-id message-type]
;; Check first if the confirmation has already arrived ;; Check first if the confirmation has already arrived
(let [statuses (get-in db [:transport/message-confirmations message-id]) (let [statuses (get-in db [:transport/message-confirmations message-id])
check-confirmations-fx (map check-confirmations-fx (map
#(check-confirmations % chat-id message-id) #(check-confirmations % chat-id message-id)
statuses) statuses)
add-envelope-data (fn [{:keys [db]}] add-envelope-data (fn [{:keys [db]}]
{:db (-> db {:db (-> db
(update :transport/message-confirmations dissoc message-id) (update :transport/message-confirmations dissoc message-id)
(assoc-in [:transport/message-envelopes message-id] (assoc-in [:transport/message-envelopes message-id]
{:chat-id chat-id {:chat-id chat-id
:message-type message-type}) :message-type message-type})
(update-in [:transport/message-ids->confirmations message-id] (update-in [:transport/message-ids->confirmations message-id]
#(or % {:pending-confirmations 1})))})] #(or % {:pending-confirmations 1})))})]
(apply fx/merge cofx (conj check-confirmations-fx add-envelope-data)))) (apply rf/merge cofx (conj check-confirmations-fx add-envelope-data))))
(fx/defn transport-message-sent (rf/defn transport-message-sent
{:events [:transport/message-sent]} {:events [:transport/message-sent]}
[cofx response-js] [cofx response-js]
(let [set-hash-fxs (map (fn [{:keys [localChatId id messageType]}] (let [set-hash-fxs (map (fn [{:keys [localChatId id messageType]}]
(set-message-envelope-hash localChatId id messageType)) (set-message-envelope-hash localChatId id messageType))
(types/js->clj (.-messages response-js)))] (types/js->clj (.-messages response-js)))]
(apply fx/merge cofx (apply rf/merge
cofx
(conj set-hash-fxs (conj set-hash-fxs
#(sanitize-messages-and-process-response % response-js false))))) #(sanitize-messages-and-process-response % response-js false)))))
+34 -31
View File
@@ -1,20 +1,20 @@
(ns ^{:doc "Protocol API and protocol utils"} (ns ^{:doc "Protocol API and protocol utils"} status-im.transport.message.protocol
status-im.transport.message.protocol
(:require [re-frame.core :as re-frame] (:require [re-frame.core :as re-frame]
[status-im.ethereum.json-rpc :as json-rpc] [status-im.ethereum.json-rpc :as json-rpc]
[status-im.utils.fx :as fx] [taoensso.timbre :as log]
[taoensso.timbre :as log])) [utils.re-frame :as rf]))
(defn build-message [{:keys [chat-id (defn build-message
text [{:keys [chat-id
response-to text
ens-name response-to
community-id ens-name
image-path community-id
audio-path image-path
audio-duration-ms audio-path
sticker audio-duration-ms
content-type]}] sticker
content-type]}]
{:chatId chat-id {:chatId chat-id
:text text :text text
:responseTo response-to :responseTo response-to
@@ -26,25 +26,28 @@
:sticker sticker :sticker sticker
:contentType content-type}) :contentType content-type})
(fx/defn send-chat-messages [_ messages] (rf/defn send-chat-messages
{::json-rpc/call [{:method "wakuext_sendChatMessages" [_ messages]
:params [(mapv build-message messages)] {::json-rpc/call [{:method "wakuext_sendChatMessages"
:params [(mapv build-message messages)]
:js-response true :js-response true
:on-success #(re-frame/dispatch [:transport/message-sent %]) :on-success #(re-frame/dispatch [:transport/message-sent %])
:on-error #(do :on-error #(do
(log/warn "failed to send a message" %) (log/warn "failed to send a message" %)
(js/alert (str "failed to send a message: " %)))}]}) (js/alert (str "failed to send a message: " %)))}]})
(fx/defn send-reaction [_ {:keys [message-id chat-id emoji-id]}] (rf/defn send-reaction
{::json-rpc/call [{:method "wakuext_sendEmojiReaction" [_ {:keys [message-id chat-id emoji-id]}]
:params [chat-id message-id emoji-id] {::json-rpc/call [{:method "wakuext_sendEmojiReaction"
:params [chat-id message-id emoji-id]
:js-response true :js-response true
:on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %]) :on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %])
:on-error #(log/error "failed to send a reaction" %)}]}) :on-error #(log/error "failed to send a reaction" %)}]})
(fx/defn send-retract-reaction [_ {:keys [emoji-reaction-id]}] (rf/defn send-retract-reaction
{::json-rpc/call [{:method "wakuext_sendEmojiReactionRetraction" [_ {:keys [emoji-reaction-id]}]
:params [emoji-reaction-id] {::json-rpc/call [{:method "wakuext_sendEmojiReactionRetraction"
:params [emoji-reaction-id]
:js-response true :js-response true
:on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %]) :on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %])
:on-error #(log/error "failed to send a reaction retraction" %)}]}) :on-error #(log/error "failed to send a reaction retraction" %)}]})
@@ -1,20 +1,22 @@
(ns status-im.ui.components.invite.events (ns status-im.ui.components.invite.events
(:require [re-frame.core :as re-frame] (:require [re-frame.core :as re-frame]
[status-im.utils.fx :as fx]
[status-im.i18n.i18n :as i18n] [status-im.i18n.i18n :as i18n]
[status-im.ui.components.react :as react] [status-im.ui.components.react :as react]
[status-im.utils.universal-links.utils :as universal-links])) [status-im.utils.universal-links.utils :as universal-links]
[utils.re-frame :as rf]))
(re-frame/reg-fx (re-frame/reg-fx
::share ::share
(fn [content] (fn [content]
(.share ^js react/sharing (clj->js content)))) (.share ^js react/sharing (clj->js content))))
(fx/defn share-link (rf/defn share-link
{:events [::share-link]} {:events [::share-link]}
[{:keys [db]}] [{:keys [db]}]
(let [{:keys [public-key preferred-name]} (get db :multiaccount) (let [{:keys [public-key preferred-name]} (get db :multiaccount)
profile-link (universal-links/generate-link :user :external profile-link (universal-links/generate-link :user
(or preferred-name public-key)) :external
(or preferred-name
public-key))
message (i18n/label :t/join-me {:url profile-link})] message (i18n/label :t/join-me {:url profile-link})]
{::share {:message message}})) {::share {:message message}}))
@@ -1,23 +1,22 @@
(ns status-im.ui.screens.chat.audio-message.views (ns status-im.ui.screens.chat.audio-message.views
(:require-macros [status-im.utils.views :refer [defview letsubs]]) (:require-macros [status-im.utils.views :refer [defview letsubs]])
(:require (:require [goog.string :as gstring]
[goog.string :as gstring] [quo.components.animated.pressable :as pressable]
[reagent.core :as reagent] [quo.design-system.colors :as colors]
[status-im.audio.core :as audio] [re-frame.core :as re-frame]
[status-im.ui.components.react :as react] [reagent.core :as reagent]
[re-frame.core :as re-frame] [status-im.audio.core :as audio]
[status-im.i18n.i18n :as i18n] [status-im.i18n.i18n :as i18n]
[quo.components.animated.pressable :as pressable] [status-im.native-module.core :as status]
[status-im.native-module.core :as status] [status-im.ui.components.animation :as anim]
[status-im.ui.screens.chat.components.input :as input] [status-im.ui.components.icons.icons :as icons]
[status-im.ui.screens.chat.components.style :as input.style] [status-im.ui.components.react :as react]
[status-im.ui.screens.chat.audio-message.styles :as styles] [status-im.ui.screens.chat.audio-message.styles :as styles]
[quo.design-system.colors :as colors] [status-im.ui.screens.chat.components.input :as input]
[status-im.ui.components.animation :as anim] [status-im.ui.screens.chat.components.style :as input.style]
[status-im.ui.components.icons.icons :as icons] [status-im.utils.fs :as fs]
[status-im.utils.utils :as utils.utils] [status-im.utils.utils :as utils.utils]
[status-im.utils.fs :as fs] [utils.re-frame :as rf]))
[status-im.utils.fx :as fx]))
;; reference db levels ;; reference db levels
(def total-silence-db -160) (def total-silence-db -160)
@@ -31,20 +30,23 @@
;;ensure animation finishes before next meter update ;;ensure animation finishes before next meter update
(defonce metering-anim-duration (int (* metering-interval 0.9))) (defonce metering-anim-duration (int (* metering-interval 0.9)))
(defn update-meter [meter-data] (defn update-meter
[meter-data]
(let [value (if meter-data (let [value (if meter-data
(.-value ^js meter-data) (.-value ^js meter-data)
total-silence-db)] total-silence-db)]
(anim/start (anim/timing visual-target-value {:toValue value (anim/start (anim/timing visual-target-value
:duration metering-anim-duration {:toValue value
:useNativeDriver true})))) :duration metering-anim-duration
:useNativeDriver true}))))
(def base-filename "am.") (def base-filename "am.")
(def default-format "aac") (def default-format "aac")
(def rec-options (merge (def rec-options
audio/default-recorder-options (merge
{:filename (str base-filename default-format) audio/default-recorder-options
:meteringInterval metering-interval})) {:filename (str base-filename default-format)
:meteringInterval metering-interval}))
;; maximum 1 minute of recordings time to keep data at certain size ;; maximum 1 minute of recordings time to keep data at certain size
(def max-recording-ms (* 1 60 1000)) (def max-recording-ms (* 1 60 1000))
@@ -53,11 +55,13 @@
(defonce recorder-ref (atom nil)) (defonce recorder-ref (atom nil))
(defonce player-ref (atom nil)) (defonce player-ref (atom nil))
(defn destroy-recorder [] (defn destroy-recorder
[]
(audio/destroy-recorder @recorder-ref) (audio/destroy-recorder @recorder-ref)
(reset! recorder-ref nil)) (reset! recorder-ref nil))
(defn destroy-player [] (defn destroy-player
[]
(audio/destroy-player @player-ref) (audio/destroy-player @player-ref)
(reset! player-ref nil)) (reset! player-ref nil))
@@ -70,7 +74,7 @@
;; to be called when app goes in background ;; to be called when app goes in background
(defonce on-background-cb (atom #())) (defonce on-background-cb (atom #()))
(fx/defn on-background (rf/defn on-background
{:events [:audio-recorder/on-background]} {:events [:audio-recorder/on-background]}
[_] [_]
(when @on-background-cb (when @on-background-cb
@@ -83,32 +87,38 @@
(defonce recording-backlog-ms (atom 0)) (defonce recording-backlog-ms (atom 0))
;; updates timer UI ;; updates timer UI
(defn update-timer [timer] (defn update-timer
[timer]
(let [ms (if @recording-start-ts (let [ms (if @recording-start-ts
(+ (+
(- (js/Date.now) @recording-start-ts) (- (js/Date.now) @recording-start-ts)
@recording-backlog-ms) @recording-backlog-ms)
@recording-backlog-ms) @recording-backlog-ms)
s (quot ms 1000)] s (quot ms 1000)]
(if (> ms max-recording-ms) (if (> ms max-recording-ms)
(@max-recording-reached-cb) (@max-recording-reached-cb)
(reset! timer (gstring/format "%d:%02d" (quot s 60) (mod s 60)))))) (reset! timer (gstring/format "%d:%02d" (quot s 60) (mod s 60))))))
(defn reset-timer [timer] (defn reset-timer
[timer]
(reset! timer "0:00") (reset! timer "0:00")
(reset! recording-backlog-ms 0)) (reset! recording-backlog-ms 0))
(defn animate-buttons [rec? show-ctrl? {:keys [rec-button-anim-value ctrl-buttons-anim-value]}] (defn animate-buttons
[rec? show-ctrl? {:keys [rec-button-anim-value ctrl-buttons-anim-value]}]
(anim/start (anim/start
(anim/parallel (anim/parallel
[(anim/timing rec-button-anim-value {:toValue (if rec? 1 0) [(anim/timing rec-button-anim-value
:duration 100 {:toValue (if rec? 1 0)
:useNativeDriver true}) :duration 100
(anim/timing ctrl-buttons-anim-value {:toValue (if show-ctrl? 1 0) :useNativeDriver true})
:duration 100 (anim/timing ctrl-buttons-anim-value
:useNativeDriver true})]))) {:toValue (if show-ctrl? 1 0)
:duration 100
:useNativeDriver true})])))
(defn start-recording [{:keys [timer] :as params}] (defn start-recording
[{:keys [timer] :as params}]
(if (> @recording-backlog-ms max-recording-ms) (if (> @recording-backlog-ms max-recording-ms)
(@max-recording-reached-cb) (@max-recording-reached-cb)
(do (do
@@ -120,7 +130,8 @@
@state-cb @state-cb
#(utils.utils/show-popup (i18n/label :t/audio-recorder-error) (:message %)))))) #(utils.utils/show-popup (i18n/label :t/audio-recorder-error) (:message %))))))
(defn reload-recorder [] (defn reload-recorder
[]
(when @recorder-ref (when @recorder-ref
(destroy-recorder)) (destroy-recorder))
(reset! recorder-ref (audio/new-recorder rec-options #(update-meter %) @state-cb)) (reset! recorder-ref (audio/new-recorder rec-options #(update-meter %) @state-cb))
@@ -134,7 +145,7 @@
(destroy-player)) (destroy-player))
(reset! player-ref (audio/new-player (reset! player-ref (audio/new-player
(:filename rec-options) (:filename rec-options)
{:autoDestroy false {:autoDestroy false
:continuesToPlayInBackground false} :continuesToPlayInBackground false}
@state-cb)) @state-cb))
(audio/prepare-player (audio/prepare-player
@@ -142,7 +153,8 @@
#(do (@state-cb) (when on-success (on-success))) #(do (@state-cb) (when on-success (on-success)))
#(utils.utils/show-popup (i18n/label :t/audio-recorder-error) (:message %))))) #(utils.utils/show-popup (i18n/label :t/audio-recorder-error) (:message %)))))
(defn stop-recording [{:keys [on-success timer max-recording-reached?] :as params}] (defn stop-recording
[{:keys [on-success timer max-recording-reached?] :as params}]
(when @recording-timer (when @recording-timer
(utils.utils/clear-interval @recording-timer) (utils.utils/clear-interval @recording-timer)
(reset! recording-timer nil)) (reset! recording-timer nil))
@@ -158,7 +170,8 @@
#(utils.utils/show-popup (i18n/label :t/audio-recorder-error) (:message %))) #(utils.utils/show-popup (i18n/label :t/audio-recorder-error) (:message %)))
(animate-buttons false max-recording-reached? params)) (animate-buttons false max-recording-reached? params))
(defn pause-recording [{:keys [timer] :as params}] (defn pause-recording
[{:keys [timer] :as params}]
(when @recording-timer (when @recording-timer
(utils.utils/clear-interval @recording-timer) (utils.utils/clear-interval @recording-timer)
(reset! recording-backlog-ms (+ @recording-backlog-ms (- (js/Date.now) @recording-start-ts))) (reset! recording-backlog-ms (+ @recording-backlog-ms (- (js/Date.now) @recording-start-ts)))
@@ -181,28 +194,29 @@
- :recording-paused - :recording-paused
- :ready-to-record" - :ready-to-record"
[state-ref] [state-ref]
(let [player-state (audio/get-state @player-ref) (let [player-state (audio/get-state @player-ref)
recorder-state (audio/get-state @recorder-ref) recorder-state (audio/get-state @recorder-ref)
output-file (or output-file (or
(audio/get-recorder-file-path @recorder-ref) (audio/get-recorder-file-path @recorder-ref)
(:output-file @state-ref)) (:output-file @state-ref))
general (cond general (cond
(= recorder-state audio/RECORDING) :recording (= recorder-state audio/RECORDING) :recording
(= player-state audio/PLAYING) :playing (= player-state audio/PLAYING) :playing
(= player-state audio/PREPARED) :ready-to-send (= player-state audio/PREPARED) :ready-to-send
(= recorder-state audio/PAUSED) :recording-paused (= recorder-state audio/PAUSED) :recording-paused
:else :ready-to-record) :else :ready-to-record)
new-state {:general general new-state {:general general
:cancel-disabled? (nil? (#{:recording :recording-paused :ready-to-send} general)) :cancel-disabled? (nil? (#{:recording :recording-paused :ready-to-send} general))
:output-file output-file :output-file output-file
:duration (audio/get-player-duration @player-ref)}] :duration (audio/get-player-duration @player-ref)}]
(if (#{:recording :recording-paused} general) (if (#{:recording :recording-paused} general)
(status/activate-keep-awake) (status/activate-keep-awake)
(status/deactivate-keep-awake)) (status/deactivate-keep-awake))
(when (not= @state-ref new-state) (when (not= @state-ref new-state)
(reset! state-ref new-state)))) (reset! state-ref new-state))))
(defn send-audio-msessage [state-ref] (defn send-audio-msessage
[state-ref]
(re-frame/dispatch [:chat/send-audio (re-frame/dispatch [:chat/send-audio
(:output-file @state-ref) (:output-file @state-ref)
(int (:duration @state-ref))]) (int (:duration @state-ref))])
@@ -210,52 +224,65 @@
(@state-cb)) (@state-cb))
;; rec-button-anim-value 0 => stopped, 1 => recording ;; rec-button-anim-value 0 => stopped, 1 => recording
(defview rec-button-view [{:keys [rec-button-anim-value state] :as params}] (defview rec-button-view
(letsubs [outer-scale (anim/interpolate visual-target-value {:inputRange [total-silence-db silence-db 0] [{:keys [rec-button-anim-value state] :as params}]
:outputRange [1 0.8 1.2]}) (letsubs [outer-scale (anim/interpolate visual-target-value
inner-scale (anim/interpolate rec-button-anim-value {:inputRange [0 1] {:inputRange [total-silence-db silence-db 0]
:outputRange [1 0.5]}) :outputRange [1 0.8 1.2]})
inner-border-radius (anim/interpolate rec-button-anim-value {:inputRange [0 1] inner-scale (anim/interpolate rec-button-anim-value
:outputRange [styles/rec-button-base-size 16]})] {:inputRange [0 1]
[react/touchable-highlight {:on-press #(if (= (:general @state) :recording) :outputRange [1 0.5]})
(pause-recording params) inner-border-radius (anim/interpolate rec-button-anim-value
(start-recording params)) {:inputRange [0 1]
:accessibility-label :start-stop-audio-recording-button} :outputRange [styles/rec-button-base-size 16]})]
[react/touchable-highlight
{:on-press #(if (= (:general @state) :recording)
(pause-recording params)
(start-recording params))
:accessibility-label :start-stop-audio-recording-button}
[react/view {:style styles/rec-button-container} [react/view {:style styles/rec-button-container}
[react/animated-view {:style (styles/rec-outer-circle outer-scale)}] [react/animated-view {:style (styles/rec-outer-circle outer-scale)}]
[react/animated-view {:style (styles/rec-inner-circle inner-scale inner-border-radius)}]]])) [react/animated-view {:style (styles/rec-inner-circle inner-scale inner-border-radius)}]]]))
(defn- cancel-button [disabled? on-press contact-request] (defn- cancel-button
[pressable/pressable {:type :scale [disabled? on-press contact-request]
:disabled disabled? [pressable/pressable
:on-press on-press} {:type :scale
:disabled disabled?
:on-press on-press}
[react/view {:style (input.style/send-message-button)} [react/view {:style (input.style/send-message-button)}
[icons/icon :main-icons/close [icons/icon :main-icons/close
{:container-style (merge (input.style/send-message-container contact-request) {:background-color colors/gray}) {:container-style (merge (input.style/send-message-container contact-request)
{:background-color colors/gray})
:accessibility-label :cancel-message-button :accessibility-label :cancel-message-button
:color colors/white-persist}]]]) :color colors/white-persist}]]])
(defview audio-message-view [] (defview audio-message-view
(letsubs [rec-button-anim-value (anim/create-value 0) []
(letsubs [rec-button-anim-value (anim/create-value 0)
ctrl-buttons-anim-value (anim/create-value 0) ctrl-buttons-anim-value (anim/create-value 0)
timer (reagent/atom "") timer (reagent/atom "")
state (reagent/atom nil)] state (reagent/atom nil)]
{:component-did-mount (fn [] {:component-did-mount (fn []
(reset-timer timer) (reset-timer timer)
(reset! state-cb #(update-state state)) (reset! state-cb #(update-state state))
(reset! max-recording-reached-cb #(do (reset! max-recording-reached-cb
(when (= (:general @state) :recording) #(do
(stop-recording {:rec-button-anim-value rec-button-anim-value (when (= (:general @state) :recording)
:ctrl-buttons-anim-value ctrl-buttons-anim-value (stop-recording {:rec-button-anim-value rec-button-anim-value
:timer timer :ctrl-buttons-anim-value ctrl-buttons-anim-value
:max-recording-reached? true})) :timer timer
(utils.utils/show-popup (i18n/label :t/audio-recorder) :max-recording-reached? true}))
(i18n/label :t/audio-recorder-max-ms-reached)))) (utils.utils/show-popup (i18n/label :t/audio-recorder)
(reset! on-background-cb #(when (= (:general @state) :recording) (i18n/label
(pause-recording {:rec-button-anim-value rec-button-anim-value :t/audio-recorder-max-ms-reached))))
:ctrl-buttons-anim-value ctrl-buttons-anim-value (reset! on-background-cb #(when (= (:general @state) :recording)
:timer timer}))) (pause-recording
(reload-recorder)) {:rec-button-anim-value rec-button-anim-value
:ctrl-buttons-anim-value
ctrl-buttons-anim-value
:timer timer})))
(reload-recorder))
:component-will-unmount (fn [] :component-will-unmount (fn []
(when @recording-timer (when @recording-timer
(utils.utils/clear-interval @recording-timer) (utils.utils/clear-interval @recording-timer)
@@ -268,26 +295,29 @@
(reset! state-cb nil) (reset! state-cb nil)
(reset! max-recording-reached-cb nil) (reset! max-recording-reached-cb nil)
(reset! on-background-cb nil))} (reset! on-background-cb nil))}
(let [base-params {:rec-button-anim-value rec-button-anim-value (let [base-params {:rec-button-anim-value rec-button-anim-value
:ctrl-buttons-anim-value ctrl-buttons-anim-value :ctrl-buttons-anim-value ctrl-buttons-anim-value
:timer timer} :timer timer}
contact-request @(re-frame/subscribe [:chats/sending-contact-request])] contact-request @(re-frame/subscribe [:chats/sending-contact-request])]
[react/view {:style styles/container} [react/view {:style styles/container}
[react/text {:style styles/timer [react/text
:accessibility-label :audio-message-recorded-time} @timer] {:style styles/timer
:accessibility-label :audio-message-recorded-time} @timer]
[react/view {:style styles/buttons-container} [react/view {:style styles/buttons-container}
[react/animated-view {:style {:opacity ctrl-buttons-anim-value}} [react/animated-view {:style {:opacity ctrl-buttons-anim-value}}
[cancel-button (:cancel-disabled? @state) #(stop-recording base-params) contact-request]] [cancel-button (:cancel-disabled? @state) #(stop-recording base-params) contact-request]]
[rec-button-view (merge base-params {:state state})] [rec-button-view (merge base-params {:state state})]
[react/animated-view {:style {:opacity ctrl-buttons-anim-value}} [react/animated-view {:style {:opacity ctrl-buttons-anim-value}}
[input/send-button (fn [] (cond [input/send-button
(= :ready-to-send (:general @state)) (fn []
(do (cond
(reset-timer timer) (= :ready-to-send (:general @state))
(animate-buttons false false base-params) (do
(send-audio-msessage state)) (reset-timer timer)
(animate-buttons false false base-params)
(send-audio-msessage state))
(#{:recording :recording-paused} (:general @state)) (#{:recording :recording-paused} (:general @state))
(stop-recording (merge base-params (stop-recording (merge base-params
{:on-success {:on-success
#(send-audio-msessage state)}))))]]]]))) #(send-audio-msessage state)}))))]]]])))
@@ -1,47 +1,54 @@
(ns status-im.ui.screens.chat.components.input (ns status-im.ui.screens.chat.components.input
(:require [status-im.ui.components.icons.icons :as icons] (:require [clojure.string :as string]
[quo.react-native :as rn] [quo.components.animated.pressable :as pressable]
[quo.react :as quo.react] [quo.components.list.item :as list-item]
[quo.platform :as platform]
[quo.components.text :as text] [quo.components.text :as text]
[quo.design-system.colors :as colors] [quo.design-system.colors :as colors]
[status-im.ui.screens.chat.components.style :as styles] [quo.platform :as platform]
[status-im.utils.fx :as fx] [quo.react :as quo.react]
[status-im.ui.screens.chat.components.reply :as reply] [quo.react-native :as rn]
[status-im.multiaccounts.core :as multiaccounts]
[status-im.chat.constants :as chat.constants]
[status-im.utils.utils :as utils.utils]
[quo.components.animated.pressable :as pressable]
[re-frame.core :as re-frame] [re-frame.core :as re-frame]
[status-im.i18n.i18n :as i18n]
[status-im.chat.models.mentions :as mentions]
[status-im.ui.components.list.views :as list]
[quo.components.list.item :as list-item]
[status-im.ui.screens.chat.photos :as photos]
[reagent.core :as reagent] [reagent.core :as reagent]
[clojure.string :as string])) [status-im.chat.constants :as chat.constants]
[status-im.chat.models.mentions :as mentions]
[status-im.i18n.i18n :as i18n]
[status-im.multiaccounts.core :as multiaccounts]
[status-im.ui.components.icons.icons :as icons]
[status-im.ui.components.list.views :as list]
[status-im.ui.screens.chat.components.reply :as reply]
[status-im.ui.screens.chat.components.style :as styles]
[status-im.ui.screens.chat.photos :as photos]
[status-im.utils.utils :as utils.utils]
[utils.re-frame :as rf]))
(defn input-focus [text-input-ref] (defn input-focus
(some-> ^js (quo.react/current-ref text-input-ref) .focus)) [text-input-ref]
(some-> ^js (quo.react/current-ref text-input-ref)
.focus))
(def panel->icons {:extensions :main-icons/commands (def panel->icons
:images :main-icons/photo}) {:extensions :main-icons/commands
:images :main-icons/photo})
(defn touchable-icon [{:keys [panel active set-active accessibility-label]}] (defn touchable-icon
[pressable/pressable {:type :scale [{:keys [panel active set-active accessibility-label]}]
:accessibility-label accessibility-label [pressable/pressable
:on-press #(set-active (when-not (= active panel) panel))} {:type :scale
:accessibility-label accessibility-label
:on-press #(set-active (when-not (= active panel) panel))}
[rn/view {:style (styles/touchable-icon)} [rn/view {:style (styles/touchable-icon)}
[icons/icon [icons/icon
(panel->icons panel) (panel->icons panel)
(styles/icon (= active panel))]]]) (styles/icon (= active panel))]]])
(defn touchable-stickers-icon [{:keys [panel active set-active accessibility-label input-focus]}] (defn touchable-stickers-icon
[pressable/pressable {:type :scale [{:keys [panel active set-active accessibility-label input-focus]}]
:accessibility-label accessibility-label [pressable/pressable
:on-press #(if (= active panel) {:type :scale
(input-focus) :accessibility-label accessibility-label
(set-active panel))} :on-press #(if (= active panel)
(input-focus)
(set-active panel))}
[rn/view {:style (styles/in-input-touchable-icon)} [rn/view {:style (styles/in-input-touchable-icon)}
(if (= active panel) (if (= active panel)
[icons/icon :main-icons/keyboard (styles/icon false)] [icons/icon :main-icons/keyboard (styles/icon false)]
@@ -49,7 +56,8 @@
;; TODO(Ferossgp): Move this into audio panel. ;; TODO(Ferossgp): Move this into audio panel.
;; Instead of not changing panel we can show a placeholder with no permission ;; Instead of not changing panel we can show a placeholder with no permission
(defn- request-record-audio-permission [set-active panel] (defn- request-record-audio-permission
[set-active panel]
(re-frame/dispatch (re-frame/dispatch
[:request-permissions [:request-permissions
{:permissions [:record-audio] {:permissions [:record-audio]
@@ -63,18 +71,21 @@
(i18n/label :t/audio-recorder-permissions-error))) (i18n/label :t/audio-recorder-permissions-error)))
50)}])) 50)}]))
(defn touchable-audio-icon [{:keys [panel active set-active accessibility-label input-focus]}] (defn touchable-audio-icon
[pressable/pressable {:type :scale [{:keys [panel active set-active accessibility-label input-focus]}]
:accessibility-label accessibility-label [pressable/pressable
:on-press #(if (= active panel) {:type :scale
(input-focus) :accessibility-label accessibility-label
(request-record-audio-permission set-active panel))} :on-press #(if (= active panel)
(input-focus)
(request-record-audio-permission set-active panel))}
[rn/view {:style (styles/in-input-touchable-icon)} [rn/view {:style (styles/in-input-touchable-icon)}
(if (= active panel) (if (= active panel)
[icons/icon :main-icons/keyboard (styles/icon false)] [icons/icon :main-icons/keyboard (styles/icon false)]
[icons/icon :main-icons/speech (styles/icon false)])]]) [icons/icon :main-icons/speech (styles/icon false)])]])
(defn send-button [on-send contact-request] (defn send-button
[on-send contact-request]
[rn/touchable-opacity {:on-press-in on-send} [rn/touchable-opacity {:on-press-in on-send}
[rn/view {:style (styles/send-message-button)} [rn/view {:style (styles/send-message-button)}
(when-not contact-request (when-not contact-request
@@ -83,22 +94,23 @@
:accessibility-label :send-message-button :accessibility-label :send-message-button
:color (styles/send-icon-color)}])]]) :color (styles/send-icon-color)}])]])
(defn on-selection-change [timeout-id last-text-change mentionable-users args] (defn on-selection-change
[timeout-id last-text-change mentionable-users args]
(let [selection (.-selection ^js (.-nativeEvent ^js args)) (let [selection (.-selection ^js (.-nativeEvent ^js args))
start (.-start selection) start (.-start selection)
end (.-end selection)] end (.-end selection)]
;; NOTE(rasom): on iOS we do not dispatch this event immediately ;; NOTE(rasom): on iOS we do not dispatch this event immediately
;; because it is needed only in case if selection is changed without ;; because it is needed only in case if selection is changed without
;; typing. Timeout might be canceled on `on-change`. ;; typing. Timeout might be canceled on `on-change`.
(when platform/ios? (when platform/ios?
(reset! (reset!
timeout-id timeout-id
(utils.utils/set-timeout (utils.utils/set-timeout
#(re-frame/dispatch [::mentions/on-selection-change #(re-frame/dispatch [::mentions/on-selection-change
{:start start {:start start
:end end} :end end}
mentionable-users]) mentionable-users])
50))) 50)))
;; NOTE(rasom): on Android we dispatch event only in case if there ;; NOTE(rasom): on Android we dispatch event only in case if there
;; was no text changes during last 50ms. `on-selection-change` is ;; was no text changes during last 50ms. `on-selection-change` is
;; dispatched after `on-change`, that's why there is no another way ;; dispatched after `on-change`, that's why there is no another way
@@ -128,42 +140,51 @@
[] []
(swap! chat-input-key inc)) (swap! chat-input-key inc))
(defn show-send [{:keys [actions-ref send-ref sticker-ref]}] (defn show-send
[{:keys [actions-ref send-ref sticker-ref]}]
(when actions-ref (when actions-ref
(quo.react/set-native-props actions-ref #js {:width 0 :left -88})) (quo.react/set-native-props actions-ref #js {:width 0 :left -88}))
(quo.react/set-native-props send-ref #js {:width nil :right nil}) (quo.react/set-native-props send-ref #js {:width nil :right nil})
(when sticker-ref (when sticker-ref
(quo.react/set-native-props sticker-ref #js {:width 0 :right -100}))) (quo.react/set-native-props sticker-ref #js {:width 0 :right -100})))
(defn hide-send [{:keys [actions-ref send-ref sticker-ref]}] (defn hide-send
[{:keys [actions-ref send-ref sticker-ref]}]
(when actions-ref (when actions-ref
(quo.react/set-native-props actions-ref #js {:width nil :left nil})) (quo.react/set-native-props actions-ref #js {:width nil :left nil}))
(quo.react/set-native-props send-ref #js {:width 0 :right -100}) (quo.react/set-native-props send-ref #js {:width 0 :right -100})
(when sticker-ref (when sticker-ref
(quo.react/set-native-props sticker-ref #js {:width nil :right nil}))) (quo.react/set-native-props sticker-ref #js {:width nil :right nil})))
(defn reset-input [refs chat-id] (defn reset-input
(some-> ^js (quo.react/current-ref (:text-input-ref refs)) .clear) [refs chat-id]
(some-> ^js (quo.react/current-ref (:text-input-ref refs))
.clear)
(swap! mentions-enabled update :render not) (swap! mentions-enabled update :render not)
(swap! input-texts dissoc chat-id)) (swap! input-texts dissoc chat-id))
(defn clear-input [chat-id refs] (defn clear-input
[chat-id refs]
(hide-send refs) (hide-send refs)
(if (get @mentions-enabled chat-id) (if (get @mentions-enabled chat-id)
(do (do
(swap! mentions-enabled dissoc chat-id) (swap! mentions-enabled dissoc chat-id)
;;we need this timeout, because if we clear text input and first index was a mention object with blue color, ;;we need this timeout, because if we clear text input and first index was a mention object with
;;after clearing text will be typed with this blue color, so we render white text first and then clear it ;;blue color,
;;after clearing text will be typed with this blue color, so we render white text first and then
;;clear it
(js/setTimeout #(reset-input refs chat-id) 50)) (js/setTimeout #(reset-input refs chat-id) 50))
(reset-input refs chat-id))) (reset-input refs chat-id)))
(defn on-text-change [val chat-id] (defn on-text-change
[val chat-id]
(swap! input-texts assoc chat-id val) (swap! input-texts assoc chat-id val)
;;we still store it in app-db for mentions, we don't have reactions in views ;;we still store it in app-db for mentions, we don't have reactions in views
(re-frame/dispatch [:chat.ui/set-chat-input-text val])) (re-frame/dispatch [:chat.ui/set-chat-input-text val]))
(defn on-change [last-text-change timeout-id mentionable-users refs chat-id sending-image args] (defn on-change
(let [text (.-text ^js (.-nativeEvent ^js args)) [last-text-change timeout-id mentionable-users refs chat-id sending-image args]
(let [text (.-text ^js (.-nativeEvent ^js args))
prev-text (get @input-texts chat-id)] prev-text (get @input-texts chat-id)]
(when (and (seq prev-text) (empty? text) (not sending-image)) (when (and (seq prev-text) (empty? text) (not sending-image))
(hide-send refs)) (hide-send refs))
@@ -187,7 +208,7 @@
(when platform/ios? (when platform/ios?
(re-frame/dispatch [::mentions/calculate-suggestions mentionable-users])))) (re-frame/dispatch [::mentions/calculate-suggestions mentionable-users]))))
(fx/defn set-input-text (rf/defn set-input-text
"Set input text for current-chat. Takes db and input text and cofx "Set input text for current-chat. Takes db and input text and cofx
as arguments and returns new fx. Always clear all validation messages." as arguments and returns new fx. Always clear all validation messages."
{:events [:chat.ui.input/set-chat-input-text]} {:events [:chat.ui.input/set-chat-input-text]}
@@ -197,15 +218,21 @@
chat (get-in db [:chats chat-id]) chat (get-in db [:chats chat-id])
current-multiaccount (:multiaccount db) current-multiaccount (:multiaccount db)
mentionable-users (mentions/get-mentionable-users mentionable-users (mentions/get-mentionable-users
chat all-contacts current-multiaccount nil) chat
all-contacts
current-multiaccount
nil)
hydrated-mentions (map hydrated-mentions (map
(fn [[t mention :as e]] (fn [[t mention :as e]]
(if (= t :mention) (if (= t :mention)
(let [mention (multiaccounts/displayed-name (let [mention (multiaccounts/displayed-name
(get mentionable-users mention))] (get mentionable-users mention))]
[:mention (if (string/starts-with? mention "@") [:mention
mention (str "@" mention))]) (if (string/starts-with? mention "@")
e)) text-with-mentions) mention
(str "@" mention))])
e))
text-with-mentions)
info (mentions/->info hydrated-mentions) info (mentions/->info hydrated-mentions)
new-text (string/join (map second hydrated-mentions))] new-text (string/join (map second hydrated-mentions))]
{:set-text-input-value [chat-id new-text] {:set-text-input-value [chat-id new-text]
@@ -215,13 +242,14 @@
(assoc-in [:chat/inputs-with-mentions chat-id] hydrated-mentions) (assoc-in [:chat/inputs-with-mentions chat-id] hydrated-mentions)
(assoc-in [:chats/mentions chat-id :mentions] info))})) (assoc-in [:chats/mentions chat-id :mentions] info))}))
(defn on-text-input [mentionable-users chat-id args] (defn on-text-input
(let [native-event (.-nativeEvent ^js args) [mentionable-users chat-id args]
text (.-text ^js native-event) (let [native-event (.-nativeEvent ^js args)
text (.-text ^js native-event)
previous-text (.-previousText ^js native-event) previous-text (.-previousText ^js native-event)
range (.-range ^js native-event) range (.-range ^js native-event)
start (.-start ^js range) start (.-start ^js range)
end (.-end ^js range)] end (.-end ^js range)]
(when (and (not (get @mentions-enabled chat-id)) (string/index-of text "@")) (when (and (not (get @mentions-enabled chat-id)) (string/index-of text "@"))
(swap! mentions-enabled assoc chat-id true)) (swap! mentions-enabled assoc chat-id true))
@@ -237,13 +265,14 @@
(when platform/android? (when platform/android?
(re-frame/dispatch [::mentions/calculate-suggestions mentionable-users])))) (re-frame/dispatch [::mentions/calculate-suggestions mentionable-users]))))
(defn text-input [{:keys [set-active-panel refs chat-id sending-image]}] (defn text-input
[{:keys [set-active-panel refs chat-id sending-image]}]
(let [cooldown-enabled? @(re-frame/subscribe [:chats/current-chat-cooldown-enabled?]) (let [cooldown-enabled? @(re-frame/subscribe [:chats/current-chat-cooldown-enabled?])
mentionable-users @(re-frame/subscribe [:chats/mentionable-users]) mentionable-users @(re-frame/subscribe [:chats/mentionable-users])
timeout-id (atom nil) timeout-id (atom nil)
last-text-change (atom nil) last-text-change (atom nil)
mentions-enabled (get @mentions-enabled chat-id) mentions-enabled (get @mentions-enabled chat-id)
contact-request @(re-frame/subscribe [:chats/sending-contact-request])] contact-request @(re-frame/subscribe [:chats/sending-contact-request])]
[rn/text-input [rn/text-input
{:style (styles/text-input contact-request) {:style (styles/text-input contact-request)
@@ -263,8 +292,12 @@
(i18n/label :t/type-a-message)) (i18n/label :t/type-a-message))
:underline-color-android :transparent :underline-color-android :transparent
:auto-capitalize :sentences :auto-capitalize :sentences
:on-selection-change (partial on-selection-change timeout-id last-text-change mentionable-users) :on-selection-change (partial on-selection-change
:on-change (partial on-change last-text-change timeout-id mentionable-users refs chat-id sending-image) timeout-id
last-text-change
mentionable-users)
:on-change
(partial on-change last-text-change timeout-id mentionable-users refs chat-id sending-image)
:on-text-input (partial on-text-input mentionable-users chat-id)} :on-text-input (partial on-text-input mentionable-users chat-id)}
(if mentions-enabled (if mentions-enabled
(for [[idx [type text]] (map-indexed (for [[idx [type text]] (map-indexed
@@ -280,39 +313,41 @@
[[public-key {:keys [alias name nickname] :as user}] _ _ text-input-ref] [[public-key {:keys [alias name nickname] :as user}] _ _ text-input-ref]
(let [ens-name? (not= alias name)] (let [ens-name? (not= alias name)]
[list-item/list-item [list-item/list-item
(cond-> {:icon [photos/member-photo public-key] (cond->
:size :small {:icon [photos/member-photo public-key]
:text-size :small :size :small
:title :text-size :small
[text/text :title
{:weight :medium [text/text
:ellipsize-mode :tail {:weight :medium
:number-of-lines 1 :ellipsize-mode :tail
:size :small} :number-of-lines 1
(if nickname :size :small}
nickname (if nickname
name) nickname
(when nickname name)
[text/text (when nickname
{:weight :regular [text/text
:color :secondary {:weight :regular
:ellipsize-mode :tail :color :secondary
:size :small} :ellipsize-mode :tail
" " :size :small}
(when ens-name? " "
"@") (when ens-name?
name])] "@")
:title-text-weight :medium name])]
:on-press :title-text-weight :medium
(fn [] :on-press
(re-frame/dispatch [:chat.ui/select-mention text-input-ref user]))} (fn []
(re-frame/dispatch [:chat.ui/select-mention text-input-ref user]))}
ens-name? ens-name?
(assoc :subtitle alias))])) (assoc :subtitle alias))]))
(def chat-toolbar-height (reagent/atom nil)) (def chat-toolbar-height (reagent/atom nil))
(defn autocomplete-mentions [text-input-ref bottom] (defn autocomplete-mentions
[text-input-ref bottom]
(let [suggestions @(re-frame/subscribe [:chat/mention-suggestions])] (let [suggestions @(re-frame/subscribe [:chat/mention-suggestions])]
(when (seq suggestions) (when (seq suggestions)
(let [height (+ 16 (* 52 (min 4.5 (count suggestions))))] (let [height (+ 16 (* 52 (min 4.5 (count suggestions))))]
@@ -330,36 +365,44 @@
:render-data text-input-ref :render-data text-input-ref
:render-fn mention-item}]]])))) :render-fn mention-item}]]]))))
(defn on-chat-toolbar-layout [^js ev] (defn on-chat-toolbar-layout
[^js ev]
(reset! chat-toolbar-height (-> ev .-nativeEvent .-layout .-height))) (reset! chat-toolbar-height (-> ev .-nativeEvent .-layout .-height)))
(defn send-image [] (defn send-image
[]
(let [sending-image @(re-frame/subscribe [:chats/sending-image])] (let [sending-image @(re-frame/subscribe [:chats/sending-image])]
(when (seq sending-image) (when (seq sending-image)
[reply/send-image sending-image]))) [reply/send-image sending-image])))
(defn actions [extensions image show-send actions-ref active-panel set-active-panel contact-request] (defn actions
[rn/view {:style (styles/actions-wrapper (and (not contact-request) show-send)) [extensions image show-send actions-ref active-panel set-active-panel contact-request]
:ref actions-ref} [rn/view
{:style (styles/actions-wrapper (and (not contact-request) show-send))
:ref actions-ref}
(when extensions (when extensions
[touchable-icon {:panel :extensions [touchable-icon
:accessibility-label :show-extensions-icon {:panel :extensions
:active active-panel :accessibility-label :show-extensions-icon
:set-active set-active-panel}]) :active active-panel
:set-active set-active-panel}])
(when image (when image
[touchable-icon {:panel :images [touchable-icon
:accessibility-label :show-photo-icon {:panel :images
:active active-panel :accessibility-label :show-photo-icon
:set-active set-active-panel}])]) :active active-panel
:set-active set-active-panel}])])
(defn chat-toolbar [{:keys [chat-id]}] (defn chat-toolbar
(let [actions-ref (quo.react/create-ref) [{:keys [chat-id]}]
send-ref (quo.react/create-ref) (let [actions-ref (quo.react/create-ref)
sticker-ref (quo.react/create-ref) send-ref (quo.react/create-ref)
sticker-ref (quo.react/create-ref)
toolbar-options (re-frame/subscribe [:chats/chat-toolbar]) toolbar-options (re-frame/subscribe [:chats/chat-toolbar])
show-send (seq (get @input-texts chat-id))] show-send (seq (get @input-texts chat-id))]
(fn [{:keys [active-panel set-active-panel text-input-ref chat-id]}] (fn [{:keys [active-panel set-active-panel text-input-ref chat-id]}]
(let [;we want to control components on native level, so instead of RN state we set native props via reference (let [;we want to control components on native level, so instead of RN state we set native props
;via reference
;we don't react on input text in this view, @input-texts below is a regular atom ;we don't react on input text in this view, @input-texts below is a regular atom
refs {:actions-ref actions-ref refs {:actions-ref actions-ref
:send-ref send-ref :send-ref send-ref
@@ -368,37 +411,43 @@
{:keys [send stickers image extensions audio sending-image]} @toolbar-options {:keys [send stickers image extensions audio sending-image]} @toolbar-options
show-send (or show-send sending-image) show-send (or show-send sending-image)
contact-request @(re-frame/subscribe [:chats/sending-contact-request])] contact-request @(re-frame/subscribe [:chats/sending-contact-request])]
[rn/view {:style (styles/toolbar) [rn/view
:on-layout on-chat-toolbar-layout} {:style (styles/toolbar)
:on-layout on-chat-toolbar-layout}
;; EXTENSIONS and IMAGE buttons ;; EXTENSIONS and IMAGE buttons
[actions extensions image show-send actions-ref active-panel set-active-panel contact-request] [actions extensions image show-send actions-ref active-panel set-active-panel contact-request]
[rn/view {:style (styles/input-container contact-request)} [rn/view {:style (styles/input-container contact-request)}
[send-image] [send-image]
[rn/view {:style styles/input-row} [rn/view {:style styles/input-row}
[text-input {:chat-id chat-id [text-input
:sending-image sending-image {:chat-id chat-id
:refs refs :sending-image sending-image
:set-active-panel set-active-panel}] :refs refs
:set-active-panel set-active-panel}]
;; SEND button ;; SEND button
[rn/view {:ref send-ref :style (when-not show-send {:width 0 :right -100})} [rn/view {:ref send-ref :style (when-not show-send {:width 0 :right -100})}
(when send (when send
[send-button #(do (clear-input chat-id refs) [send-button
(re-frame/dispatch [:chat.ui/send-current-message])) #(do (clear-input chat-id refs)
(re-frame/dispatch [:chat.ui/send-current-message]))
contact-request])] contact-request])]
;; STICKERS and AUDIO buttons ;; STICKERS and AUDIO buttons
(when-not @(re-frame/subscribe [:chats/edit-message]) (when-not @(re-frame/subscribe [:chats/edit-message])
[rn/view {:style (merge {:flex-direction :row} (when show-send {:width 0 :right -100})) [rn/view
:ref sticker-ref} {:style (merge {:flex-direction :row} (when show-send {:width 0 :right -100}))
:ref sticker-ref}
(when stickers (when stickers
[touchable-stickers-icon {:panel :stickers [touchable-stickers-icon
:accessibility-label :show-stickers-icon {:panel :stickers
:active active-panel :accessibility-label :show-stickers-icon
:input-focus #(input-focus text-input-ref) :active active-panel
:set-active set-active-panel}]) :input-focus #(input-focus text-input-ref)
:set-active set-active-panel}])
(when audio (when audio
[touchable-audio-icon {:panel :audio [touchable-audio-icon
:accessibility-label :show-audio-message-icon {:panel :audio
:active active-panel :accessibility-label :show-audio-message-icon
:input-focus #(input-focus text-input-ref) :active active-panel
:set-active set-active-panel}])])]]])))) :input-focus #(input-focus text-input-ref)
:set-active set-active-panel}])])]]]))))
@@ -1,26 +1,27 @@
(ns status-im.ui.screens.chat.message.audio-old (ns status-im.ui.screens.chat.message.audio-old
(:require-macros [status-im.utils.views :refer [defview letsubs]]) (:require-macros [status-im.utils.views :refer [defview letsubs]])
(:require [status-im.utils.utils :as utils] (:require ["react-native-blob-util" :default ReactNativeBlobUtil]
[reagent.core :as reagent]
[goog.string :as gstring] [goog.string :as gstring]
[status-im.audio.core :as audio]
[status-im.utils.fx :as fx]
[status-im.ui.screens.chat.styles.message.audio-old :as style]
[status-im.ui.components.animation :as anim]
[quo.design-system.colors :as colors] [quo.design-system.colors :as colors]
[reagent.core :as reagent]
[status-im.audio.core :as audio]
[status-im.ui.components.animation :as anim]
[status-im.ui.components.icons.icons :as icons] [status-im.ui.components.icons.icons :as icons]
[status-im.ui.components.react :as react] [status-im.ui.components.react :as react]
[status-im.ui.components.slider :as slider] [status-im.ui.components.slider :as slider]
["react-native-blob-util" :default ReactNativeBlobUtil] [status-im.ui.screens.chat.styles.message.audio-old :as style]
[status-im.utils.platform :as platform] [status-im.utils.platform :as platform]
[taoensso.timbre :as log])) [status-im.utils.utils :as utils]
[taoensso.timbre :as log]
[utils.re-frame :as rf]))
(defonce player-ref (atom nil)) (defonce player-ref (atom nil))
(defonce current-player-message-id (atom nil)) (defonce current-player-message-id (atom nil))
(defonce current-active-state-ref-ref (atom nil)) (defonce current-active-state-ref-ref (atom nil))
(defonce progress-timer (atom nil)) (defonce progress-timer (atom nil))
(defn start-stop-progress-timer [{:keys [state-ref progress-ref progress-anim]} start?] (defn start-stop-progress-timer
[{:keys [state-ref progress-ref progress-anim]} start?]
(when @progress-timer (when @progress-timer
(utils/clear-interval @progress-timer) (utils/clear-interval @progress-timer)
(when-not start? (when-not start?
@@ -28,48 +29,61 @@
(when start? (when start?
(when @progress-timer (when @progress-timer
(utils/clear-interval @progress-timer)) (utils/clear-interval @progress-timer))
(reset! progress-timer (utils/set-interval (reset! progress-timer
#(when (and @state-ref (not (:slider-seeking @state-ref))) (utils/set-interval
(let [ct (audio/get-player-current-time @player-ref)] #(when (and @state-ref (not (:slider-seeking @state-ref)))
(reset! progress-ref ct) (let [ct (audio/get-player-current-time @player-ref)]
(when ct (reset! progress-ref ct)
(anim/start (anim/timing progress-anim {:toValue @progress-ref (when ct
:duration 100 (anim/start (anim/timing progress-anim
:easing (.-linear ^js anim/easing) {:toValue @progress-ref
:useNativeDriver true}))))) :duration 100
100)))) :easing (.-linear ^js anim/easing)
:useNativeDriver true})))))
100))))
(defn update-state [{:keys [state-ref progress-ref progress-anim message-id seek-to-ms audio-duration-ms slider-new-state-seeking? unloaded? error]}] (defn update-state
(let [player-state (audio/get-state @player-ref) [{:keys [state-ref progress-ref progress-anim message-id seek-to-ms audio-duration-ms
slider-new-state-seeking? unloaded? error]}]
(let [player-state (audio/get-state @player-ref)
slider-seeking (if (some? slider-new-state-seeking?) slider-seeking (if (some? slider-new-state-seeking?)
slider-new-state-seeking? slider-new-state-seeking?
(:slider-seeking @state-ref)) (:slider-seeking @state-ref))
general (cond general (cond
(some? error) :error (some? error) :error
(or unloaded? (not= message-id @current-player-message-id)) :not-loaded (or unloaded? (not= message-id @current-player-message-id)) :not-loaded
slider-seeking (:general @state-ref) ; persist player state at the time user started sliding slider-seeking (:general
(= player-state audio/PLAYING) :playing @state-ref) ; persist
(= player-state audio/PAUSED) :paused ; player
(= player-state audio/SEEKING) :seeking ; state
(= player-state audio/PREPARED) :ready-to-play ; at
:else :preparing) ; the
new-state {:general general ; time
:error-msg error ; user
:duration (cond (not (#{:preparing :not-loaded :error} general)) ; started
(audio/get-player-duration @player-ref) ; sliding
(= player-state audio/PLAYING) :playing
(= player-state audio/PAUSED) :paused
(= player-state audio/SEEKING) :seeking
(= player-state audio/PREPARED) :ready-to-play
:else :preparing)
new-state {:general general
:error-msg error
:duration (cond (not (#{:preparing :not-loaded :error} general))
(audio/get-player-duration @player-ref)
audio-duration-ms audio-duration-ms audio-duration-ms audio-duration-ms
:else (:duration @state-ref)) :else (:duration @state-ref))
:progress-ref (or progress-ref (:progress-ref @state-ref)) :progress-ref (or progress-ref (:progress-ref @state-ref))
:progress-anim (or progress-anim (:progress-anim @state-ref)) :progress-anim (or progress-anim (:progress-anim @state-ref))
:slider-seeking slider-seeking :slider-seeking slider-seeking
; persist seek-to-ms while seeking or audio is not loaded ; persist seek-to-ms while seeking or audio is not loaded
:seek-to-ms (when (or :seek-to-ms (when (or
slider-seeking slider-seeking
(#{:preparing :not-loaded :error} general)) (#{:preparing :not-loaded :error} general))
(or seek-to-ms (:seek-to-ms @state-ref)))}] (or seek-to-ms (:seek-to-ms @state-ref)))}]
; update state if needed ; update state if needed
(when (not= @state-ref new-state) (when (not= @state-ref new-state)
(reset! state-ref new-state)) (reset! state-ref new-state))
@@ -87,9 +101,11 @@
(reset! (:progress-ref new-state) 0) (reset! (:progress-ref new-state) 0)
(anim/set-value (:progress-anim new-state) 0)))) (anim/set-value (:progress-anim new-state) 0))))
(defn destroy-player [{:keys [message-id reloading?]}] (defn destroy-player
(when (and @player-ref (or reloading? [{:keys [message-id reloading?]}]
(= message-id @current-player-message-id))) (when (and @player-ref
(or reloading?
(= message-id @current-player-message-id)))
(audio/destroy-player @player-ref) (audio/destroy-player @player-ref)
(reset! player-ref nil) (reset! player-ref nil)
(when @current-active-state-ref-ref (when @current-active-state-ref-ref
@@ -99,7 +115,8 @@
(defonce last-seek (atom (js/Date.now))) (defonce last-seek (atom (js/Date.now)))
(defn seek [{:keys [message-id] :as params} value immediate? on-success] (defn seek
[{:keys [message-id] :as params} value immediate? on-success]
(when (and @player-ref (= message-id @current-player-message-id)) (when (and @player-ref (= message-id @current-player-message-id))
(let [now (js/Date.now)] (let [now (js/Date.now)]
(when (or immediate? (> (- now @last-seek) 200)) (when (or immediate? (> (- now @last-seek) 200))
@@ -113,13 +130,15 @@
#(update-state (merge params {:error (:message %)})))))) #(update-state (merge params {:error (:message %)}))))))
(update-state (merge params {:seek-to-ms value}))) (update-state (merge params {:seek-to-ms value})))
(defn download-audio-http [base64-uri on-success] (defn download-audio-http
[base64-uri on-success]
(-> (.config ReactNativeBlobUtil (clj->js {:trusty platform/ios?})) (-> (.config ReactNativeBlobUtil (clj->js {:trusty platform/ios?}))
(.fetch "GET" (str base64-uri)) (.fetch "GET" (str base64-uri))
(.then #(on-success (.base64 ^js %))) (.then #(on-success (.base64 ^js %)))
(.catch #(log/error "could not fetch audio")))) (.catch #(log/error "could not fetch audio"))))
(defn reload-player [{:keys [message-id state-ref] :as params} audio-url on-success] (defn reload-player
[{:keys [message-id state-ref] :as params} audio-url on-success]
;; to avoid reloading player while is initializing, ;; to avoid reloading player while is initializing,
;; we go ahead only if there is no player or ;; we go ahead only if there is no player or
;; if it is already prepared ;; if it is already prepared
@@ -131,7 +150,7 @@
(fn [base64-data] (fn [base64-data]
(reset! player-ref (audio/new-player (reset! player-ref (audio/new-player
(str "data:audio/acc;base64," base64-data) (str "data:audio/acc;base64," base64-data)
{:autoDestroy false {:autoDestroy false
:continuesToPlayInBackground false} :continuesToPlayInBackground false}
#(seek params 0 true nil))) #(seek params 0 true nil)))
(audio/prepare-player (audio/prepare-player
@@ -142,7 +161,8 @@
(reset! current-active-state-ref-ref state-ref) (reset! current-active-state-ref-ref state-ref)
(update-state params))))) (update-state params)))))
(defn play-pause [{:keys [message-id state-ref] :as params} audio] (defn play-pause
[{:keys [message-id state-ref] :as params} audio]
(if (not= message-id @current-player-message-id) (if (not= message-id @current-player-message-id)
;; player has audio from another message, we need to reload ;; player has audio from another message, we need to reload
(reload-player params (reload-player params
@@ -171,40 +191,43 @@
(update-state params)) (update-state params))
#(update-state (merge params {:error (:message %)})))))) #(update-state (merge params {:error (:message %)}))))))
(defn- play-pause-button [state-ref outgoing on-press] (defn- play-pause-button
[state-ref outgoing on-press]
(let [color (if outgoing colors/blue colors/white-persist)] (let [color (if outgoing colors/blue colors/white-persist)]
(if (= (:general @state-ref) :preparing) (if (= (:general @state-ref) :preparing)
[react/view {:style (style/play-pause-container outgoing true)} [react/view {:style (style/play-pause-container outgoing true)}
[react/small-loading-indicator color]] [react/small-loading-indicator color]]
[react/touchable-highlight {:on-press on-press} [react/touchable-highlight {:on-press on-press}
[icons/icon (case (:general @state-ref) [icons/icon
:playing :main-icons/pause (case (:general @state-ref)
:main-icons/play) :playing :main-icons/pause
:main-icons/play)
{:container-style (style/play-pause-container outgoing false) {:container-style (style/play-pause-container outgoing false)
:accessibility-label :play-pause-audio-message-button :accessibility-label :play-pause-audio-message-button
:color color}]]))) :color color}]])))
(fx/defn on-background (rf/defn on-background
{:events [:audio-message/on-background]} {:events [:audio-message/on-background]}
[_] [_]
(when (and @current-active-state-ref-ref (when (and @current-active-state-ref-ref
@@current-active-state-ref-ref) @@current-active-state-ref-ref)
(update-state {:state-ref @current-active-state-ref-ref (update-state {:state-ref @current-active-state-ref-ref
:message-id @current-player-message-id})) :message-id @current-player-message-id}))
nil) nil)
(defview message-content [{:keys [audio audio-duration-ms message-id outgoing]}] (defview message-content
(letsubs [state (reagent/atom nil) [{:keys [audio audio-duration-ms message-id outgoing]}]
progress (reagent/atom 0) (letsubs [state (reagent/atom nil)
progress (reagent/atom 0)
progress-anim (anim/create-value 0) progress-anim (anim/create-value 0)
width [:dimensions/window-width]] width [:dimensions/window-width]]
{:component-did-mount (fn [] {:component-did-mount (fn []
(update-state {:state-ref state (update-state {:state-ref state
:audio-duration-ms audio-duration-ms :audio-duration-ms audio-duration-ms
:message-id message-id :message-id message-id
:unloaded? true :unloaded? true
:progress-ref progress :progress-ref progress
:progress-anim progress-anim})) :progress-anim progress-anim}))
:component-will-unmount (fn [] :component-will-unmount (fn []
(destroy-player {:state-ref state :message-id message-id}) (destroy-player {:state-ref state :message-id message-id})
(when (= @current-player-message-id message-id) (when (= @current-player-message-id message-id)
@@ -212,27 +235,38 @@
(reset! current-player-message-id nil)) (reset! current-player-message-id nil))
(reset! state nil))} (reset! state nil))}
(let [base-params {:state-ref state :message-id message-id :progress-ref progress :progress-anim progress-anim}] (let [base-params {:state-ref state
:message-id message-id
:progress-ref progress
:progress-anim progress-anim}]
(if (= (:general @state) :error) (if (= (:general @state) :error)
[react/text {:style {:typography :main-medium [react/text
:margin-bottom 16}} (:error-msg @state)] {:style {:typography :main-medium
:margin-bottom 16}} (:error-msg @state)]
[react/view (style/container width) [react/view (style/container width)
[react/view style/play-pause-slider-container [react/view style/play-pause-slider-container
[play-pause-button state outgoing #(play-pause base-params audio)] [play-pause-button state outgoing #(play-pause base-params audio)]
[react/view style/slider-container [react/view style/slider-container
[slider/animated-slider (merge (style/slider outgoing) [slider/animated-slider
{:minimum-value 0 (merge (style/slider outgoing)
:maximum-value (:duration @state) {:minimum-value 0
:value progress-anim :maximum-value (:duration @state)
:on-value-change #(seek base-params % false nil) :value progress-anim
:on-sliding-start #(seek (merge base-params {:slider-new-state-seeking? true}) % true nil) :on-value-change #(seek base-params % false nil)
:on-sliding-complete #(seek (merge base-params {:slider-new-state-seeking? false}) % true nil)})]]] :on-sliding-start #(seek (merge base-params {:slider-new-state-seeking? true})
%
true
nil)
:on-sliding-complete #(seek (merge base-params {:slider-new-state-seeking? false})
%
true
nil)})]]]
[react/view style/times-container [react/view style/times-container
[react/text {:style (style/timestamp outgoing)} [react/text {:style (style/timestamp outgoing)}
(let [time (cond (let [time (cond
(or (:slider-seeking @state) (> (:seek-to-ms @state) 0)) (:seek-to-ms @state) (or (:slider-seeking @state) (> (:seek-to-ms @state) 0)) (:seek-to-ms @state)
(#{:playing :paused :seeking} (:general @state)) @progress (#{:playing :paused :seeking} (:general @state)) @progress
:else (:duration @state)) :else (:duration @state))
s (quot time 1000)] s (quot time 1000)]
(gstring/format "%02d:%02d" (quot s 60) (mod s 60)))]]])))) (gstring/format "%02d:%02d" (quot s 60) (mod s 60)))]]]))))
@@ -1,87 +1,101 @@
(ns status-im.ui.screens.chat.message.pinned-message (ns status-im.ui.screens.chat.message.pinned-message
(:require [re-frame.core :as re-frame] (:require [quo.core :as quo]
[status-im.i18n.i18n :as i18n]
[quo.design-system.colors :as colors] [quo.design-system.colors :as colors]
[status-im.ui.components.react :as react] [re-frame.core :as re-frame]
[quo.core :as quo]
[reagent.core :as reagent] [reagent.core :as reagent]
[status-im.i18n.i18n :as i18n]
[status-im.ui.components.list.views :as list] [status-im.ui.components.list.views :as list]
[status-im.utils.handlers :refer [<sub]] [status-im.ui.components.react :as react]
[status-im.ui.screens.chat.message.message :as message])) [status-im.ui.screens.chat.message.message :as message]
[utils.re-frame :as rf]))
(def selected-unpin (reagent/atom nil)) (def selected-unpin (reagent/atom nil))
(defn render-pin-fn [{:keys [message-id outgoing] :as message} (defn render-pin-fn
_ [{:keys [message-id outgoing] :as message}
_ _
{:keys [group-chat public? current-public-key space-keeper]}] _
[react/touchable-without-feedback {:style {:width "100%"} {:keys [group-chat public? current-public-key space-keeper]}]
:on-press #(reset! selected-unpin message-id)} [react/touchable-without-feedback
[react/view {:style {:flex-direction :row {:style {:width "100%"}
:align-items :center :on-press #(reset! selected-unpin message-id)}
:justify-content :space-between [react/view
:flex 1 {:style {:flex-direction :row
:padding-right 20}} :align-items :center
:justify-content :space-between
:flex 1
:padding-right 20}}
[message/chat-message [message/chat-message
(assoc message (assoc message
:group-chat group-chat :group-chat group-chat
:public? public? :public? public?
:current-public-key current-public-key :current-public-key current-public-key
:show-input? false :show-input? false
:pinned false :pinned false
:display-username? (not outgoing) :display-username? (not outgoing)
:display-photo? false :display-photo? false
:last-in-group? false :last-in-group? false
:in-popover? true) :in-popover? true)
space-keeper] space-keeper]
[react/view {:style {:position :absolute [react/view
:right 18 {:style {:position :absolute
:padding-top 4}} :right 18
:padding-top 4}}
[quo/radio {:value (= @selected-unpin message-id)}]]]]) [quo/radio {:value (= @selected-unpin message-id)}]]]])
(def list-key-fn #(or (:message-id %) (:value %))) (def list-key-fn #(or (:message-id %) (:value %)))
(defn pinned-messages-limit-list [chat-id] (defn pinned-messages-limit-list
[chat-id]
(let [pinned-messages @(re-frame/subscribe [:chats/pinned-sorted-list chat-id])] (let [pinned-messages @(re-frame/subscribe [:chats/pinned-sorted-list chat-id])]
[list/flat-list [list/flat-list
{:key-fn list-key-fn {:key-fn list-key-fn
:data (reverse pinned-messages) :data (reverse pinned-messages)
:render-data {:chat-id chat-id} :render-data {:chat-id chat-id}
:render-fn render-pin-fn :render-fn render-pin-fn
:on-scroll-to-index-failed identity :on-scroll-to-index-failed identity
:style {:flex-grow 0 :style {:flex-grow 0
:border-top-width 1 :border-top-width 1
:border-bottom-width 1 :border-bottom-width 1
:border-top-color colors/gray-lighter :border-top-color colors/gray-lighter
:border-bottom-color colors/gray-lighter} :border-bottom-color colors/gray-lighter}
:content-container-style {:padding-bottom 10 :content-container-style {:padding-bottom 10
:padding-top 10}}])) :padding-top 10}}]))
(defn pin-limit-popover [] (defn pin-limit-popover
(let [{:keys [message]} (<sub [:popover/popover])] []
(let [{:keys [message]} (rf/sub [:popover/popover])]
[react/view {:style {:flex-shrink 1}} [react/view {:style {:flex-shrink 1}}
[react/view {:style {:height 60 [react/view
:justify-content :center}} {:style {:height 60
[react/text {:style {:padding-horizontal 40 :justify-content :center}}
:text-align :center}} [react/text
{:style {:padding-horizontal 40
:text-align :center}}
(i18n/label :t/pin-limit-reached)]] (i18n/label :t/pin-limit-reached)]]
[pinned-messages-limit-list (message :chat-id)] [pinned-messages-limit-list (message :chat-id)]
[react/view {:flex-direction :row :padding-horizontal 16 :height 60 :justify-content :space-between :align-items :center} [react/view
{:flex-direction :row
:padding-horizontal 16
:height 60
:justify-content :space-between
:align-items :center}
[quo/button [quo/button
{:on-press #(do {:on-press #(do
(reset! selected-unpin nil) (reset! selected-unpin nil)
(re-frame/dispatch [:hide-popover])) (re-frame/dispatch [:hide-popover]))
:type :secondary} :type :secondary}
(i18n/label :t/cancel)] (i18n/label :t/cancel)]
[quo/button [quo/button
{:on-press #(do {:on-press #(do
(re-frame/dispatch [:pin-message/send-pin-message {:chat-id (message :chat-id) (re-frame/dispatch [:pin-message/send-pin-message
:message-id @selected-unpin {:chat-id (message :chat-id)
:pinned false}]) :message-id @selected-unpin
:pinned false}])
(re-frame/dispatch [:pin-message/send-pin-message (assoc message :pinned true)]) (re-frame/dispatch [:pin-message/send-pin-message (assoc message :pinned true)])
(re-frame/dispatch [:hide-popover]) (re-frame/dispatch [:hide-popover])
(reset! selected-unpin nil)) (reset! selected-unpin nil))
:type :secondary :type :secondary
:disabled (nil? @selected-unpin) :disabled (nil? @selected-unpin)
:theme (if (nil? @selected-unpin) :disabled :negative)} :theme (if (nil? @selected-unpin) :disabled :negative)}
(i18n/label :t/unpin)]]])) (i18n/label :t/unpin)]]]))
@@ -1,59 +1,67 @@
(ns status-im.ui.screens.communities.channel-details (ns status-im.ui.screens.communities.channel-details
(:require [quo.core :as quo] (:require [clojure.string :as string]
[status-im.utils.handlers :refer [>evt <sub]] [quo.core :as quo]
[status-im.ui.components.profile-header.view :as profile-header] [re-frame.core :as re-frame]
[status-im.i18n.i18n :as i18n]
[clojure.string :as string]
[status-im.communities.core :as communities] [status-im.communities.core :as communities]
[re-frame.core :as re-frame])) [status-im.i18n.i18n :as i18n]
[status-im.ui.components.profile-header.view :as profile-header]
[utils.re-frame :as rf]))
(defn view [] (defn view
(let [{:keys [chat-id]} (<sub [:get-screen-params])] []
(let [{:keys [chat-id]} (rf/sub [:get-screen-params])]
(fn [] (fn []
(let [current-chat (<sub [:chat-by-id chat-id]) (let [current-chat (rf/sub [:chat-by-id chat-id])
{:keys [chat-name color description community-id emoji]} current-chat {:keys [chat-name color description community-id emoji]} current-chat
{:keys [position]} (<sub [:chats/community-chat-by-id community-id chat-id]) {:keys [position]} (rf/sub [:chats/community-chat-by-id
category (<sub [:chats/category-by-chat-id community-id chat-id]) community-id chat-id])
{:keys [admin]} (<sub [:communities/community community-id]) category (rf/sub [:chats/category-by-chat-id
pinned-messages (<sub [:chats/pinned chat-id])] community-id chat-id])
[quo/animated-header {:left-accessories [{:icon :main-icons/arrow-left {:keys [admin]} (rf/sub [:communities/community
:accessibility-label :back-button community-id])
:on-press #(>evt [:navigate-back])}] pinned-messages (rf/sub [:chats/pinned chat-id])]
:right-accessories (when admin [{:icon :edit [quo/animated-header
:accessibility-label :invite-button {:left-accessories [{:icon :main-icons/arrow-left
:on-press #(>evt [::communities/edit-channel-pressed :accessibility-label :back-button
community-id :on-press #(rf/dispatch [:navigate-back])}]
chat-name :right-accessories (when admin
description [{:icon :edit
color :accessibility-label :invite-button
emoji :on-press #(rf/dispatch [::communities/edit-channel-pressed
chat-id community-id
(:id category) chat-name
position])}]) description
:extended-header (profile-header/extended-header color
{:title chat-name emoji
:color color chat-id
:emoji emoji (:id category)
:subtitle (i18n/label :t/public-channel)}) position])}])
:use-insets true} :extended-header (profile-header/extended-header
{:title chat-name
:color color
:emoji emoji
:subtitle (i18n/label :t/public-channel)})
:use-insets true}
(when-not (string/blank? description) (when-not (string/blank? description)
[:<> [:<>
[quo/list-footer {:color :main} [quo/list-footer {:color :main}
description] description]
[quo/separator {:style {:margin-vertical 8}}] [quo/separator {:style {:margin-vertical 8}}]
(when admin (when admin
[quo/list-item {:title (i18n/label :t/category) [quo/list-item
:on-press #(>evt [:open-modal :select-category {:chat current-chat {:title (i18n/label :t/category)
:category category :on-press #(rf/dispatch [:open-modal :select-category
:community-id community-id}]) {:chat current-chat
:chevron true :category category
:accessory :text :community-id community-id}])
:accessory-text (if category :chevron true
(:name category) :accessory :text
(i18n/label :t/none))}]) :accessory-text (if category
(:name category)
(i18n/label :t/none))}])
[quo/list-item [quo/list-item
{:title (i18n/label :t/pinned-messages) {:title (i18n/label :t/pinned-messages)
:accessory :text :accessory :text
:accessory-text (count pinned-messages) :accessory-text (count pinned-messages)
:chevron true :chevron true
:on-press #(re-frame/dispatch [:contact.ui/pinned-messages-pressed chat-id])}]])])))) :on-press #(re-frame/dispatch [:contact.ui/pinned-messages-pressed chat-id])}]])]))))
@@ -1,44 +1,52 @@
(ns status-im.ui.screens.communities.community-emoji-thumbnail-picker (ns status-im.ui.screens.communities.community-emoji-thumbnail-picker
(:require [quo.react-native :as rn] (:require [clojure.string :as string]
[quo.core :as quo] [quo.core :as quo]
[clojure.string :as string] [quo.react-native :as rn]
[status-im.ui.components.react :as react]
[status-im.utils.handlers :refer [>evt <sub]]
[status-im.communities.core :as communities] [status-im.communities.core :as communities]
[status-im.ui.components.keyboard-avoid-presentation :as kb-presentation] [status-im.ui.components.emoji-thumbnail.color-picker :as color-picker]
[status-im.ui.components.emoji-thumbnail.styles :as styles]
[status-im.ui.components.emoji-thumbnail.preview :as emoji-thumbnail-preview] [status-im.ui.components.emoji-thumbnail.preview :as emoji-thumbnail-preview]
[status-im.ui.components.emoji-thumbnail.color-picker :as color-picker])) [status-im.ui.components.emoji-thumbnail.styles :as styles]
[status-im.ui.components.keyboard-avoid-presentation :as kb-presentation]
[status-im.ui.components.react :as react]
[utils.re-frame :as rf]))
(defn thumbnail-preview-section [] (defn thumbnail-preview-section
(let [{:keys [color emoji]} (<sub [:communities/create-channel]) []
size styles/emoji-thumbnail-preview-size] (let [{:keys [color emoji]} (rf/sub [:communities/create-channel])
size styles/emoji-thumbnail-preview-size]
[rn/view styles/emoji-thumbnail-preview [rn/view styles/emoji-thumbnail-preview
[emoji-thumbnail-preview/emoji-thumbnail [emoji-thumbnail-preview/emoji-thumbnail
emoji color size]])) emoji color size]]))
(defn color-circle [item] (defn color-circle
(let [{:keys [color]} (<sub [:communities/create-channel]) [item]
item-color (:color item) (let [{:keys [color]} (rf/sub [:communities/create-channel])
key (:key key) item-color (:color item)
color-selected? (= (string/lower-case item-color) (string/lower-case color))] key (:key key)
[react/touchable-opacity {:key key color-selected? (= (string/lower-case item-color) (string/lower-case color))]
:accessibility-label :color-circle [react/touchable-opacity
:on-press #(>evt [::communities/create-channel-field :color item-color])} {:key key
:accessibility-label :color-circle
:on-press #(rf/dispatch [::communities/create-channel-field :color item-color])}
[rn/view {:style (styles/emoji-picker-color-border item-color color-selected?)} [rn/view {:style (styles/emoji-picker-color-border item-color color-selected?)}
[rn/view {:style (styles/emoji-picker-color item-color)}]]])) [rn/view {:style (styles/emoji-picker-color item-color)}]]]))
(defn update-emoji [emoji] (defn update-emoji
[emoji]
(when-not (string/blank? emoji) (when-not (string/blank? emoji)
(>evt [::communities/create-channel-field :emoji emoji]))) (rf/dispatch [::communities/create-channel-field :emoji emoji])))
(defn emoji-keyboard-section [] (defn emoji-keyboard-section
(let [{:keys [width height]} (<sub [:dimensions/window]) []
keyboard_height (if (> width height) 400 (- height styles/emoji-picker-upper-components-size))] (let [{:keys [width height]} (rf/sub [:dimensions/window])
keyboard_height (if (> width height)
400
(- height styles/emoji-picker-upper-components-size))]
[rn/view {:style {:height keyboard_height} :accessibility-label :emoji-keyboard-container} [rn/view {:style {:height keyboard_height} :accessibility-label :emoji-keyboard-container}
[rn/emoji-keyboard (styles/emoji-keyboard #(update-emoji (.-emoji ^js %)))]])) [rn/emoji-keyboard (styles/emoji-keyboard #(update-emoji (.-emoji ^js %)))]]))
(defn view [] (defn view
[]
[kb-presentation/keyboard-avoiding-view {:style {:flex 1}} [kb-presentation/keyboard-avoiding-view {:style {:flex 1}}
[rn/scroll-view [rn/scroll-view
[quo/separator] [quo/separator]
+152 -120
View File
@@ -1,182 +1,214 @@
(ns status-im.ui.screens.communities.create (ns status-im.ui.screens.communities.create
(:require [quo.react-native :as rn] (:require [clojure.string :as string]
[status-im.i18n.i18n :as i18n]
[quo.core :as quo] [quo.core :as quo]
[clojure.string :as string]
[status-im.utils.handlers :refer [>evt <sub]]
[status-im.communities.core :as communities]
[status-im.ui.components.toolbar :as toolbar]
[status-im.utils.image :as utils.image]
[quo.design-system.colors :as colors] [quo.design-system.colors :as colors]
[status-im.ui.components.react :as react] [quo.react-native :as rn]
[status-im.ui.screens.communities.membership :as memberships] [status-im.communities.core :as communities]
[status-im.i18n.i18n :as i18n]
[status-im.ui.components.icons.icons :as icons] [status-im.ui.components.icons.icons :as icons]
[utils.debounce :as debounce])) [status-im.ui.components.react :as react]
[status-im.ui.components.toolbar :as toolbar]
[status-im.ui.screens.communities.membership :as memberships]
[status-im.utils.image :as utils.image]
[utils.debounce :as debounce]
[utils.re-frame :as rf]))
(def max-name-length 30) (def max-name-length 30)
(def max-description-length 140) (def max-description-length 140)
(defn valid? [community-name community-description] (defn valid?
[community-name community-description]
(and (not (string/blank? community-name)) (and (not (string/blank? community-name))
(not (string/blank? community-description)) (not (string/blank? community-description))
(<= (count community-name) max-name-length) (<= (count community-name) max-name-length)
(<= (count community-description) max-description-length))) (<= (count community-description) max-description-length)))
(def crop-size 1000) (def crop-size 1000)
(def crop-opts {:cropping true (def crop-opts
:cropperCircleOverlay true {:cropping true
:width crop-size :cropperCircleOverlay true
:height crop-size}) :width crop-size
:height crop-size})
(defn pick-pic [] (defn pick-pic
[]
;;we need timeout because first we need to close bottom sheet and then open picker ;;we need timeout because first we need to close bottom sheet and then open picker
(js/setTimeout (js/setTimeout
(fn [] (fn []
(react/show-image-picker (react/show-image-picker
#(do (>evt [::communities/create-field :image (.-path ^js %)]) #(do (rf/dispatch [::communities/create-field :image (.-path ^js %)])
(>evt [::communities/create-field :new-image (.-path ^js %)])) (rf/dispatch [::communities/create-field :new-image (.-path ^js %)]))
crop-opts)) crop-opts))
300)) 300))
(defn take-pic [] (defn take-pic
[]
;;we need timeout because first we need to close bottom sheet and then open picker ;;we need timeout because first we need to close bottom sheet and then open picker
(js/setTimeout (js/setTimeout
(fn [] (fn []
(react/show-image-picker-camera (react/show-image-picker-camera
#(do (>evt [::communities/create-field :image (.-path ^js %)]) #(do (rf/dispatch [::communities/create-field :image (.-path ^js %)])
(>evt [::communities/create-field :new-image (.-path ^js %)])) (rf/dispatch [::communities/create-field :new-image (.-path ^js %)]))
crop-opts)) crop-opts))
300)) 300))
(defn bottom-sheet [has-picture editing?] (defn bottom-sheet
[has-picture editing?]
(fn [] (fn []
[:<> [:<>
[quo/list-item {:accessibility-label :take-photo [quo/list-item
:theme :accent {:accessibility-label :take-photo
:icon :main-icons/camera :theme :accent
:title (i18n/label :t/community-image-take) :icon :main-icons/camera
:on-press #(do :title (i18n/label :t/community-image-take)
(>evt [:bottom-sheet/hide]) :on-press #(do
(take-pic))}] (rf/dispatch [:bottom-sheet/hide])
[quo/list-item {:accessibility-label :pick-photo (take-pic))}]
:icon :main-icons/gallery [quo/list-item
:theme :accent {:accessibility-label :pick-photo
:title (i18n/label :t/community-image-pick) :icon :main-icons/gallery
:on-press #(do :theme :accent
(>evt [:bottom-sheet/hide]) :title (i18n/label :t/community-image-pick)
(pick-pic))}] :on-press #(do
(rf/dispatch [:bottom-sheet/hide])
(pick-pic))}]
(when (and has-picture (not editing?)) (when (and has-picture (not editing?))
[quo/list-item {:accessibility-label :remove-photo [quo/list-item
:icon :main-icons/delete {:accessibility-label :remove-photo
:theme :accent :icon :main-icons/delete
:title (i18n/label :t/community-image-remove) :theme :accent
:on-press #(do :title (i18n/label :t/community-image-remove)
(>evt [:bottom-sheet/hide]) :on-press #(do
(>evt [::communities/remove-field :image]))}])])) (rf/dispatch [:bottom-sheet/hide])
(rf/dispatch [::communities/remove-field :image]))}])]))
(defn photo-picker [] (defn photo-picker
(let [{:keys [image editing?]} (<sub [:communities/create])] []
[rn/view {:style {:padding-top 16 (let [{:keys [image editing?]} (rf/sub [:communities/create])]
:align-items :center}} [rn/view
[rn/touchable-opacity {:on-press #(>evt [:bottom-sheet/show-sheet {:style {:padding-top 16
{:content (bottom-sheet (boolean image) editing?)}])} :align-items :center}}
[rn/view {:style {:width 128 [rn/touchable-opacity
:height 128}} {:on-press #(rf/dispatch [:bottom-sheet/show-sheet
[rn/view {:style {:flex 1 {:content (bottom-sheet (boolean image) editing?)}])}
:border-radius 64 [rn/view
:background-color (colors/get-color :ui-01) {:style {:width 128
:justify-content :center :height 128}}
:align-items :center}} [rn/view
{:style {:flex 1
:border-radius 64
:background-color (colors/get-color :ui-01)
:justify-content :center
:align-items :center}}
(if image (if image
[rn/image {:source (utils.image/source image) [rn/image
:style {:width 128 {:source (utils.image/source image)
:height 128 :style {:width 128
:border-radius 64} :height 128
:resize-mode :cover :border-radius 64}
:accessibility-label :community-image}] :resize-mode :cover
:accessibility-label :community-image}]
[:<> [:<>
[icons/icon :main-icons/photo {:color (colors/get-color :icon-02)}] [icons/icon :main-icons/photo {:color (colors/get-color :icon-02)}]
[quo/text {:color :secondary} [quo/text {:color :secondary}
(i18n/label :t/community-thumbnail-upload)]])] (i18n/label :t/community-thumbnail-upload)]])]
[rn/view {:style {:position :absolute [rn/view
:top 0 {:style {:position :absolute
:right 7}} :top 0
[rn/view {:style {:width 40 :right 7}}
:height 40 [rn/view
:background-color (colors/get-color :interactive-01) {:style {:width 40
:border-radius 20 :height 40
:align-items :center :background-color (colors/get-color :interactive-01)
:justify-content :center :border-radius 20
:shadow-offset {:width 0 :height 1} :align-items :center
:shadow-radius 6 :justify-content :center
:shadow-opacity 1 :shadow-offset {:width 0 :height 1}
:shadow-color (colors/get-color :shadow-01) :shadow-radius 6
:elevation 2}} :shadow-opacity 1
:shadow-color (colors/get-color :shadow-01)
:elevation 2}}
[icons/icon :main-icons/add {:color colors/white}]]]]]])) [icons/icon :main-icons/add {:color colors/white}]]]]]]))
(defn countable-label [{:keys [label text max-length]}] (defn countable-label
[rn/view {:style {:padding-bottom 10 [{:keys [label text max-length]}]
:justify-content :space-between [rn/view
:align-items :flex-end {:style {:padding-bottom 10
:flex-direction :row :justify-content :space-between
:flex-wrap :nowrap}} :align-items :flex-end
:flex-direction :row
:flex-wrap :nowrap}}
[quo/text label] [quo/text label]
[quo/text {:size :small [quo/text
:color (if (> (count text) max-length) {:size :small
:negative :color (if (> (count text) max-length)
:secondary)} :negative
:secondary)}
(str (count text) "/" max-length)]]) (str (count text) "/" max-length)]])
(defn form [] (defn form
(let [{:keys [name description membership editing?]} (<sub [:communities/create])] []
[rn/scroll-view {:keyboard-should-persist-taps :handled (let [{:keys [name description membership editing?]} (rf/sub [:communities/create])]
:style {:flex 1} [rn/scroll-view
:content-container-style {:padding-vertical 16}} {:keyboard-should-persist-taps :handled
[rn/view {:style {:padding-bottom 16 :style {:flex 1}
:padding-top 10 :content-container-style {:padding-vertical 16}}
:padding-horizontal 16}} [rn/view
[countable-label {:label (i18n/label :t/name-your-community) {:style {:padding-bottom 16
:text name :padding-top 10
:max-length max-name-length}] :padding-horizontal 16}}
[countable-label
{:label (i18n/label :t/name-your-community)
:text name
:max-length max-name-length}]
[quo/text-input [quo/text-input
{:placeholder (i18n/label :t/name-your-community-placeholder) {:placeholder (i18n/label :t/name-your-community-placeholder)
:default-value name :default-value name
:on-change-text #(>evt [::communities/create-field :name %]) :on-change-text #(rf/dispatch [::communities/create-field :name %])
:auto-focus true}]] :auto-focus true}]]
[rn/view {:style {:padding-bottom 16 [rn/view
:padding-top 10 {:style {:padding-bottom 16
:padding-horizontal 16}} :padding-top 10
[countable-label {:label (i18n/label :t/give-a-short-description-community) :padding-horizontal 16}}
:text description [countable-label
:max-length max-description-length}] {:label (i18n/label :t/give-a-short-description-community)
:text description
:max-length max-description-length}]
[quo/text-input [quo/text-input
{:placeholder (i18n/label :t/give-a-short-description-community) {:placeholder (i18n/label :t/give-a-short-description-community)
:multiline true :multiline true
:default-value description :default-value description
:on-change-text #(>evt [::communities/create-field :description %])}]] :on-change-text #(rf/dispatch [::communities/create-field :description %])}]]
[quo/list-header {:color :main} [quo/list-header {:color :main}
(i18n/label :t/community-thumbnail-image)] (i18n/label :t/community-thumbnail-image)]
[photo-picker] [photo-picker]
(when-not editing? [:<> (when-not editing?
[quo/separator {:style {:margin-vertical 10}}] [:<>
[quo/list-item {:title (i18n/label :t/membership-button) [quo/separator {:style {:margin-vertical 10}}]
:accessory-text (i18n/label (get-in memberships/options [membership :title] :t/membership-none)) [quo/list-item
:accessory :text {:title (i18n/label :t/membership-button)
:on-press #(>evt [:navigate-to :community-membership]) :accessory-text (i18n/label (get-in memberships/options
:chevron true [membership :title]
:size :small}] :t/membership-none))
[quo/list-footer :accessory :text
(i18n/label (get-in memberships/options [membership :description] :t/membership-none-placeholder))]])])) :on-press #(rf/dispatch [:navigate-to :community-membership])
:chevron true
:size :small}]
[quo/list-footer
(i18n/label
(get-in memberships/options [membership :description] :t/membership-none-placeholder))]])]))
(defn view [] (defn view
(let [{:keys [name description]} (<sub [:communities/create])] []
(let [{:keys [name description]} (rf/sub [:communities/create])]
[rn/keyboard-avoiding-view {:style {:flex 1}} [rn/keyboard-avoiding-view {:style {:flex 1}}
[form] [form]
[toolbar/toolbar [toolbar/toolbar
{:show-border? true {:show-border? true
:center :center
[quo/button {:disabled (not (valid? name description)) [quo/button
:type :secondary {:disabled (not (valid? name description))
:on-press #(debounce/dispatch-and-chill [::communities/create-confirmation-pressed] 3000)} :type :secondary
:on-press #(debounce/dispatch-and-chill [::communities/create-confirmation-pressed] 3000)}
(i18n/label :t/create)]}]])) (i18n/label :t/create)]}]]))
@@ -1,42 +1,46 @@
(ns status-im.ui.screens.communities.create-category (ns status-im.ui.screens.communities.create-category
(:require [status-im.ui.components.keyboard-avoid-presentation :as kb-presentation] (:require [clojure.string :as string]
[status-im.utils.handlers :refer [<sub]]
[status-im.ui.components.toolbar :as toolbar]
[status-im.ui.components.react :as react]
[quo.core :as quo] [quo.core :as quo]
[utils.debounce :as debounce] [reagent.core :as reagent]
[status-im.i18n.i18n :as i18n]
[clojure.string :as string]
[status-im.ui.components.list.views :as list]
[status-im.ui.screens.home.views.inner-item :as inner-item]
[status-im.communities.core :as communities] [status-im.communities.core :as communities]
[reagent.core :as reagent])) [status-im.i18n.i18n :as i18n]
[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.toolbar :as toolbar]
[status-im.ui.screens.home.views.inner-item :as inner-item]
[utils.debounce :as debounce]
[utils.re-frame :as rf]))
(defn valid? [category-name] (defn valid?
[category-name]
(and (not (string/blank? category-name)) (and (not (string/blank? category-name))
(<= (count category-name) 30))) (<= (count category-name) 30)))
(def selected-items (reagent/atom #{})) (def selected-items (reagent/atom #{}))
(defn render-fn [{:keys [chat-id] :as home-item}] (defn render-fn
(let [selected (get @selected-items chat-id) [{:keys [chat-id] :as home-item}]
(let [selected (get @selected-items chat-id)
on-change (fn [] on-change (fn []
(swap! selected-items #(if selected (disj % chat-id) (conj % chat-id))))] (swap! selected-items #(if selected (disj % chat-id) (conj % chat-id))))]
[react/view {:flex-direction :row :flex 1 :align-items :center} [react/view {:flex-direction :row :flex 1 :align-items :center}
[react/view {:padding-left 16} [react/view {:padding-left 16}
[quo/checkbox {:value selected [quo/checkbox
:on-change on-change}]] {:value selected
:on-change on-change}]]
[react/view {:flex 1} [react/view {:flex 1}
[inner-item/home-list-item-old [inner-item/home-list-item-old
(assoc home-item :public? true) (assoc home-item :public? true)
{:on-press on-change}]]])) {:on-press on-change}]]]))
(defn view [] (defn view
(let [{:keys [community-id]} (<sub [:get-screen-params]) []
category-name (reagent/atom "") (let [{:keys [community-id]} (rf/sub [:get-screen-params])
category-name (reagent/atom "")
_ (reset! selected-items #{})] _ (reset! selected-items #{})]
(fn [] (fn []
(let [chats (<sub [:chats/with-empty-category-by-community-id community-id])] (let [chats (rf/sub [:chats/with-empty-category-by-community-id community-id])]
[kb-presentation/keyboard-avoiding-view {:style {:flex 1}} [kb-presentation/keyboard-avoiding-view {:style {:flex 1}}
[react/view {:flex 1} [react/view {:flex 1}
[react/view {:padding-horizontal 16} [react/view {:padding-horizontal 16}
@@ -57,12 +61,13 @@
[toolbar/toolbar [toolbar/toolbar
{:show-border? true {:show-border? true
:center :center
[quo/button {:disabled (not (valid? @category-name)) [quo/button
:type :secondary {:disabled (not (valid? @category-name))
:on-press #(debounce/dispatch-and-chill :type :secondary
[::communities/create-category-confirmation-pressed :on-press #(debounce/dispatch-and-chill
community-id [::communities/create-category-confirmation-pressed
@category-name community-id
(vec @selected-items)] @category-name
3000)} (vec @selected-items)]
3000)}
(i18n/label :t/create)]}]])))) (i18n/label :t/create)]}]]))))
@@ -1,65 +1,72 @@
(ns status-im.ui.screens.communities.create-channel (ns status-im.ui.screens.communities.create-channel
(:require [clojure.string :as string] (:require [clojure.string :as string]
[quo.react-native :as rn]
[quo.core :as quo] [quo.core :as quo]
[status-im.i18n.i18n :as i18n] [quo.react-native :as rn]
[status-im.ui.components.toolbar :as toolbar]
[status-im.communities.core :as communities] [status-im.communities.core :as communities]
[utils.debounce :as debounce] [status-im.i18n.i18n :as i18n]
[status-im.utils.handlers :refer [>evt <sub]] [status-im.ui.components.emoji-thumbnail.preview :as emoji-thumbnail-preview]
[status-im.ui.screens.communities.create :as create]
[status-im.ui.components.keyboard-avoid-presentation :as kb-presentation]
[status-im.ui.components.emoji-thumbnail.styles :as styles] [status-im.ui.components.emoji-thumbnail.styles :as styles]
[status-im.ui.components.emoji-thumbnail.preview :as emoji-thumbnail-preview])) [status-im.ui.components.keyboard-avoid-presentation :as kb-presentation]
[status-im.ui.components.toolbar :as toolbar]
[status-im.ui.screens.communities.create :as create]
[utils.debounce :as debounce]
[utils.re-frame :as rf]))
(defn valid? [channel-name channel-description] (defn valid?
[channel-name channel-description]
(and (not (string/blank? channel-name)) (and (not (string/blank? channel-name))
(not (string/blank? channel-description)) (not (string/blank? channel-description))
(<= (count channel-name) create/max-name-length) (<= (count channel-name) create/max-name-length)
(<= (count channel-description) create/max-description-length))) (<= (count channel-description) create/max-description-length)))
(defn thumbnail [] (defn thumbnail
(let [{:keys [color emoji]} (<sub [:communities/create-channel]) []
size styles/emoji-thumbnail-preview-size] (let [{:keys [color emoji]} (rf/sub [:communities/create-channel])
size styles/emoji-thumbnail-preview-size]
[rn/view styles/emoji-thumbnail-preview [rn/view styles/emoji-thumbnail-preview
[emoji-thumbnail-preview/emoji-thumbnail-touchable [emoji-thumbnail-preview/emoji-thumbnail-touchable
emoji color size emoji color size
#(>evt [:open-modal :community-emoji-thumbnail-picker nil])]])) #(rf/dispatch [:open-modal :community-emoji-thumbnail-picker nil])]]))
(defn form [] (defn form
(let [{:keys [name description]} (<sub [:communities/create-channel])] []
[rn/scroll-view {:style {:flex 1} (let [{:keys [name description]} (rf/sub [:communities/create-channel])]
:content-container-style {:padding-bottom 16}} [rn/scroll-view
{:style {:flex 1}
:content-container-style {:padding-bottom 16}}
[:<> [:<>
[thumbnail] [thumbnail]
[rn/view {:padding-horizontal 16} [rn/view {:padding-horizontal 16}
[quo/text-input [quo/text-input
{:placeholder (i18n/label :t/name-your-channel-placeholder) {:placeholder (i18n/label :t/name-your-channel-placeholder)
:on-change-text #(>evt [::communities/create-channel-field :name %]) :on-change-text #(rf/dispatch [::communities/create-channel-field :name %])
:default-value name :default-value name
:auto-focus false}]] :auto-focus false}]]
[quo/separator {:style {:margin-vertical 16}}] [quo/separator {:style {:margin-vertical 16}}]
[rn/view {:padding-horizontal 16} [rn/view {:padding-horizontal 16}
[create/countable-label {:label (i18n/label :t/description) [create/countable-label
:text description {:label (i18n/label :t/description)
:max-length create/max-description-length}] :text description
:max-length create/max-description-length}]
[quo/text-input [quo/text-input
{:placeholder (i18n/label :t/give-a-short-description-community) {:placeholder (i18n/label :t/give-a-short-description-community)
:multiline true :multiline true
:default-value description :default-value description
:on-change-text #(>evt [::communities/create-channel-field :description %])}]]]])) :on-change-text #(rf/dispatch [::communities/create-channel-field :description %])}]]]]))
(defn view [] (defn view
(let [{:keys [name description]} (<sub [:communities/create-channel])] []
(let [{:keys [name description]} (rf/sub [:communities/create-channel])]
[kb-presentation/keyboard-avoiding-view {:style {:flex 1}} [kb-presentation/keyboard-avoiding-view {:style {:flex 1}}
[quo/separator] [quo/separator]
[form] [form]
[toolbar/toolbar [toolbar/toolbar
{:show-border? true {:show-border? true
:center :center
[quo/button {:disabled (not (valid? name description)) [quo/button
:type :secondary {:disabled (not (valid? name description))
:on-press #(debounce/dispatch-and-chill :type :secondary
[::communities/create-channel-confirmation-pressed] :on-press #(debounce/dispatch-and-chill
3000)} [::communities/create-channel-confirmation-pressed]
3000)}
(i18n/label :t/create)]}]])) (i18n/label :t/create)]}]]))
+11 -9
View File
@@ -1,19 +1,21 @@
(ns status-im.ui.screens.communities.edit (ns status-im.ui.screens.communities.edit
(:require [quo.core :as quo] (:require [quo.core :as quo]
[status-im.i18n.i18n :as i18n]
[status-im.ui.screens.communities.create :as community.create]
[status-im.utils.handlers :refer [>evt <sub]]
[status-im.communities.core :as communities] [status-im.communities.core :as communities]
[status-im.ui.components.toolbar :as toolbar])) [status-im.i18n.i18n :as i18n]
[status-im.ui.components.toolbar :as toolbar]
[status-im.ui.screens.communities.create :as community.create]
[utils.re-frame :as rf]))
(defn edit [] (defn edit
(let [{:keys [name description]} (<sub [:communities/create])] []
(let [{:keys [name description]} (rf/sub [:communities/create])]
[:<> [:<>
[community.create/form] [community.create/form]
[toolbar/toolbar [toolbar/toolbar
{:show-border? true {:show-border? true
:center :center
[quo/button {:disabled (not (community.create/valid? name description)) [quo/button
:type :secondary {:disabled (not (community.create/valid? name description))
:on-press #(>evt [::communities/edit-confirmation-pressed])} :type :secondary
:on-press #(rf/dispatch [::communities/edit-confirmation-pressed])}
(i18n/label :t/save)]}]])) (i18n/label :t/save)]}]]))
@@ -1,27 +1,30 @@
(ns status-im.ui.screens.communities.edit-channel (ns status-im.ui.screens.communities.edit-channel
(:require [clojure.string :as string] (:require [clojure.string :as string]
[quo.core :as quo] [quo.core :as quo]
[status-im.communities.core :as communities]
[status-im.i18n.i18n :as i18n] [status-im.i18n.i18n :as i18n]
[status-im.ui.components.toolbar :as toolbar] [status-im.ui.components.toolbar :as toolbar]
[status-im.communities.core :as communities] [status-im.ui.screens.communities.create-channel :as create-channel]
[utils.debounce :as debounce] [utils.debounce :as debounce]
[status-im.utils.handlers :refer [<sub]] [utils.re-frame :as rf]))
[status-im.ui.screens.communities.create-channel :as create-channel]))
(defn valid? [community-name] (defn valid?
[community-name]
(not (string/blank? community-name))) (not (string/blank? community-name)))
(defn view [] (defn view
(let [{:keys [name]} (<sub [:communities/create-channel])] []
(let [{:keys [name]} (rf/sub [:communities/create-channel])]
(fn [] (fn []
[:<> [:<>
[create-channel/form] [create-channel/form]
[toolbar/toolbar [toolbar/toolbar
{:show-border? true {:show-border? true
:center :center
[quo/button {:disabled (not (valid? name)) [quo/button
:type :secondary {:disabled (not (valid? name))
:on-press #(debounce/dispatch-and-chill :type :secondary
[::communities/edit-channel-confirmation-pressed] :on-press #(debounce/dispatch-and-chill
3000)} [::communities/edit-channel-confirmation-pressed]
3000)}
(i18n/label :t/save)]}]]))) (i18n/label :t/save)]}]])))
@@ -1,20 +1,23 @@
(ns status-im.ui.screens.communities.import (ns status-im.ui.screens.communities.import
(:require [quo.react-native :as rn] (:require [quo.core :as quo]
[quo.react-native :as rn]
[reagent.core :as reagent] [reagent.core :as reagent]
[quo.core :as quo]
[status-im.i18n.i18n :as i18n]
[status-im.utils.handlers :refer [>evt]]
[status-im.communities.core :as communities] [status-im.communities.core :as communities]
[status-im.ui.components.toolbar :as toolbar])) [status-im.i18n.i18n :as i18n]
[status-im.ui.components.toolbar :as toolbar]
[utils.re-frame :as rf]))
(defn view [] (defn view
[]
(let [community-key (reagent/atom "")] (let [community-key (reagent/atom "")]
(fn [] (fn []
[:<> [:<>
[rn/scroll-view {:style {:flex 1} [rn/scroll-view
:content-container-style {:padding 16}} {:style {:flex 1}
[rn/view {:style {:padding-bottom 16 :content-container-style {:padding 16}}
:padding-top 10}} [rn/view
{:style {:padding-bottom 16
:padding-top 10}}
[quo/text-input [quo/text-input
{:label (i18n/label :t/community-key) {:label (i18n/label :t/community-key)
:placeholder (i18n/label :t/community-key-placeholder) :placeholder (i18n/label :t/community-key-placeholder)
@@ -24,7 +27,8 @@
[toolbar/toolbar [toolbar/toolbar
{:show-border? true {:show-border? true
:center [quo/button {:disabled (= @community-key "") :center [quo/button
:type :secondary {:disabled (= @community-key "")
:on-press #(>evt [::communities/import @community-key])} :type :secondary
:on-press #(rf/dispatch [::communities/import @community-key])}
(i18n/label :t/import)]}]]))) (i18n/label :t/import)]}]])))
@@ -1,21 +1,24 @@
(ns status-im.ui.screens.communities.invite (ns status-im.ui.screens.communities.invite
(:require [reagent.core :as reagent] (:require [clojure.string :as string]
[quo.react-native :as rn]
[quo.core :as quo] [quo.core :as quo]
[status-im.i18n.i18n :as i18n] [quo.react-native :as rn]
[status-im.constants :as constants] [reagent.core :as reagent]
[status-im.ui.components.toolbar :as toolbar]
[status-im.utils.handlers :refer [<sub >evt-once]]
[status-im.communities.core :as communities] [status-im.communities.core :as communities]
[status-im.ui.components.topbar :as topbar] [status-im.constants :as constants]
[status-im.ui.components.chat-icon.screen :as chat-icon.screen] [status-im.i18n.i18n :as i18n]
[status-im.multiaccounts.core :as multiaccounts] [status-im.multiaccounts.core :as multiaccounts]
[clojure.string :as string])) [status-im.ui.components.chat-icon.screen :as chat-icon.screen]
[status-im.ui.components.toolbar :as toolbar]
[status-im.ui.components.topbar :as topbar]
[utils.debounce :as debounce]
[utils.re-frame :as rf]))
(defn header [user-pk] (defn header
[user-pk]
[:<> [:<>
[rn/view {:style {:padding-horizontal 16 [rn/view
:padding-vertical 8}} {:style {:padding-horizontal 16
:padding-vertical 8}}
[quo/text-input [quo/text-input
{:label (i18n/label :t/enter-user-pk) {:label (i18n/label :t/enter-user-pk)
:placeholder (i18n/label :t/enter-user-pk) :placeholder (i18n/label :t/enter-user-pk)
@@ -25,7 +28,8 @@
[quo/separator {:style {:margin-vertical 8}}] [quo/separator {:style {:margin-vertical 8}}]
[quo/list-header (i18n/label :t/contacts)]]) [quo/list-header (i18n/label :t/contacts)]])
(defn contacts-list-item [{:keys [public-key active] :as contact} _ _ {:keys [selected]}] (defn contacts-list-item
[{:keys [public-key active] :as contact} _ _ {:keys [selected]}]
(let [[first-name second-name] (multiaccounts/contact-two-names contact true)] (let [[first-name second-name] (multiaccounts/contact-two-names contact true)]
[quo/list-item [quo/list-item
{:title first-name {:title first-name
@@ -39,44 +43,51 @@
(swap! selected disj public-key) (swap! selected disj public-key)
(swap! selected conj public-key)))}])) (swap! selected conj public-key)))}]))
(defn invite [] (defn invite
[]
(let [user-pk (reagent/atom "") (let [user-pk (reagent/atom "")
contacts-selected (reagent/atom #{}) contacts-selected (reagent/atom #{})
{:keys [invite?]} (<sub [:get-screen-params])] {:keys [invite?]} (rf/sub [:get-screen-params])]
(fn [] (fn []
(let [contacts-data (<sub [:contacts/active]) (let [contacts-data (rf/sub [:contacts/active])
{:keys [permissions {:keys [permissions
can-manage-users?]} (<sub [:communities/edited-community]) can-manage-users?]}
selected @contacts-selected (rf/sub [:communities/edited-community])
contacts (map (fn [{:keys [public-key] :as contact}] selected @contacts-selected
(assoc contact :active (contains? selected public-key))) contacts (map (fn [{:keys [public-key] :as contact}]
contacts-data) (assoc contact :active (contains? selected public-key)))
contacts-data)
;; no-membership communities can only be shared ;; no-membership communities can only be shared
can-invite? (and can-manage-users? can-invite? (and can-manage-users?
invite? invite?
(not= (:access permissions) constants/community-no-membership-access))] (not= (:access permissions) constants/community-no-membership-access))]
[:<> [:<>
[topbar/topbar {:title (i18n/label (if can-invite? [topbar/topbar
:t/invite-people {:title (i18n/label (if can-invite?
:t/community-share-title)) :t/invite-people
:modal? true}] :t/community-share-title))
[rn/flat-list {:style {:flex 1} :modal? true}]
:content-container-style {:padding-vertical 16} [rn/flat-list
;:header [header user-pk] {:style {:flex 1}
:render-data {:selected contacts-selected} :content-container-style {:padding-vertical 16}
:render-fn contacts-list-item ;:header [header user-pk]
:key-fn (fn [{:keys [active public-key]}] :render-data {:selected contacts-selected}
(str public-key active)) :render-fn contacts-list-item
:data contacts}] :key-fn (fn [{:keys [active public-key]}]
(str public-key active))
:data contacts}]
[toolbar/toolbar [toolbar/toolbar
{:show-border? true {:show-border? true
:center :center
[quo/button {:disabled (and (string/blank? @user-pk) [quo/button
{:disabled (and (string/blank? @user-pk)
(zero? (count selected))) (zero? (count selected)))
:accessibility-label :share-community-link :accessibility-label :share-community-link
:type :secondary :type :secondary
:on-press #(>evt-once :on-press #(debounce/dispatch-and-chill
[(if can-invite? [(if can-invite?
::communities/invite-people-confirmation-pressed ::communities/invite-people-confirmation-pressed
::communities/share-community-confirmation-pressed) @user-pk selected])} ::communities/share-community-confirmation-pressed) @user-pk
selected]
3000)}
(i18n/label (if can-invite? :t/invite :t/share))]}]])))) (i18n/label (if can-invite? :t/invite :t/share))]}]]))))
@@ -1,22 +1,24 @@
(ns status-im.ui.screens.communities.members (ns status-im.ui.screens.communities.members
(:require [quo.react-native :as rn] (:require [quo.core :as quo]
[quo.core :as quo] [quo.react-native :as rn]
[reagent.core :as reagent] [reagent.core :as reagent]
[status-im.communities.core :as communities]
[status-im.constants :as constants] [status-im.constants :as constants]
[status-im.ui.components.react :as react]
[status-im.utils.handlers :refer [<sub >evt]]
[status-im.ui.components.chat-icon.screen :as chat-icon]
[status-im.ui.components.unviewed-indicator :as unviewed-indicator]
[status-im.multiaccounts.core :as multiaccounts]
[status-im.ui.components.topbar :as topbar]
[status-im.i18n.i18n :as i18n] [status-im.i18n.i18n :as i18n]
[status-im.communities.core :as communities])) [status-im.multiaccounts.core :as multiaccounts]
[status-im.ui.components.chat-icon.screen :as chat-icon]
[status-im.ui.components.react :as react]
[status-im.ui.components.topbar :as topbar]
[status-im.ui.components.unviewed-indicator :as unviewed-indicator]
[utils.re-frame :as rf]))
(defn hide-sheet-and-dispatch [event] (defn hide-sheet-and-dispatch
(>evt [:bottom-sheet/hide]) [event]
(>evt event)) (rf/dispatch [:bottom-sheet/hide])
(rf/dispatch event))
(defn member-sheet [first-name {:keys [public-key] :as member} community-id can-kick-users? can-manage-users? admin?] (defn member-sheet
[first-name {:keys [public-key] :as member} community-id can-kick-users? can-manage-users? admin?]
[:<> [:<>
[quo/list-item [quo/list-item
{:theme :accent {:theme :accent
@@ -30,30 +32,36 @@
(when can-kick-users? (when can-kick-users?
[:<> [:<>
[quo/separator {:style {:margin-vertical 8}}] [quo/separator {:style {:margin-vertical 8}}]
[quo/list-item {:theme :negative [quo/list-item
:icon :main-icons/arrow-left {:theme :negative
:title (i18n/label :t/member-kick) :icon :main-icons/arrow-left
:on-press #(>evt [::communities/member-kick community-id public-key])}]]) :title (i18n/label :t/member-kick)
:on-press #(rf/dispatch [::communities/member-kick community-id public-key])}]])
(when can-manage-users? (when can-manage-users?
[:<> [:<>
[quo/list-item {:theme :negative [quo/list-item
:icon :main-icons/cancel {:theme :negative
:title (i18n/label :t/member-ban) :icon :main-icons/cancel
:on-press #(>evt [::communities/member-ban community-id public-key])}]]) :title (i18n/label :t/member-ban)
:on-press #(rf/dispatch [::communities/member-ban community-id public-key])}]])
(when admin? (when admin?
[:<> [:<>
[quo/list-item {:theme :accent [quo/list-item
:icon :main-icons/make-admin {:theme :accent
:title (i18n/label :t/make-moderator) :icon :main-icons/make-admin
:on-press #(>evt [:community.member/add-role community-id public-key constants/community-member-role-moderator])}]])]) :title (i18n/label :t/make-moderator)
:on-press #(rf/dispatch [:community.member/add-role community-id public-key
constants/community-member-role-moderator])}]])])
(defn render-member [public-key _ _ {:keys [community-id (defn render-member
my-public-key [public-key _ _
can-manage-users? {:keys [community-id
can-kick-users? my-public-key
admin?]}] can-manage-users?
(let [member (<sub [:contacts/contact-by-identity public-key]) can-kick-users?
[first-name second-name] (<sub [:contacts/contact-two-names-by-identity public-key])] admin?]}]
(let [member (rf/sub [:contacts/contact-by-identity public-key])
[first-name second-name] (rf/sub [:contacts/contact-two-names-by-identity public-key])]
[quo/list-item [quo/list-item
{:title first-name {:title first-name
:subtitle second-name :subtitle second-name
@@ -62,66 +70,78 @@
{:public-key public-key {:public-key public-key
:photo-path (multiaccounts/displayed-photo member)}] :photo-path (multiaccounts/displayed-photo member)}]
:accessory (when (not= public-key my-public-key) :accessory (when (not= public-key my-public-key)
[quo/button {:on-press [quo/button
#(>evt [:bottom-sheet/show-sheet {:on-press
{:content (fn [] #(rf/dispatch [:bottom-sheet/show-sheet
[member-sheet first-name member community-id can-kick-users? can-manage-users? admin?])}]) {:content (fn []
:type :icon [member-sheet first-name member community-id
:theme :icon can-kick-users? can-manage-users? admin?])}])
:accessibility-label :menu-option} :type :icon
:theme :icon
:accessibility-label :menu-option}
:main-icons/more])}])) :main-icons/more])}]))
(defn header [community-id] (defn header
[community-id]
[:<> [:<>
[quo/list-item {:icon :main-icons/share [quo/list-item
:title (i18n/label :t/invite-people) {:icon :main-icons/share
:accessibility-label :community-invite-people :title (i18n/label :t/invite-people)
:theme :accent :accessibility-label :community-invite-people
:on-press #(>evt [:communities/invite-people-pressed community-id])}] :theme :accent
:on-press #(rf/dispatch [:communities/invite-people-pressed community-id])}]
[quo/separator {:style {:margin-vertical 8}}]]) [quo/separator {:style {:margin-vertical 8}}]])
(defn requests-to-join [community-id] (defn requests-to-join
(let [requests (<sub [:communities/requests-to-join-for-community community-id]) [community-id]
(let [requests (rf/sub [:communities/requests-to-join-for-community community-id])
requests-count (count requests)] requests-count (count requests)]
[:<> [:<>
[quo/list-item {:chevron true [quo/list-item
:accessory {:chevron true
[react/view {:flex-direction :row} :accessory
(when (pos? requests-count) [react/view {:flex-direction :row}
[unviewed-indicator/unviewed-indicator requests-count])] (when (pos? requests-count)
:on-press #(>evt [:navigate-to :community-requests-to-join {:community-id community-id}]) [unviewed-indicator/unviewed-indicator requests-count])]
:title (i18n/label :t/membership-requests)}] :on-press #(rf/dispatch [:navigate-to :community-requests-to-join {:community-id community-id}])
:title (i18n/label :t/membership-requests)}]
[quo/separator {:style {:margin-vertical 8}}]])) [quo/separator {:style {:margin-vertical 8}}]]))
(defn members [] (defn members
(let [{:keys [community-id]} (<sub [:get-screen-params])] []
(let [{:keys [community-id]} (rf/sub [:get-screen-params])]
(fn [] (fn []
(let [my-public-key (<sub [:multiaccount/public-key]) (let [my-public-key (rf/sub [:multiaccount/public-key])
{:keys [permissions {:keys [permissions
can-manage-users? can-manage-users?
admin]} (<sub [:communities/community community-id]) admin]}
sorted-members (<sub [:communities/sorted-community-members (rf/sub [:communities/community community-id])
community-id])] sorted-members (rf/sub [:communities/sorted-community-members
community-id])]
[:<> [:<>
[topbar/topbar {:title (i18n/label :t/community-members-title) [topbar/topbar
:subtitle (str (count sorted-members))}] {:title (i18n/label :t/community-members-title)
:subtitle (str (count sorted-members))}]
[header community-id] [header community-id]
(when (and can-manage-users? (= constants/community-on-request-access (:access permissions))) (when (and can-manage-users? (= constants/community-on-request-access (:access permissions)))
[requests-to-join community-id]) [requests-to-join community-id])
[rn/flat-list {:data (keys sorted-members) [rn/flat-list
:render-data {:community-id community-id {:data (keys sorted-members)
:my-public-key my-public-key :render-data {:community-id community-id
:can-kick-users? (and can-manage-users? :my-public-key my-public-key
(not= (:access permissions) :can-kick-users? (and can-manage-users?
constants/community-no-membership-access)) (not= (:access permissions)
:can-manage-users? can-manage-users? constants/community-no-membership-access))
:admin? admin} :can-manage-users? can-manage-users?
:key-fn identity :admin? admin}
:render-fn render-member}]])))) :key-fn identity
:render-fn render-member}]]))))
(defn members-container [] (defn members-container
[]
(reagent/create-class (reagent/create-class
{:display-name "community-members-view" {:display-name "community-members-view"
:component-did-mount (fn [] :component-did-mount (fn []
(communities/fetch-requests-to-join! (get (<sub [:get-screen-params]) :community-id))) (communities/fetch-requests-to-join! (get (rf/sub [:get-screen-params])
:community-id)))
:reagent-render members})) :reagent-render members}))
@@ -1,41 +1,47 @@
(ns status-im.ui.screens.communities.membership (ns status-im.ui.screens.communities.membership
(:require [quo.react-native :as rn] (:require [quo.core :as quo]
[status-im.ui.components.toolbar :as toolbar] [quo.react-native :as rn]
[quo.core :as quo]
[status-im.i18n.i18n :as i18n]
[status-im.utils.handlers :refer [>evt <sub]]
[status-im.communities.core :as communities] [status-im.communities.core :as communities]
[status-im.constants :as constants])) [status-im.constants :as constants]
[status-im.i18n.i18n :as i18n]
[status-im.ui.components.toolbar :as toolbar]
[utils.re-frame :as rf]))
(def options {constants/community-on-request-access (def options
{:title :t/membership-approval {constants/community-on-request-access
:description :t/membership-approval-description} {:title :t/membership-approval
constants/community-no-membership-access :description :t/membership-approval-description}
{:title :t/membership-free constants/community-no-membership-access
:description :t/membership-free-description}}) {:title :t/membership-free
:description :t/membership-free-description}})
(defn option [{:keys [title description]} {:keys [selected on-select]}] (defn option
[{:keys [title description]} {:keys [selected on-select]}]
[:<> [:<>
[quo/list-item {:title (i18n/label title) [quo/list-item
:size :small {:title (i18n/label title)
:accessory :radio :size :small
:active selected :accessory :radio
:on-press on-select}] :active selected
:on-press on-select}]
[quo/list-footer [quo/list-footer
(i18n/label description)] (i18n/label description)]
[quo/separator {:style {:margin-vertical 8}}]]) [quo/separator {:style {:margin-vertical 8}}]])
(defn membership [] (defn membership
(let [{:keys [membership]} (<sub [:communities/create])] []
(let [{:keys [membership]} (rf/sub [:communities/create])]
[:<> [:<>
[rn/scroll-view {} [rn/scroll-view {}
(doall (doall
(for [[id o] options] (for [[id o] options]
^{:key (str "option-" id)} ^{:key (str "option-" id)}
[option o {:selected (= id membership) [option o
:on-select #(>evt [::communities/create-field :membership id])}]))] {:selected (= id membership)
:on-select #(rf/dispatch [::communities/create-field :membership id])}]))]
[toolbar/toolbar [toolbar/toolbar
{:show-border? true {:show-border? true
:center [quo/button {:type :secondary :center [quo/button
:on-press #(>evt [:navigate-back])} {:type :secondary
:on-press #(rf/dispatch [:navigate-back])}
(i18n/label :t/done)]}]])) (i18n/label :t/done)]}]]))
@@ -1,52 +1,57 @@
(ns status-im.ui.screens.communities.profile (ns status-im.ui.screens.communities.profile
(:require [quo.core :as quo] (:require [clojure.string :as string]
[status-im.utils.handlers :refer [>evt <sub]] [quo.core :as quo]
[status-im.ui.components.profile-header.view :as profile-header]
[status-im.i18n.i18n :as i18n]
[reagent.core :as reagent]
[status-im.ui.components.copyable-text :as copyable-text]
[status-im.communities.core :as communities]
[quo.design-system.colors :as colors] [quo.design-system.colors :as colors]
[status-im.ui.components.react :as react]
[status-im.constants :as constants]
[status-im.react-native.resources :as resources]
[status-im.ui.components.unviewed-indicator :as unviewed-indicator]
[quo.react-native :as rn] [quo.react-native :as rn]
[clojure.string :as string])) [reagent.core :as reagent]
[status-im.communities.core :as communities]
[status-im.constants :as constants]
[status-im.i18n.i18n :as i18n]
[status-im.react-native.resources :as resources]
[status-im.ui.components.copyable-text :as copyable-text]
[status-im.ui.components.profile-header.view :as profile-header]
[status-im.ui.components.react :as react]
[status-im.ui.components.unviewed-indicator :as unviewed-indicator]
[utils.re-frame :as rf]))
(defn management [] (defn management
(let [{:keys [community-id]} (<sub [:get-screen-params])] []
(let [{:keys [community-id]} (rf/sub [:get-screen-params])]
(fn [] (fn []
(let [requests-to-join (<sub [:communities/requests-to-join-for-community community-id]) (let [requests-to-join (rf/sub [:communities/requests-to-join-for-community community-id])
community (<sub [:communities/community community-id]) community (rf/sub [:communities/community community-id])
{:keys [color members permissions description name admin]} community {:keys [color members permissions description name admin]} community
roles false roles false
notifications false notifications false
show-members-count? (not= (:access permissions) constants/community-no-membership-access) show-members-count? (not= (:access permissions) constants/community-no-membership-access)
request-membership? (= (:access permissions) constants/community-on-request-access) request-membership? (= (:access permissions) constants/community-on-request-access)
members-count (count members)] members-count (count members)]
[:<> [:<>
[quo/animated-header {:left-accessories [{:icon :main-icons/arrow-left [quo/animated-header
:accessibility-label :back-button {:left-accessories [{:icon :main-icons/arrow-left
:on-press #(>evt [:navigate-back])}] :accessibility-label :back-button
:right-accessories [{:icon :main-icons/share :on-press #(rf/dispatch [:navigate-back])}]
:accessibility-label :invite-button :right-accessories [{:icon :main-icons/share
:on-press #(>evt [:communities/share-community-pressed community-id])}] :accessibility-label :invite-button
:extended-header (profile-header/extended-header :on-press #(rf/dispatch [:communities/share-community-pressed
{:title name community-id])}]
:color (or color (rand-nth colors/chat-colors)) :extended-header (profile-header/extended-header
:photo (if (= community-id constants/status-community-id) {:title name
(:uri :color (or color (rand-nth colors/chat-colors))
(rn/resolve-asset-source :photo (if (= community-id constants/status-community-id)
(resources/get-image :status-logo))) (:uri
(get-in community [:images :large :uri])) (rn/resolve-asset-source
:membership (when request-membership? (resources/get-image :status-logo)))
(i18n/label :t/membership-approval)) (get-in community [:images :large :uri]))
:subtitle (if show-members-count? :membership (when request-membership?
(i18n/label-pluralize members-count :t/community-members {:count members-count}) (i18n/label :t/membership-approval))
(i18n/label :t/open-membership)) :subtitle (if show-members-count?
:community? true}) (i18n/label-pluralize members-count
:use-insets true} :t/community-members
{:count members-count})
(i18n/label :t/open-membership))
:community? true})
:use-insets true}
[:<> [:<>
(when-not (string/blank? description) (when-not (string/blank? description)
[:<> [:<>
@@ -55,57 +60,67 @@
[quo/separator {:style {:margin-vertical 8}}]]) [quo/separator {:style {:margin-vertical 8}}]])
[:<> [:<>
(let [link (communities/universal-link community-id)] (let [link (communities/universal-link community-id)]
[react/view {:padding-vertical 10 [react/view
:padding-horizontal 16} {:padding-vertical 10
:padding-horizontal 16}
[react/view {:margin-bottom 20} [react/view {:margin-bottom 20}
[quo/text {:color :secondary} (i18n/label :t/community-link)]] [quo/text {:color :secondary} (i18n/label :t/community-link)]]
[copyable-text/copyable-text-view [copyable-text/copyable-text-view
{:copied-text link} {:copied-text link}
[react/view {:border-radius 16 [react/view
:padding-horizontal 16 {:border-radius 16
:padding-vertical 11 :padding-horizontal 16
:background-color colors/blue-light} :padding-vertical 11
:background-color colors/blue-light}
[quo/text {:color :link} (subs link 8)]]]]) [quo/text {:color :link} (subs link 8)]]]])
[quo/separator {:style {:margin-vertical 8}}]] [quo/separator {:style {:margin-vertical 8}}]]
(when show-members-count? (when show-members-count?
[quo/list-item {:chevron true [quo/list-item
:accessory {:chevron true
[react/view {:flex-direction :row} :accessory
(when (pos? members-count) [react/view {:flex-direction :row}
[quo/text {:color :secondary} (str members-count)]) (when (pos? members-count)
[unviewed-indicator/unviewed-indicator (count requests-to-join)]] [quo/text {:color :secondary} (str members-count)])
:on-press #(>evt [:navigate-to :community-members {:community-id community-id}]) [unviewed-indicator/unviewed-indicator (count requests-to-join)]]
:title (i18n/label :t/members-label) :on-press #(rf/dispatch [:navigate-to :community-members {:community-id community-id}])
:icon :main-icons/group-chat}]) :title (i18n/label :t/members-label)
:icon :main-icons/group-chat}])
(when (and admin roles) (when (and admin roles)
[quo/list-item {:chevron true [quo/list-item
:title (i18n/label :t/commonuity-role) {:chevron true
:icon :main-icons/objects}]) :title (i18n/label :t/commonuity-role)
:icon :main-icons/objects}])
(when notifications (when notifications
[quo/list-item {:chevron true [quo/list-item
:title (i18n/label :t/chat-notification-preferences) {:chevron true
:icon :main-icons/notification}]) :title (i18n/label :t/chat-notification-preferences)
:icon :main-icons/notification}])
(when (or show-members-count? notifications (and admin roles)) (when (or show-members-count? notifications (and admin roles))
[quo/separator {:style {:margin-vertical 8}}]) [quo/separator {:style {:margin-vertical 8}}])
(when admin (when admin
[quo/list-item {:theme :accent [quo/list-item
:icon :main-icons/edit {:theme :accent
:title (i18n/label :t/edit-community) :icon :main-icons/edit
:on-press #(>evt [::communities/open-edit-community community-id])}]) :title (i18n/label :t/edit-community)
[quo/list-item {:theme :accent :on-press #(rf/dispatch [::communities/open-edit-community community-id])}])
:icon :main-icons/arrow-left [quo/list-item
:title (i18n/label :t/leave-community) {:theme :accent
:on-press #(>evt [:communities/leave community-id])}] :icon :main-icons/arrow-left
:title (i18n/label :t/leave-community)
:on-press #(rf/dispatch [:communities/leave community-id])}]
;; Disable as not implemented yet ;; Disable as not implemented yet
(when false (when false
[quo/list-item {:theme :negative [quo/list-item
:icon :main-icons/delete {:theme :negative
:title (i18n/label :t/delete) :icon :main-icons/delete
:on-press #(>evt [::communities/delete-community community-id])}])]]])))) :title (i18n/label :t/delete)
:on-press #(rf/dispatch [::communities/delete-community community-id])}])]]]))))
(defn management-container [] (defn management-container
[]
(reagent/create-class (reagent/create-class
{:display-name "community-profile-view" {:display-name "community-profile-view"
:component-did-mount (fn [] :component-did-mount (fn []
(communities/fetch-requests-to-join! (get (<sub [:get-screen-params]) :community-id))) (communities/fetch-requests-to-join! (get (rf/sub [:get-screen-params])
:reagent-render management})) :community-id)))
:reagent-render management}))
@@ -1,43 +1,46 @@
(ns status-im.ui.screens.communities.reorder-categories (ns status-im.ui.screens.communities.reorder-categories
(:require [quo.core :as quo] (:require [clojure.set :as clojure.set]
[clojure.walk :as walk]
[quo.react-native :as rn]
[reagent.core :as reagent]
[clojure.string :as string] [clojure.string :as string]
[clojure.set :as clojure.set] [clojure.walk :as walk]
[status-im.i18n.i18n :as i18n] [quo.core :as quo]
[status-im.utils.utils :as utils]
[status-im.constants :as constants]
[quo.design-system.colors :as colors] [quo.design-system.colors :as colors]
[status-im.utils.platform :as platform] [quo.react-native :as rn]
[status-im.communities.core :as communities]
[status-im.utils.handlers :refer [>evt <sub]]
[status-im.ui.components.icons.icons :as icons]
[quo2.components.community.style :as styles] [quo2.components.community.style :as styles]
[status-im.ui.components.topbar :as topbar] [reagent.core :as reagent]
[status-im.communities.core :as communities]
[status-im.constants :as constants]
[status-im.i18n.i18n :as i18n]
[status-im.ui.components.icons.icons :as icons]
[status-im.ui.components.react :as react] [status-im.ui.components.react :as react]
[status-im.ui.components.tabs :as tabs] [status-im.ui.components.tabs :as tabs]
[status-im.ui.components.topbar :as topbar]
[status-im.ui.screens.communities.community :as community] [status-im.ui.screens.communities.community :as community]
[status-im.ui.screens.home.views.inner-item :as inner-item])) [status-im.ui.screens.home.views.inner-item :as inner-item]
[status-im.utils.platform :as platform]
[status-im.utils.utils :as utils]
[utils.re-frame :as rf]))
(def data (reagent/atom [])) (def data (reagent/atom []))
(def state (reagent/atom {:tab :chats})) (def state (reagent/atom {:tab :chats}))
(defn show-delete-chat-confirmation [community-id chat-id] (defn show-delete-chat-confirmation
[community-id chat-id]
(utils/show-confirmation (utils/show-confirmation
{:title (i18n/label :t/delete-confirmation) {:title (i18n/label :t/delete-confirmation)
:content (i18n/label :t/delete-chat-confirmation) :content (i18n/label :t/delete-chat-confirmation)
:confirm-button-text (i18n/label :t/delete) :confirm-button-text (i18n/label :t/delete)
:on-accept #(>evt [:delete-community-chat community-id chat-id])})) :on-accept #(rf/dispatch [:delete-community-chat community-id chat-id])}))
(defn show-delete-category-confirmation [community-id category-id] (defn show-delete-category-confirmation
[community-id category-id]
(utils/show-confirmation (utils/show-confirmation
{:title (i18n/label :t/delete-confirmation) {:title (i18n/label :t/delete-confirmation)
:content (i18n/label :t/delete-category-confirmation) :content (i18n/label :t/delete-category-confirmation)
:confirm-button-text (i18n/label :t/delete) :confirm-button-text (i18n/label :t/delete)
:on-accept #(>evt [:delete-community-category community-id category-id])})) :on-accept #(rf/dispatch [:delete-community-category community-id category-id])}))
(defn categories-tab? [] (defn categories-tab?
[]
(let [{:keys [tab]} @state] (let [{:keys [tab]} @state]
(= tab :categories))) (= tab :categories)))
@@ -46,19 +49,21 @@
(let [chat-id (string/replace id community-id "") (let [chat-id (string/replace id community-id "")
background-color (if is-active? colors/gray-lighter colors/white) background-color (if is-active? colors/gray-lighter colors/white)
home-item (clojure.set/rename-keys home-item {:id :chat-id})] home-item (clojure.set/rename-keys home-item {:id :chat-id})]
[rn/view {:accessibility-label :chat-item [rn/view
:style (merge styles/category-item {:accessibility-label :chat-item
{:background-color background-color})} :style (merge styles/category-item
{:background-color background-color})}
[rn/touchable-opacity [rn/touchable-opacity
{:accessibility-label :delete-community-chat {:accessibility-label :delete-community-chat
:on-press #(show-delete-chat-confirmation community-id chat-id)} :on-press #(show-delete-chat-confirmation community-id chat-id)}
[icons/icon :main-icons/delete-circle {:no-color true}]] [icons/icon :main-icons/delete-circle {:no-color true}]]
[rn/view {:flex 1} [rn/view {:flex 1}
[inner-item/home-list-item-old (assoc home-item :edit? true) {:active-opacity 1}]] [inner-item/home-list-item-old (assoc home-item :edit? true) {:active-opacity 1}]]
[rn/touchable-opacity {:on-long-press drag [rn/touchable-opacity
:delay-long-press 100 {:on-long-press drag
:accessibility-label :chat-drag-handle :delay-long-press 100
:style {:padding 20}} :accessibility-label :chat-drag-handle
:style {:padding 20}}
[icons/icon :main-icons/reorder-handle {:no-color true :width 18 :height 12}]]])) [icons/icon :main-icons/reorder-handle {:no-color true :width 18 :height 12}]]]))
(defn category-item (defn category-item
@@ -67,9 +72,10 @@
category-none? (string/blank? id)] category-none? (string/blank? id)]
[:<> [:<>
[quo/separator] [quo/separator]
[rn/view {:accessibility-label :category-item [rn/view
:style (merge styles/category-item {:accessibility-label :category-item
{:background-color background-color})} :style (merge styles/category-item
{:background-color background-color})}
(if (not (categories-tab?)) (if (not (categories-tab?))
[icons/icon :main-icons/channel-category {:color colors/gray}] [icons/icon :main-icons/channel-category {:color colors/gray}]
[rn/touchable-opacity [rn/touchable-opacity
@@ -79,10 +85,11 @@
[rn/view {:flex 1} [rn/view {:flex 1}
[rn/text {:style {:font-size 17 :margin-left 10 :color colors/black}} name]] [rn/text {:style {:font-size 17 :margin-left 10 :color colors/black}} name]]
(when (and (not category-none?) (categories-tab?)) (when (and (not category-none?) (categories-tab?))
[rn/touchable-opacity {:accessibility-label :category-drag-handle [rn/touchable-opacity
:on-long-press drag {:accessibility-label :category-drag-handle
:delay-long-press 100 :on-long-press drag
:style {:padding 20}} :delay-long-press 100
:style {:padding 20}}
[icons/icon :main-icons/reorder-handle {:no-color true :width 18 :height 12}]])]])) [icons/icon :main-icons/reorder-handle {:no-color true :width 18 :height 12}]])]]))
(defn render-fn (defn render-fn
@@ -98,80 +105,105 @@
(if going-up? (if going-up?
(if (= chat-type constants/community-chat-type) (if (= chat-type constants/community-chat-type)
[categoryID (if second-call? (inc position) position)] [categoryID (if second-call? (inc position) position)]
(if second-call? [id 0] (if second-call?
(calculate-chat-new-position-and-category (dec to) true old-category true))) [id 0]
(calculate-chat-new-position-and-category (dec to) true old-category true)))
(if (= chat-type constants/community-chat-type) (if (= chat-type constants/community-chat-type)
(if (= categoryID old-category) (if (= categoryID old-category)
[categoryID position] [categoryID position]
[categoryID (inc position)]) [id 0]))] [categoryID (inc position)])
[id 0]))]
[new-category new-position])) [new-category new-position]))
(defn update-local-atom [data-js] (defn update-local-atom
[data-js]
(reset! data data-js) (reset! data data-js)
(reagent/flush)) (reagent/flush))
(defn on-drag-end-chat [from to data-js] (defn on-drag-end-chat
[from to data-js]
(let [{:keys [id community-id categoryID position]} (get @data from) (let [{:keys [id community-id categoryID position]} (get @data from)
[new-category new-position] (calculate-chat-new-position-and-category [new-category new-position] (calculate-chat-new-position-and-category
to false categoryID (> from to)) to
false
categoryID
(> from to))
chat-id (string/replace id community-id "")] chat-id (string/replace id community-id "")]
(when-not (and (= new-position position) (= new-category categoryID)) (when-not (and (= new-position position) (= new-category categoryID))
(update-local-atom data-js) (update-local-atom data-js)
(>evt [::communities/reorder-community-category-chat (rf/dispatch [::communities/reorder-community-category-chat
community-id new-category chat-id new-position])))) community-id new-category chat-id new-position]))))
(defn on-drag-end-category [from to data-js] (defn on-drag-end-category
[from to data-js]
(let [{:keys [id community-id position]} (get @data from)] (let [{:keys [id community-id position]} (get @data from)]
(when (and (< to (count @data)) (not= position to) (not= id "")) (when (and (< to (count @data)) (not= position to) (not= id ""))
(update-local-atom data-js) (update-local-atom data-js)
(>evt [::communities/reorder-community-category community-id id to])))) (rf/dispatch [::communities/reorder-community-category community-id id to]))))
(defn on-drag-end-fn [from to data-js] (defn on-drag-end-fn
[from to data-js]
(if (categories-tab?) (if (categories-tab?)
(on-drag-end-category from to data-js) (on-drag-end-category from to data-js)
(when-not (= to 0) (on-drag-end-chat from to data-js)))) (when-not (= to 0) (on-drag-end-chat from to data-js))))
(defn reset-data [categories chats] (defn reset-data
(reset! data (if (categories-tab?) [categories chats]
categories (reset! data
(walk/postwalk-replace (if (categories-tab?)
{:chat-id :id} categories
(reduce (fn [acc category] (walk/postwalk-replace
(-> acc {:chat-id :id}
(conj category) (reduce (fn [acc category]
(into (get chats (:id category))))) [] categories))))) (-> acc
(conj category)
(into (get chats (:id category)))))
[]
categories)))))
(defn draggable-list [] (defn draggable-list
[]
[rn/draggable-flat-list [rn/draggable-flat-list
{:key-fn :id {:key-fn :id
:data @data :data @data
:render-fn render-fn :render-fn render-fn
:autoscroll-threshold (if platform/android? 150 250) :autoscroll-threshold (if platform/android? 150 250)
:autoscroll-speed (if platform/android? 10 150) ;; TODO - Use same speed for both ios and android :autoscroll-speed (if platform/android? 10 150) ;; TODO - Use same speed for both ios and android
:container-style {:margin-bottom 108} ;; after bumping react native version to > 0.64 :container-style {:margin-bottom 108} ;; after bumping react native version to >
;; 0.64
:on-drag-end-fn on-drag-end-fn}]) :on-drag-end-fn on-drag-end-fn}])
(defn chats_and_categories [] (defn chats_and_categories
[]
(let [{:keys [tab]} @state] (let [{:keys [tab]} @state]
[:<> [:<>
[react/view {:flex-direction :row :margin-horizontal 16 :margin-vertical 8 [react/view
:border-radius 8 :background-color colors/blue-light} {:flex-direction :row
:margin-horizontal 16
:margin-vertical 8
:border-radius 8
:background-color colors/blue-light}
[tabs/tab-button state :chats (i18n/label :t/edit-chats) (= tab :chats)] [tabs/tab-button state :chats (i18n/label :t/edit-chats) (= tab :chats)]
[tabs/tab-button state :categories (i18n/label :t/edit-categories) (= tab :categories)]]])) [tabs/tab-button state :categories (i18n/label :t/edit-categories) (= tab :categories)]]]))
(defn view [] (defn view
(let [{:keys [community-id]} (<sub [:get-screen-params]) []
(let [{:keys [community-id]} (rf/sub [:get-screen-params])
{:keys [id name images members permissions color]} {:keys [id name images members permissions color]}
(<sub [:communities/community community-id]) (rf/sub [:communities/community community-id])
sorted-categories (<sub [:communities/sorted-categories community-id]) sorted-categories (rf/sub [:communities/sorted-categories
categories (if (categories-tab?) community-id])
sorted-categories categories (if (categories-tab?)
(conj sorted-categories sorted-categories
{:id "" (conj sorted-categories
:position (count sorted-categories) {:id ""
:name (i18n/label :t/none) :position (count
:community-id community-id})) sorted-categories)
chats (<sub [:chats/sorted-categories-by-community-id community-id])] :name (i18n/label :t/none)
:community-id community-id}))
chats (rf/sub
[:chats/sorted-categories-by-community-id
community-id])]
(reset-data categories chats) (reset-data categories chats)
[:<> [:<>
[topbar/topbar [topbar/topbar
@@ -182,4 +214,4 @@
[community/blank-page (i18n/label :t/welcome-community-blank-message-edit-chats)] [community/blank-page (i18n/label :t/welcome-community-blank-message-edit-chats)]
[:<> [:<>
[chats_and_categories] [chats_and_categories]
[draggable-list]])])) [draggable-list]])]))

Some files were not shown because too many files have changed in this diff Show More