Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0aede3510d |
@@ -1,7 +1,6 @@
|
||||
{:lint-as {status-im.utils.views/defview clojure.core/defn
|
||||
status-im.utils.views/letsubs 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
|
||||
quo.react/with-deps-check clojure.core/fn
|
||||
quo.previews.preview/list-comp clojure.core/for
|
||||
@@ -12,7 +11,7 @@
|
||||
:linters {:consistent-alias {:level :error
|
||||
:aliases {clojure.string string
|
||||
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
|
||||
;; https://github.com/borkdude/clj-kondo/issues/867
|
||||
:unresolved-symbol {:exclude [PersistentPriorityMap.EMPTY
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
(ns status-im.add-new.core
|
||||
(:require [clojure.string :as string]
|
||||
[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.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.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-im.ethereum.stateofus :as stateofus]
|
||||
[status-im.utils.db :as utils.db]))
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(re-frame/reg-fx
|
||||
:resolve-public-key
|
||||
@@ -24,19 +24,19 @@
|
||||
;;NOTE we want to handle only last resolve
|
||||
(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]}
|
||||
[{db :db} new-identity-raw new-ens-name id]
|
||||
(let [ens-error (and (= new-identity-raw "0x") (not (string/blank? new-ens-name)))]
|
||||
(when (or (not id) (= id @resolve-last-id))
|
||||
(if ens-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)
|
||||
(utils.db/valid-public-key? new-identity))
|
||||
is-ens? (and (not is-public-key?)
|
||||
(ens/valid-eth-name-prefix? new-identity))
|
||||
error (db/validate-pub-key db new-identity)]
|
||||
is-ens? (and (not is-public-key?)
|
||||
(ens/valid-eth-name-prefix? new-identity))
|
||||
error (db/validate-pub-key db new-identity)]
|
||||
(reset! resolve-last-id nil)
|
||||
(merge {:db (assoc db
|
||||
:contacts/new-identity
|
||||
@@ -61,25 +61,27 @@
|
||||
new-identity
|
||||
@resolve-last-id])}})))))))
|
||||
|
||||
(fx/defn clear-new-identity
|
||||
(rf/defn clear-new-identity
|
||||
{:events [::clear-new-identity ::new-chat-focus]}
|
||||
[{:keys [db]}]
|
||||
{:db (dissoc db :contacts/new-identity)})
|
||||
|
||||
(fx/defn qr-code-handled
|
||||
(rf/defn 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}]
|
||||
(let [public-key? (and (string? data)
|
||||
(string/starts-with? data "0x"))
|
||||
chat-key (cond
|
||||
(= type :private-chat) chat-id
|
||||
(= type :contact) public-key
|
||||
(and (= type :undefined)
|
||||
public-key?) data)
|
||||
[{:keys [db] :as cofx} {:keys [type public-key chat-id data ens-name]}
|
||||
{:keys [new-contact? nickname] :as opts}]
|
||||
(let [public-key? (and (string? data)
|
||||
(string/starts-with? data "0x"))
|
||||
chat-key (cond
|
||||
(= type :private-chat) chat-id
|
||||
(= type :contact) public-key
|
||||
(and (= type :undefined)
|
||||
public-key?)
|
||||
data)
|
||||
validation-result (db/validate-pub-key db chat-key)]
|
||||
(if-not validation-result
|
||||
(if new-contact?
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(contact/add-contact chat-key nickname ens-name)
|
||||
(navigation/navigate-to-cofx :contacts-list {}))
|
||||
(chat/start-chat cofx chat-key ens-name))
|
||||
@@ -91,7 +93,7 @@
|
||||
(i18n/label :t/can-not-add-yourself))
|
||||
: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]}
|
||||
[{:keys [db]} data opts]
|
||||
{::router/handle-uri {:chain (ethereum/chain-keyword db)
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
(ns status-im.backup.core
|
||||
(:require [re-frame.core :as re-frame]
|
||||
[taoensso.timbre :as log]
|
||||
[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]}
|
||||
[{:keys [db]}]
|
||||
{:db (dissoc db :backup/performing-backup)})
|
||||
|
||||
(fx/defn handle-backup-perfomed
|
||||
(rf/defn handle-backup-perfomed
|
||||
{:events [::backup-performed]}
|
||||
[{:keys [db]}]
|
||||
{:db (dissoc db :backup/performing-backup)})
|
||||
|
||||
(fx/defn handle-perform-backup-pressed
|
||||
(rf/defn handle-perform-backup-pressed
|
||||
{:events [:multiaccounts.ui/perform-backup-pressed]}
|
||||
[{:keys [db]}]
|
||||
{:db (assoc db :backup/performing-backup true)
|
||||
{:db (assoc db :backup/performing-backup true)
|
||||
::json-rpc/call [{:method "wakuext_backupData"
|
||||
:params []
|
||||
:on-error #(do
|
||||
|
||||
@@ -4,24 +4,27 @@
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
[status-im.multiaccounts.update.core :as multiaccounts.update]
|
||||
[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})")
|
||||
|
||||
(defn valid-address? [address]
|
||||
(defn valid-address?
|
||||
[address]
|
||||
(re-matches address-regex address))
|
||||
|
||||
(defn- build [id bootnode-name address chain]
|
||||
(defn- build
|
||||
[id bootnode-name address chain]
|
||||
{:address address
|
||||
:chain chain
|
||||
:id (string/replace id "-" "")
|
||||
:name bootnode-name})
|
||||
|
||||
(fx/defn fetch [cofx id]
|
||||
(rf/defn fetch
|
||||
[cofx id]
|
||||
(let [network (get-in cofx [:db :networks/current-network])]
|
||||
(get-in cofx [:db :multiaccount :custom-bootnodes network id])))
|
||||
|
||||
(fx/defn set-input
|
||||
(rf/defn set-input
|
||||
{:events [:bootnodes.ui/input-changed]}
|
||||
[{:keys [db]} input-key value]
|
||||
{:db (update
|
||||
@@ -35,16 +38,17 @@
|
||||
:name (string/blank? value)
|
||||
:url (not (valid-address? value)))})})
|
||||
|
||||
(fx/defn edit
|
||||
(rf/defn edit
|
||||
{:events [:bootnodes.ui/add-bootnode-pressed]}
|
||||
[{:keys [db] :as cofx} id]
|
||||
(let [{:keys [id
|
||||
address
|
||||
name]} (fetch cofx id)
|
||||
fxs (fx/merge cofx
|
||||
(set-input :id id)
|
||||
(set-input :url (str address))
|
||||
(set-input :name (str name)))]
|
||||
name]}
|
||||
(fetch cofx id)
|
||||
fxs (rf/merge cofx
|
||||
(set-input :id id)
|
||||
(set-input :url (str address))
|
||||
(set-input :name (str name)))]
|
||||
(assoc fxs :dispatch [:navigate-to :edit-bootnode])))
|
||||
|
||||
(defn custom-bootnodes-in-use?
|
||||
@@ -52,68 +56,75 @@
|
||||
(let [network (:networks/current-network db)]
|
||||
(get-in db [:multiaccount :custom-bootnodes-enabled? network])))
|
||||
|
||||
(fx/defn delete [{{:keys [multiaccount] :as db} :db :as cofx} id]
|
||||
(let [network (:networks/current-network db)
|
||||
(rf/defn delete
|
||||
[{{:keys [multiaccount] :as db} :db :as cofx} id]
|
||||
(let [network (:networks/current-network db)
|
||||
new-multiaccount (update-in multiaccount [:custom-bootnodes network] dissoc id)]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{:db (assoc db :multiaccount new-multiaccount)}
|
||||
(multiaccounts.update/multiaccount-update
|
||||
:custom-bootnodes (:custom-bootnodes new-multiaccount)
|
||||
:custom-bootnodes
|
||||
(:custom-bootnodes new-multiaccount)
|
||||
{:success-event (when (custom-bootnodes-in-use? cofx)
|
||||
[:multiaccounts.update.callback/save-settings-success])}))))
|
||||
|
||||
(fx/defn upsert
|
||||
{:events [:bootnodes.ui/save-pressed]
|
||||
(rf/defn upsert
|
||||
{:events [:bootnodes.ui/save-pressed]
|
||||
:interceptors [(re-frame/inject-cofx :random-id-generator)]}
|
||||
[{{: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
|
||||
network (:networks/current-network db)
|
||||
bootnode (build
|
||||
(or (:value id) (random-id-generator))
|
||||
(:value name)
|
||||
(:value url)
|
||||
network)
|
||||
new-bootnodes (assoc-in
|
||||
(:custom-bootnodes multiaccount)
|
||||
[network (:id bootnode)]
|
||||
bootnode)]
|
||||
network (:networks/current-network db)
|
||||
bootnode (build
|
||||
(or (:value id) (random-id-generator))
|
||||
(:value name)
|
||||
(:value url)
|
||||
network)
|
||||
new-bootnodes (assoc-in
|
||||
(:custom-bootnodes multiaccount)
|
||||
[network (:id bootnode)]
|
||||
bootnode)]
|
||||
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{:db (dissoc db :bootnodes/manage)
|
||||
:dispatch [:navigate-back]}
|
||||
(multiaccounts.update/multiaccount-update
|
||||
:custom-bootnodes new-bootnodes
|
||||
:custom-bootnodes
|
||||
new-bootnodes
|
||||
{:success-event (when (custom-bootnodes-in-use? cofx)
|
||||
[:multiaccounts.update.callback/save-settings-success])}))))
|
||||
|
||||
(fx/defn toggle-custom-bootnodes
|
||||
(rf/defn toggle-custom-bootnodes
|
||||
{:events [:bootnodes.ui/custom-bootnodes-switch-toggled]}
|
||||
[{: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?])]
|
||||
(multiaccounts.update/multiaccount-update
|
||||
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]})))
|
||||
|
||||
(fx/defn set-bootnodes-from-qr
|
||||
(rf/defn set-bootnodes-from-qr
|
||||
{:events [:bootnodes.callback/qr-code-scanned]}
|
||||
[cofx 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]}
|
||||
[_ bootnode-id]
|
||||
{:ui/show-confirmation {:title (i18n/label :t/delete-bootnode-title)
|
||||
:content (i18n/label :t/delete-bootnode-are-you-sure)
|
||||
{:ui/show-confirmation {:title (i18n/label :t/delete-bootnode-title)
|
||||
:content (i18n/label :t/delete-bootnode-are-you-sure)
|
||||
: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]}
|
||||
[cofx bootnode-id]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(delete bootnode-id)
|
||||
(navigation/navigate-back)))
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
(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]}]
|
||||
{:show-bottom-sheet nil
|
||||
:db (assoc db
|
||||
:bottom-sheet/show? true
|
||||
:bottom-sheet/view view
|
||||
:bottom-sheet/options options)})
|
||||
:db (assoc db
|
||||
:bottom-sheet/show? true
|
||||
:bottom-sheet/view view
|
||||
:bottom-sheet/options options)})
|
||||
|
||||
(fx/defn show-bottom-sheet-event
|
||||
(rf/defn show-bottom-sheet-event
|
||||
{:events [:bottom-sheet/show-sheet]}
|
||||
[cofx view options]
|
||||
(show-bottom-sheet
|
||||
@@ -17,8 +17,8 @@
|
||||
{:view view
|
||||
:options options}))
|
||||
|
||||
(fx/defn hide-bottom-sheet
|
||||
(rf/defn hide-bottom-sheet
|
||||
{:events [:bottom-sheet/hide]}
|
||||
[{:keys [db]}]
|
||||
{:hide-bottom-sheet nil
|
||||
:db (assoc db :bottom-sheet/show? false)})
|
||||
:db (assoc db :bottom-sheet/show? false)})
|
||||
|
||||
+195
-152
@@ -1,62 +1,65 @@
|
||||
(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]
|
||||
[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.webview-ref :as webview-ref]
|
||||
[status-im.constants :as constants]
|
||||
[status-im.ethereum.core :as ethereum]
|
||||
[status-im.ethereum.ens :as ens]
|
||||
[status-im.ethereum.json-rpc :as json-rpc]
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
[status-im.multiaccounts.update.core :as multiaccounts.update]
|
||||
[status-im.native-module.core :as status]
|
||||
[status-im.signing.core :as signing]
|
||||
[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.platform :as platform]
|
||||
[status-im.utils.random :as random]
|
||||
[status-im.utils.types :as types]
|
||||
[status-im.utils.universal-links.utils :as links]
|
||||
[status-im2.navigation.events :as navigation]
|
||||
[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]
|
||||
[status-im.browser.eip3085 :as eip3085]
|
||||
[status-im.browser.eip3326 :as eip3326]))
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(fx/defn update-browser-option
|
||||
(rf/defn update-browser-option
|
||||
[{:keys [db]} 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]
|
||||
{: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])]))
|
||||
|
||||
(defn get-current-url [{:keys [history history-index]
|
||||
:or {history-index 0}}]
|
||||
(defn get-current-url
|
||||
[{:keys [history history-index]
|
||||
:or {history-index 0}}]
|
||||
(when history
|
||||
(nth history history-index)))
|
||||
|
||||
(defn secure? [{:keys [error? dapp?]} {:keys [url]}]
|
||||
(defn secure?
|
||||
[{:keys [error? dapp?]} {:keys [url]}]
|
||||
(or dapp?
|
||||
(and (not error?)
|
||||
(when url
|
||||
(string/starts-with? url "https://")))))
|
||||
|
||||
(fx/defn remove-browser
|
||||
(rf/defn remove-browser
|
||||
{:events [:browser.ui/remove-browser-pressed]}
|
||||
[{:keys [db]} browser-id]
|
||||
{:db (update-in db [:browser/browsers] dissoc browser-id)
|
||||
::json-rpc/call [{:method "wakuext_deleteBrowser"
|
||||
:params [browser-id]
|
||||
{:db (update-in db [:browser/browsers] dissoc browser-id)
|
||||
::json-rpc/call [{:method "wakuext_deleteBrowser"
|
||||
:params [browser-id]
|
||||
:on-success #()}]})
|
||||
|
||||
(fx/defn clear-all-browsers
|
||||
(rf/defn clear-all-browsers
|
||||
{:events [:browser.ui/clear-all-browsers-pressed]}
|
||||
[{:keys [db]}]
|
||||
{:db (dissoc db :browser/browsers)
|
||||
@@ -65,106 +68,116 @@
|
||||
:params [browser-id]
|
||||
: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))))
|
||||
|
||||
(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 _)))]
|
||||
(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))
|
||||
(re-frame/dispatch [:browser.callback/resolve-ens-multihash-success url])
|
||||
(re-frame/dispatch [:browser.callback/resolve-ens-multihash-error])))
|
||||
|
||||
(fx/defn resolve-url
|
||||
(rf/defn resolve-url
|
||||
[{:keys [db]} {:keys [error? resolved-url]}]
|
||||
(when (not error?)
|
||||
(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))
|
||||
{: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)
|
||||
:ens-name host
|
||||
:cb resolve-ens-contenthash-callback}}
|
||||
{: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 [browser-id] :as browser}]
|
||||
(let [updated-browser (-> browser
|
||||
(update-dapp-name)
|
||||
(check-if-phishing-url))]
|
||||
{:db (update-in db
|
||||
[:browser/browsers browser-id]
|
||||
merge updated-browser)
|
||||
::json-rpc/call [{:method "wakuext_addBrowser"
|
||||
:params [(select-keys updated-browser [:browser-id :timestamp :name :dapp? :history :history-index])]
|
||||
{:db (update-in db
|
||||
[:browser/browsers browser-id]
|
||||
merge
|
||||
updated-browser)
|
||||
::json-rpc/call [{:method "wakuext_addBrowser"
|
||||
:params [(select-keys updated-browser
|
||||
[:browser-id :timestamp :name :dapp? :history
|
||||
:history-index])]
|
||||
:on-success #()}]}))
|
||||
|
||||
(fx/defn store-bookmark
|
||||
(rf/defn store-bookmark
|
||||
{:events [:browser/store-bookmark]}
|
||||
[{:keys [db]}
|
||||
{:keys [url] :as bookmark}]
|
||||
{:db (assoc-in db [:bookmarks/bookmarks url] bookmark)
|
||||
::json-rpc/call [{:method "wakuext_addBookmark"
|
||||
:params [bookmark]
|
||||
{:db (assoc-in db [:bookmarks/bookmarks url] bookmark)
|
||||
::json-rpc/call [{:method "wakuext_addBookmark"
|
||||
:params [bookmark]
|
||||
:on-success #()}]})
|
||||
|
||||
(fx/defn update-bookmark
|
||||
(rf/defn update-bookmark
|
||||
{:events [:browser/update-bookmark]}
|
||||
[{:keys [db] :as cofx}
|
||||
{:keys [url] :as bookmark}]
|
||||
(let [old-bookmark (get-in db [:bookmarks/bookmarks url])
|
||||
new-bookmark (merge old-bookmark bookmark)]
|
||||
(fx/merge cofx {:db (assoc-in db [:bookmarks/bookmarks url] new-bookmark)
|
||||
::json-rpc/call [{:method "wakuext_updateBookmark"
|
||||
:params [url bookmark]
|
||||
:on-success #()}]})))
|
||||
(rf/merge cofx
|
||||
{:db (assoc-in db [:bookmarks/bookmarks url] new-bookmark)
|
||||
::json-rpc/call [{:method "wakuext_updateBookmark"
|
||||
:params [url bookmark]
|
||||
:on-success #()}]})))
|
||||
|
||||
(fx/defn delete-bookmark
|
||||
(rf/defn delete-bookmark
|
||||
{:events [:browser/delete-bookmark]}
|
||||
[{:keys [db] :as cofx}
|
||||
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})]
|
||||
(fx/merge cofx {:db (update db :bookmarks/bookmarks dissoc url)
|
||||
::json-rpc/call [{:method "wakuext_removeBookmark"
|
||||
:params [url]
|
||||
:on-success #()}]})))
|
||||
(rf/merge cofx
|
||||
{:db (update db :bookmarks/bookmarks dissoc url)
|
||||
::json-rpc/call [{:method "wakuext_removeBookmark"
|
||||
:params [url]
|
||||
:on-success #()}]})))
|
||||
|
||||
(defn can-go-back? [{:keys [history-index]}]
|
||||
(defn can-go-back?
|
||||
[{:keys [history-index]}]
|
||||
(pos? history-index))
|
||||
|
||||
(fx/defn navigate-to-previous-page
|
||||
(rf/defn navigate-to-previous-page
|
||||
{:events [:browser.ui/previous-page-button-pressed]}
|
||||
[cofx]
|
||||
(let [{:keys [history-index] :as browser} (get-current-browser (:db cofx))]
|
||||
(when (can-go-back? browser)
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(update-browser (assoc browser :history-index (dec history-index)))
|
||||
(resolve-url nil)))))
|
||||
|
||||
(fx/defn ignore-unsafe
|
||||
(rf/defn ignore-unsafe
|
||||
{:events [:browser/ignore-unsafe]}
|
||||
[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))))
|
||||
|
||||
(defn can-go-forward? [{:keys [history-index history]}]
|
||||
(defn can-go-forward?
|
||||
[{:keys [history-index 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]}
|
||||
[cofx]
|
||||
(let [{:keys [history-index] :as browser} (get-current-browser (:db cofx))]
|
||||
(when (can-go-forward? browser)
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(update-browser (assoc browser :history-index (inc history-index)))
|
||||
(resolve-url nil)))))
|
||||
|
||||
(fx/defn update-browser-history
|
||||
(rf/defn update-browser-history
|
||||
[cofx browser url]
|
||||
(let [history-index (:history-index browser)
|
||||
history (:history browser)]
|
||||
@@ -173,73 +186,81 @@
|
||||
new-index (dec (count new-history))]
|
||||
(update-browser cofx
|
||||
(assoc browser
|
||||
:history new-history
|
||||
:history new-history
|
||||
:history-index new-index))))))
|
||||
|
||||
(fx/defn resolve-ens-multihash-success
|
||||
(rf/defn resolve-ens-multihash-success
|
||||
{:events [:browser.callback/resolve-ens-multihash-success]}
|
||||
[{:keys [db] :as cofx} url]
|
||||
(let [current-url (get-current-url (get-current-browser db))
|
||||
host (http/url-host current-url)
|
||||
path (subs current-url (+ (.indexOf ^js current-url host) (count host)))
|
||||
gateway url]
|
||||
(fx/merge cofx
|
||||
{:db (-> (update db :browser/options
|
||||
(rf/merge cofx
|
||||
{:db (-> (update db
|
||||
:browser/options
|
||||
assoc
|
||||
:url (str gateway path)
|
||||
:resolving? false)
|
||||
:url (str gateway path)
|
||||
:resolving? false)
|
||||
(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]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(update-browser-options cofx {:url (get-current-url (get-current-browser db))
|
||||
:resolving? false
|
||||
:error? true}))
|
||||
(update-browser-options cofx
|
||||
{:url (get-current-url (get-current-browser db))
|
||||
:resolving? false
|
||||
:error? true}))
|
||||
|
||||
(fx/defn handle-browser-error
|
||||
(rf/defn handle-browser-error
|
||||
{:events [:browser/error-occured]}
|
||||
[cofx]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(update-browser-option :error? true)
|
||||
(update-browser-option :loading? false)))
|
||||
|
||||
(fx/defn handle-pdf
|
||||
(rf/defn handle-pdf
|
||||
[_ url]
|
||||
(when (and platform/android? (string/ends-with? url ".pdf"))
|
||||
{:browser/show-web-browser-selection url}))
|
||||
|
||||
(fx/defn handle-message-link
|
||||
(rf/defn handle-message-link
|
||||
{:events [:browser.ui/message-link-pressed]}
|
||||
[_ link]
|
||||
(if (links/universal-link? link)
|
||||
{:dispatch [:universal-links/handle-url link]}
|
||||
{:browser/show-browser-selection link}))
|
||||
|
||||
(fx/defn update-browser-on-nav-change
|
||||
(rf/defn update-browser-on-nav-change
|
||||
[cofx url error?]
|
||||
(let [browser (get-current-browser (:db cofx))
|
||||
options (get-in cofx [:db :browser/options])
|
||||
(let [browser (get-current-browser (:db cofx))
|
||||
options (get-in cofx [:db :browser/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]
|
||||
(not= (.indexOf ^js url (second v)) -1))
|
||||
(:resolved-ens options)))
|
||||
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)]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(update-browser-history browser resolved-url)
|
||||
(handle-pdf url)
|
||||
(resolve-url {:error? error? :resolved-url (when resolved-ens url)}))))))
|
||||
|
||||
(fx/defn update-browser-name
|
||||
(rf/defn update-browser-name
|
||||
[cofx title]
|
||||
(let [browser (get-current-browser (:db cofx))]
|
||||
(when (and browser (not (:dapp? browser)) title (not (string/blank? title)))
|
||||
(update-browser cofx (assoc browser :name title)))))
|
||||
|
||||
(fx/defn navigation-state-changed
|
||||
(rf/defn navigation-state-changed
|
||||
{:events [:browser/navigation-state-changed]}
|
||||
[cofx event error?]
|
||||
(let [{:strs [url loading title]} (js->clj event)
|
||||
@@ -247,59 +268,61 @@
|
||||
(if (links/universal-link? url)
|
||||
(when-not (and deep-link? platform/ios?) ;; ios webview handles this
|
||||
{:dispatch [:universal-links/handle-url url]})
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(update-browser-option :loading? loading)
|
||||
(update-browser-name title)
|
||||
(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
|
||||
and history of the current browser is updated so that the user can navigate back to the
|
||||
origin url"
|
||||
{:events [:browser.ui/url-submitted]}
|
||||
[cofx url]
|
||||
(let [browser (get-current-browser (:db cofx))
|
||||
(let [browser (get-current-browser (:db cofx))
|
||||
normalized-url (http/normalize-and-decode-url url)]
|
||||
(if (links/universal-link? normalized-url)
|
||||
{:dispatch [:universal-links/handle-url normalized-url]}
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(update-browser-option :url-editing? false)
|
||||
(update-browser-history browser normalized-url)
|
||||
(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
|
||||
there is already a browser for this host, this browser is reused
|
||||
If the browser is reused, the history is flushed"
|
||||
{:events [:browser.ui/open-url]}
|
||||
[{:keys [db] :as cofx} url]
|
||||
(let [normalized-url (http/normalize-and-decode-url url)
|
||||
browser {:browser-id (random/id)
|
||||
:history-index 0
|
||||
:history [normalized-url]}]
|
||||
browser {:browser-id (random/id)
|
||||
:history-index 0
|
||||
:history [normalized-url]}]
|
||||
(if (links/universal-link? normalized-url)
|
||||
{:dispatch [:universal-links/handle-url normalized-url]}
|
||||
(fx/merge cofx
|
||||
{:db (assoc db :browser/options
|
||||
(rf/merge cofx
|
||||
{:db (assoc db
|
||||
:browser/options
|
||||
{:browser-id (:browser-id browser)})}
|
||||
(navigation/change-tab :browser)
|
||||
(navigation/set-stack-root :browser-stack :browser)
|
||||
(update-browser browser)
|
||||
(resolve-url nil)))))
|
||||
|
||||
(fx/defn open-existing-browser
|
||||
(rf/defn open-existing-browser
|
||||
"Opens an existing browser with it's history"
|
||||
{:events [:browser.ui/browser-item-selected]}
|
||||
[{:keys [db] :as cofx} browser-id]
|
||||
(let [browser (get-in db [:browser/browsers browser-id])]
|
||||
(fx/merge cofx
|
||||
{:db (assoc db :browser/options
|
||||
(rf/merge cofx
|
||||
{:db (assoc db
|
||||
:browser/options
|
||||
{:browser-id browser-id})}
|
||||
(update-browser browser)
|
||||
(navigation/set-stack-root :browser-stack :browser)
|
||||
(resolve-url nil))))
|
||||
|
||||
(fx/defn web3-error-callback
|
||||
(rf/defn web3-error-callback
|
||||
{:events [:browser.dapp/transaction-on-error]}
|
||||
[_ message-id message]
|
||||
{:browser/send-to-bridge
|
||||
@@ -307,7 +330,7 @@
|
||||
:messageId message-id
|
||||
:error message}})
|
||||
|
||||
(fx/defn dapp-complete-transaction
|
||||
(rf/defn dapp-complete-transaction
|
||||
{:events [:browser.dapp/transaction-on-result]}
|
||||
[_ message-id id result]
|
||||
;;TODO check and test id
|
||||
@@ -336,21 +359,24 @@
|
||||
(ethereum/address? second-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]}
|
||||
[_ 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-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]
|
||||
(let [message? (web3-sign-message? method)
|
||||
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))
|
||||
(let [[address data] (cond (and (= method constants/web3-keycard-sign-typed-data)
|
||||
(not (vector? params)))
|
||||
@@ -359,34 +385,38 @@
|
||||
message? (normalize-sign-message-params params typed?)
|
||||
:else [nil nil])]
|
||||
(when (or (not message?) (and address data))
|
||||
(signing/sign cofx (merge
|
||||
(if message?
|
||||
{:message {:address address
|
||||
:data data
|
||||
:v4 (= constants/web3-sign-typed-data-v4 method)
|
||||
:typed? typed?
|
||||
:pinless? (= method constants/web3-keycard-sign-typed-data)
|
||||
:from dapps-address}}
|
||||
{:tx-obj (-> params
|
||||
first
|
||||
(update :from #(or % dapps-address))
|
||||
(dissoc :gasPrice))})
|
||||
{:on-result [:browser.dapp/transaction-on-result message-id id]
|
||||
:on-error [:browser.dapp/transaction-on-error message-id]}))))
|
||||
(signing/sign cofx
|
||||
(merge
|
||||
(if message?
|
||||
{:message {:address address
|
||||
:data data
|
||||
:v4 (= constants/web3-sign-typed-data-v4 method)
|
||||
:typed? typed?
|
||||
:pinless? (= method constants/web3-keycard-sign-typed-data)
|
||||
:from dapps-address}}
|
||||
{:tx-obj (-> params
|
||||
first
|
||||
(update :from #(or % dapps-address))
|
||||
(dissoc :gasPrice))})
|
||||
{:on-result [:browser.dapp/transaction-on-result message-id id]
|
||||
:on-error [:browser.dapp/transaction-on-error message-id]}))))
|
||||
(cond
|
||||
(#{"eth_accounts" "eth_coinbase"} method)
|
||||
(send-to-bridge cofx {:type constants/web3-send-async-callback
|
||||
:messageId message-id
|
||||
:result {:jsonrpc "2.0"
|
||||
:id (int id)
|
||||
:result (if (= method "eth_coinbase") dapps-address [dapps-address])}})
|
||||
(send-to-bridge
|
||||
cofx
|
||||
{:type constants/web3-send-async-callback
|
||||
:messageId message-id
|
||||
:result {:jsonrpc "2.0"
|
||||
:id (int id)
|
||||
:result (if (= method "eth_coinbase") dapps-address [dapps-address])}})
|
||||
(= method "personal_ecRecover")
|
||||
{:signing.fx/recover-message {:params {:message (first params)
|
||||
{:signing.fx/recover-message {:params {:message (first params)
|
||||
:signature (second params)}
|
||||
:on-completed #(re-frame/dispatch [:browser.callback/call-rpc
|
||||
{:type constants/web3-send-async-callback
|
||||
:messageId message-id
|
||||
:result (types/json->clj %)}])}}
|
||||
:on-completed #(re-frame/dispatch
|
||||
[:browser.callback/call-rpc
|
||||
{:type constants/web3-send-async-callback
|
||||
:messageId message-id
|
||||
:result (types/json->clj %)}])}}
|
||||
(= method "wallet_switchEthereumChain")
|
||||
(eip3326/handle-switch-ethereum-chain cofx dapp-name id message-id (first params))
|
||||
|
||||
@@ -401,7 +431,8 @@
|
||||
:error %1
|
||||
: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")
|
||||
;; eth_accounts returns empty array for compatibility with meta-mask
|
||||
(send-to-bridge cofx
|
||||
@@ -420,41 +451,48 @@
|
||||
"keycard_signTypedData"
|
||||
"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
|
||||
(and (permissioned-method method)
|
||||
(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]
|
||||
(if (has-permissions? db dapp-name method)
|
||||
(handle-no-permissions cofx 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]}
|
||||
[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/process-next-permission dapp-name)
|
||||
(navigation/navigate-back)))
|
||||
|
||||
(fx/defn handle-canceled-qr-code
|
||||
(rf/defn handle-canceled-qr-code
|
||||
{:events [:browser.bridge.callback/qr-code-canceled]}
|
||||
[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/process-next-permission dapp-name)))
|
||||
|
||||
(fx/defn process-bridge-message
|
||||
(rf/defn process-bridge-message
|
||||
{:events [:browser/bridge-message-received]}
|
||||
[{:keys [db] :as cofx} message]
|
||||
(let [browser (get-current-browser db)
|
||||
url-original (get-current-url browser)
|
||||
data (types/json->clj message)
|
||||
(let [browser (get-current-browser
|
||||
db)
|
||||
url-original (get-current-url
|
||||
browser)
|
||||
data (types/json->clj
|
||||
message)
|
||||
{{:keys [url]} :navState :keys [type permission payload messageId params]} data
|
||||
{:keys [dapp? name]} browser
|
||||
dapp-name (if dapp? name (http/url-host url-original))]
|
||||
{:keys [dapp? name]} browser
|
||||
dapp-name (if dapp?
|
||||
name
|
||||
(http/url-host
|
||||
url-original))]
|
||||
(cond
|
||||
(and (= type constants/history-state-changed)
|
||||
(not= "about:blank" url))
|
||||
@@ -474,10 +512,13 @@
|
||||
(re-frame/reg-fx
|
||||
:browser/send-to-bridge
|
||||
(fn [message]
|
||||
(let [^js webview @webview-ref/webview-ref
|
||||
msg (str "(function() { var __send = function() { if (ReactNativeWebView.onMessage) { ReactNativeWebView.onMessage('"
|
||||
(types/clj->json message)
|
||||
"');} else {setTimeout(__send, 0)}}; __send();})();")]
|
||||
(let
|
||||
[^js webview @webview-ref/webview-ref
|
||||
msg
|
||||
(str
|
||||
"(function() { var __send = function() { if (ReactNativeWebView.onMessage) { ReactNativeWebView.onMessage('"
|
||||
(types/clj->json message)
|
||||
"');} else {setTimeout(__send, 0)}}; __send();})();")]
|
||||
(when (and message webview)
|
||||
(.injectJavaScript webview msg)))))
|
||||
|
||||
@@ -508,59 +549,61 @@
|
||||
(fn []
|
||||
(status/clear-web-data)))
|
||||
|
||||
(defn share-link [url]
|
||||
(defn share-link
|
||||
[url]
|
||||
(let [link (links/generate-link :browse :external url)
|
||||
message (i18n/label :t/share-dapp-text {:link link})]
|
||||
(list-selection/open-share {:message message})))
|
||||
|
||||
(fx/defn dapps-account-selected
|
||||
(rf/defn dapps-account-selected
|
||||
{:events [:dapps-account-selected]}
|
||||
[{:keys [db] :as cofx} address]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{:browser/clear-web-data nil}
|
||||
(bottom-sheet/hide-bottom-sheet)
|
||||
(browser.permissions/clear-dapps-permissions)
|
||||
(multiaccounts.update/multiaccount-update :dapps-address address {})
|
||||
#(when (= (:view-id db) :browser)
|
||||
(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]}
|
||||
[cofx]
|
||||
(debounce/clear :browser/navigation-state-changed)
|
||||
(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]}
|
||||
[cofx _]
|
||||
(update-browser-option cofx :url-editing? true))
|
||||
|
||||
(fx/defn url-input-blured
|
||||
(rf/defn url-input-blured
|
||||
{:events [:browser.ui/url-input-blured]}
|
||||
[cofx]
|
||||
(update-browser-option cofx :url-editing? false))
|
||||
|
||||
(fx/defn lock-pressed
|
||||
(rf/defn lock-pressed
|
||||
{:events [:browser.ui/lock-pressed]}
|
||||
[cofx 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]}
|
||||
[cofx]
|
||||
(update-browser-option cofx :show-tooltip nil))
|
||||
|
||||
(fx/defn loading-started
|
||||
(rf/defn loading-started
|
||||
{:events [:browser/loading-started]}
|
||||
[cofx]
|
||||
(update-browser-options cofx {:error? false :loading? true}))
|
||||
|
||||
(fx/defn handle-bookmarks
|
||||
(rf/defn handle-bookmarks
|
||||
[{:keys [db]} bookmarks]
|
||||
(let [changed-bookmarks (reduce (fn [acc {:keys [url] :as bookmark}]
|
||||
(assoc acc url bookmark))
|
||||
{}
|
||||
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))}))
|
||||
|
||||
@@ -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
|
||||
(:require [status-im.constants :as constants]
|
||||
[clojure.string :as string]
|
||||
(:require [clojure.string :as string]
|
||||
[re-frame.core :as re-frame]
|
||||
[status-im.utils.fx :as fx]
|
||||
[status-im.network.core :as network]
|
||||
[taoensso.timbre :as log]
|
||||
[status-im.utils.random :as random]
|
||||
[status-im.constants :as constants]
|
||||
[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]}
|
||||
[_ id messageId]
|
||||
{:browser/send-to-bridge {:type constants/web3-send-async-callback
|
||||
{:browser/send-to-bridge {:type constants/web3-send-async-callback
|
||||
:messageId messageId
|
||||
:result {:jsonrpc "2.0"
|
||||
:id (int id)
|
||||
:result nil}}})
|
||||
:result {:jsonrpc "2.0"
|
||||
:id (int id)
|
||||
:result nil}}})
|
||||
|
||||
(fx/defn allow-permission
|
||||
(rf/defn allow-permission
|
||||
{:events [:eip3085.ui/dapp-permission-allowed]}
|
||||
[{:keys [db] :as cofx} message-id {:keys [new-networks id]}]
|
||||
{:db (assoc db :networks/networks new-networks)
|
||||
::json-rpc/call [{:method "settings_saveSetting"
|
||||
:params [:networks/networks (vals new-networks)]
|
||||
:on-success #(re-frame/dispatch [:eip3085/send-success-call-to-bridge cofx id message-id])
|
||||
{:db (assoc db :networks/networks new-networks)
|
||||
::json-rpc/call [{:method "settings_saveSetting"
|
||||
:params [:networks/networks (vals new-networks)]
|
||||
:on-success #(re-frame/dispatch [:eip3085/send-success-call-to-bridge cofx id
|
||||
message-id])
|
||||
: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]}
|
||||
[_ 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
|
||||
:error {:code 4001
|
||||
:message "User rejected the request."}}
|
||||
:dispatch [:bottom-sheet/hide]})
|
||||
:error {:code 4001
|
||||
:message "User rejected the request."}}
|
||||
:dispatch [:bottom-sheet/hide]})
|
||||
|
||||
(fx/defn handle-add-ethereum-chain
|
||||
(rf/defn handle-add-ethereum-chain
|
||||
{:events [:eip3085/handle-add-ethereum-chain]}
|
||||
[{{:networks/keys [networks] :as db} :db :as cofx}
|
||||
dapp-name id message-id {:keys [chainId blockExplorerUrls chainName iconUrls nativeCurrency rpcUrls] :as params}]
|
||||
(let [manage {:name {:value chainName}
|
||||
:symbol {:value (:symbol nativeCurrency)}
|
||||
:url {:value (first rpcUrls)}
|
||||
dapp-name id message-id
|
||||
{:keys [chainId blockExplorerUrls chainName iconUrls nativeCurrency rpcUrls] :as params}]
|
||||
(let [manage {:name {:value chainName}
|
||||
:symbol {:value (:symbol nativeCurrency)}
|
||||
:url {:value (first rpcUrls)}
|
||||
:network-id {:value chainId}
|
||||
:chain {:value :custom}}]
|
||||
:chain {:value :custom}}]
|
||||
(if (network/valid-manage? manage)
|
||||
(let [{:keys [name url chain network-id symbol]} manage
|
||||
random-id (string/replace (random/id) "-" "")
|
||||
network (network/new-network random-id
|
||||
(:value name)
|
||||
(:value symbol)
|
||||
(:value url)
|
||||
(:value chain)
|
||||
(:value network-id))
|
||||
new-networks (assoc networks random-id network)
|
||||
params (assoc params :new-networks new-networks :id id :new-network network)]
|
||||
random-id (string/replace (random/id) "-" "")
|
||||
network (network/new-network random-id
|
||||
(:value name)
|
||||
(:value symbol)
|
||||
(:value url)
|
||||
(:value chain)
|
||||
(:value network-id))
|
||||
new-networks (assoc networks random-id network)
|
||||
params (assoc params
|
||||
:new-networks new-networks
|
||||
:id id
|
||||
:new-network network)]
|
||||
(if (network/chain-id-available? networks network)
|
||||
{:dispatch [:bottom-sheet/show-sheet {:content (fn []
|
||||
[sheet/permissions-panel dapp-name message-id params])}]}
|
||||
{:dispatch [:bottom-sheet/show-sheet
|
||||
{:content (fn []
|
||||
[sheet/permissions-panel dapp-name message-id params])}]}
|
||||
(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
|
||||
:error {:code -32602
|
||||
:message "invalid network parameters"}}})))
|
||||
:error {:code -32602
|
||||
:message "invalid network parameters"}}})))
|
||||
|
||||
|
||||
@@ -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
|
||||
(:require [status-im.constants :as constants]
|
||||
[status-im.utils.fx :as fx]
|
||||
[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]}
|
||||
[{: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
|
||||
:error {:code 4001
|
||||
:message "User rejected the request."}}
|
||||
:dispatch [:bottom-sheet/hide]})
|
||||
:error {:code 4001
|
||||
:message "User rejected the request."}}
|
||||
:dispatch [:bottom-sheet/hide]})
|
||||
|
||||
(fx/defn handle-switch-ethereum-chain
|
||||
(rf/defn handle-switch-ethereum-chain
|
||||
{:events [:eip3326/handle-switch-ethereum-chain]}
|
||||
[{:keys [db] :as cofx} dapp-name id message-id {:keys [chainId] :as params}]
|
||||
(let [target-chain-id (js/parseInt chainId 16)
|
||||
networks (vals (get-in db [:networks/networks]))
|
||||
exist-chain-ids (set (map ethereum/network->chain-id networks))
|
||||
(let [target-chain-id (js/parseInt chainId 16)
|
||||
networks (vals (get-in db [:networks/networks]))
|
||||
exist-chain-ids (set (map ethereum/network->chain-id networks))
|
||||
current-chain-id (ethereum/chain-id db)]
|
||||
(if (exist-chain-ids 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
|
||||
:result {:jsonrpc "2.0"
|
||||
:id (int id)
|
||||
:result nil}}}
|
||||
(let [target-network (first (filter #(= (ethereum/network->chain-id %1) target-chain-id) networks))
|
||||
:result {:jsonrpc "2.0"
|
||||
:id (int id)
|
||||
:result nil}}}
|
||||
(let [target-network (first (filter #(= (ethereum/network->chain-id %1) target-chain-id)
|
||||
networks))
|
||||
target-network-id (:id target-network)
|
||||
current-network (ethereum/current-network db)
|
||||
network-from (:name current-network)
|
||||
network-to (:name target-network)
|
||||
params (assoc params :target-network-id target-network-id :network-from network-from :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
|
||||
current-network (ethereum/current-network db)
|
||||
network-from (:name current-network)
|
||||
network-to (:name target-network)
|
||||
params (assoc params
|
||||
:target-network-id target-network-id
|
||||
:network-from network-from
|
||||
: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
|
||||
:error {:code 4902
|
||||
:message (str "Unrecognized chain ID: " target-chain-id ". Try adding the chain using wallet_addEthereumChain first.")}}})))
|
||||
:error
|
||||
{:code 4902
|
||||
:message
|
||||
(str
|
||||
"Unrecognized chain ID: "
|
||||
target-chain-id
|
||||
". Try adding the chain using wallet_addEthereumChain first.")}}})))
|
||||
|
||||
|
||||
@@ -2,51 +2,53 @@
|
||||
(:require [status-im.constants :as constants]
|
||||
[status-im.ethereum.json-rpc :as json-rpc]
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
[status-im2.navigation.events :as navigation]
|
||||
[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 send-response-to-bridge)
|
||||
|
||||
(def supported-permissions
|
||||
{constants/dapp-permission-qr-code {:yield-control? true
|
||||
:allowed? true}
|
||||
constants/dapp-permission-contact-code {:type :profile
|
||||
:title (i18n/label :t/wants-to-access-profile)
|
||||
:description (i18n/label :t/your-contact-code)
|
||||
:icon :main-icons/profile}
|
||||
constants/dapp-permission-web3 {:type :wallet
|
||||
:title (i18n/label :t/dapp-would-like-to-connect-wallet)
|
||||
:description (i18n/label :t/allowing-authorizes-this-dapp)
|
||||
:icon :main-icons/wallet}})
|
||||
{constants/dapp-permission-qr-code {:yield-control? true
|
||||
:allowed? true}
|
||||
constants/dapp-permission-contact-code {:type :profile
|
||||
:title (i18n/label :t/wants-to-access-profile)
|
||||
:description (i18n/label :t/your-contact-code)
|
||||
:icon :main-icons/profile}
|
||||
constants/dapp-permission-web3 {:type :wallet
|
||||
:title (i18n/label :t/dapp-would-like-to-connect-wallet)
|
||||
:description (i18n/label :t/allowing-authorizes-this-dapp)
|
||||
:icon :main-icons/wallet}})
|
||||
|
||||
(fx/defn permission-yield-control
|
||||
(rf/defn permission-yield-control
|
||||
[{:keys [db] :as cofx} dapp-name permission message-id params]
|
||||
(cond
|
||||
(= 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
|
||||
:cancel-handler :browser.bridge.callback/qr-code-canceled
|
||||
:data {:dapp-name dapp-name
|
||||
:permission permission
|
||||
: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?]
|
||||
{:db (assoc-in db [:browser/options :show-permission]
|
||||
{:requested-permission permission
|
||||
:message-id message-id
|
||||
:dapp-name dapp-name
|
||||
:yield-control? yield-control?})})
|
||||
{:db (assoc-in db
|
||||
[:browser/options :show-permission]
|
||||
{:requested-permission permission
|
||||
:message-id message-id
|
||||
: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])]
|
||||
(get {constants/dapp-permission-contact-code (:public-key multiaccount)
|
||||
constants/dapp-permission-web3 [(:dapps-address multiaccount)]}
|
||||
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
|
||||
with the permission"
|
||||
[{:keys [db] :as cofx} permission message-id allowed? data]
|
||||
@@ -57,7 +59,7 @@
|
||||
allowed?
|
||||
(assoc :data data))})
|
||||
|
||||
(fx/defn update-dapp-permissions
|
||||
(rf/defn update-dapp-permissions
|
||||
[{:keys [db]} dapp-name permission allowed?]
|
||||
(let [dapp-permissions-set (set (get-in db [:dapps/permissions dapp-name :permissions]))
|
||||
allowed-permissions-set (if allowed?
|
||||
@@ -65,28 +67,28 @@
|
||||
(disj dapp-permissions-set permission))
|
||||
allowed-permissions {:dapp dapp-name
|
||||
:permissions (vec allowed-permissions-set)}]
|
||||
{:db (assoc-in db [:dapps/permissions dapp-name] allowed-permissions)
|
||||
::json-rpc/call [{:method "permissions_addDappPermissions"
|
||||
:params [allowed-permissions]
|
||||
{:db (assoc-in db [:dapps/permissions dapp-name] allowed-permissions)
|
||||
::json-rpc/call [{:method "permissions_addDappPermissions"
|
||||
:params [allowed-permissions]
|
||||
:on-success #()}]}))
|
||||
|
||||
(fx/defn revoke-permissions
|
||||
(rf/defn revoke-permissions
|
||||
{:events [:browser/revoke-dapp-permissions]}
|
||||
[{:keys [db] :as cofx} dapp]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{:db (update-in db [:dapps/permissions] dissoc dapp)
|
||||
::json-rpc/call [{:method "permissions_deleteDappPermissions"
|
||||
:params [dapp]
|
||||
::json-rpc/call [{:method "permissions_deleteDappPermissions"
|
||||
:params [dapp]
|
||||
:on-success #()}]}))
|
||||
|
||||
(fx/defn revoke-dapp-permissions
|
||||
(rf/defn revoke-dapp-permissions
|
||||
{:events [:dapps/revoke-access]}
|
||||
[cofx dapp]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(revoke-permissions dapp)
|
||||
(navigation/navigate-back)))
|
||||
|
||||
(fx/defn clear-dapps-permissions
|
||||
(rf/defn clear-dapps-permissions
|
||||
[{:keys [db]}]
|
||||
(let [dapp-permissions (keys (:dapps/permissions db))]
|
||||
{:db (dissoc db :dapps/permissions)
|
||||
@@ -95,7 +97,7 @@
|
||||
:params [dapp]
|
||||
:on-success #()})}))
|
||||
|
||||
(fx/defn process-next-permission
|
||||
(rf/defn process-next-permission
|
||||
"Process next permission by removing it from pending permissions and prompting user
|
||||
if there is no pending permissions left, save all granted permissions
|
||||
and return the result to the bridge"
|
||||
@@ -105,46 +107,47 @@
|
||||
{:db db}
|
||||
(let [pending-permissions (get-in db [:browser/options :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]
|
||||
(if (and yield-control? allowed?)
|
||||
(permission-yield-control new-cofx dapp-name permission message-id params)
|
||||
(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]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(send-response-to-bridge requested-permission
|
||||
message-id
|
||||
true
|
||||
(get-permission-data cofx requested-permission))
|
||||
(process-next-permission dapp-name)))
|
||||
|
||||
(fx/defn allow-permission
|
||||
(rf/defn allow-permission
|
||||
"Add permission to set of allowed permission and process next permission"
|
||||
{:events [:browser.permissions.ui/dapp-permission-allowed]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(let [{:keys [requested-permission message-id dapp-name yield-control? params]}
|
||||
(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)
|
||||
(if yield-control?
|
||||
(permission-yield-control dapp-name requested-permission message-id params)
|
||||
(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"
|
||||
{:events [:browser.permissions.ui/dapp-permission-denied]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(let [{:keys [requested-permission message-id dapp-name]} (get-in db [:browser/options :show-permission])]
|
||||
(fx/merge (assoc-in cofx [:db :browser/options :show-permission] nil)
|
||||
(let [{:keys [requested-permission message-id dapp-name]} (get-in db
|
||||
[:browser/options :show-permission])]
|
||||
(rf/merge (assoc-in cofx [:db :browser/options :show-permission] nil)
|
||||
(send-response-to-bridge requested-permission
|
||||
message-id
|
||||
false
|
||||
(get-permission-data cofx requested-permission))
|
||||
(process-next-permission dapp-name))))
|
||||
|
||||
(fx/defn process-permission
|
||||
(rf/defn process-permission
|
||||
"Process the permission requested by a dapp
|
||||
If supported permission is already granted, return the result immediatly to the bridge
|
||||
Otherwise process the first permission which will prompt user"
|
||||
@@ -156,15 +159,18 @@
|
||||
(not supported-permission)
|
||||
(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))
|
||||
|
||||
:else
|
||||
(process-next-permission (update-in cofx [:db :browser/options :pending-permissions]
|
||||
conj {:permission permission
|
||||
:allowed? (or permission-allowed?
|
||||
(:allowed? supported-permission))
|
||||
:yield-control? (:yield-control? supported-permission)
|
||||
:params params
|
||||
:message-id message-id})
|
||||
(process-next-permission (update-in cofx
|
||||
[:db :browser/options :pending-permissions]
|
||||
conj
|
||||
{:permission permission
|
||||
:allowed? (or permission-allowed?
|
||||
(:allowed? supported-permission))
|
||||
:yield-control? (:yield-control? supported-permission)
|
||||
:params params
|
||||
:message-id message-id})
|
||||
dapp-name))))
|
||||
|
||||
+164
-150
@@ -1,31 +1,33 @@
|
||||
(ns status-im.chat.models
|
||||
(:require [re-frame.core :as re-frame]
|
||||
[taoensso.timbre :as log]
|
||||
[status-im.multiaccounts.model :as multiaccounts.model]
|
||||
(:require [clojure.set :as set]
|
||||
[quo.design-system.colors :as colors]
|
||||
[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-im2.contexts.chat.messages.message.delete-message.events :as delete-message]
|
||||
[status-im2.contexts.chat.messages.message.delete-message-for-me.events :as delete-for-me]
|
||||
[status-im.constants :as constants]
|
||||
[status-im.data-store.chats :as chats-store]
|
||||
[status-im.mailserver.core :as mailserver]
|
||||
[status-im.data-store.contacts :as contacts-store]
|
||||
[status-im.ethereum.json-rpc :as json-rpc]
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
[quo.design-system.colors :as colors]
|
||||
[status-im.constants :as constants]
|
||||
[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.mailserver.core :as mailserver]
|
||||
[status-im.multiaccounts.model :as multiaccounts.model]
|
||||
[status-im.ui.screens.chat.state :as chat.state]
|
||||
[clojure.set :as set]
|
||||
[status-im2.navigation.events :as navigation]))
|
||||
[status-im.utils.clocks :as utils.clocks]
|
||||
[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"))))
|
||||
|
||||
(defn- get-chat [cofx chat-id]
|
||||
(defn- get-chat
|
||||
[cofx chat-id]
|
||||
(get-in cofx [:db :chats chat-id]))
|
||||
|
||||
(defn multi-user-chat?
|
||||
@@ -49,7 +51,8 @@
|
||||
([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)]
|
||||
(:active chat)))
|
||||
|
||||
@@ -85,52 +88,56 @@
|
||||
(defn- create-new-chat
|
||||
[chat-id {:keys [db now]}]
|
||||
(let [name (get-in db [:contacts/contacts chat-id :name])]
|
||||
{:chat-id chat-id
|
||||
:name (or name "")
|
||||
:color (rand-nth colors/chat-colors)
|
||||
:chat-type constants/one-to-one-chat-type
|
||||
:group-chat false
|
||||
:timestamp now
|
||||
:contacts #{chat-id}
|
||||
:last-clock-value 0}))
|
||||
{:chat-id chat-id
|
||||
:name (or name "")
|
||||
:color (rand-nth colors/chat-colors)
|
||||
:chat-type constants/one-to-one-chat-type
|
||||
:group-chat false
|
||||
:timestamp now
|
||||
:contacts #{chat-id}
|
||||
:last-clock-value 0}))
|
||||
|
||||
(defn map-chats [{:keys [db] :as cofx}]
|
||||
(defn map-chats
|
||||
[{:keys [db] :as cofx}]
|
||||
(fn [val]
|
||||
(assoc
|
||||
(merge
|
||||
(or (get (:chats db) (:chat-id val))
|
||||
(create-new-chat (:chat-id val) cofx))
|
||||
val)
|
||||
:invitation-admin (:invitation-admin val))))
|
||||
:invitation-admin
|
||||
(:invitation-admin val))))
|
||||
|
||||
(defn filter-chats [db]
|
||||
(defn filter-chats
|
||||
[db]
|
||||
(fn [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
|
||||
:as cofx}]
|
||||
:as cofx}]
|
||||
(when (and (= view-id :chat)
|
||||
(not (contains? chats current-chat-id)))
|
||||
(navigation/navigate-back cofx)))
|
||||
|
||||
(fx/defn ensure-chats
|
||||
(rf/defn ensure-chats
|
||||
"Add chats to db and update"
|
||||
[{:keys [db] :as cofx} 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}]
|
||||
(if (not (or active muted))
|
||||
(update acc :removed-chats conj chat-id)
|
||||
(cond-> acc
|
||||
(and (not profile-public-key) (not timeline?) (not community-id) active)
|
||||
(update :chats-home-list conj chat-id)
|
||||
:always
|
||||
(assoc-in [:all-chats chat-id] chat))))
|
||||
{:all-chats {}
|
||||
:chats-home-list #{}
|
||||
:removed-chats #{}}
|
||||
(map (map-chats cofx) chats))]
|
||||
(fx/merge
|
||||
(reduce
|
||||
(fn [acc {:keys [chat-id profile-public-key timeline? community-id active muted] :as chat}]
|
||||
(if (not (or active muted))
|
||||
(update acc :removed-chats conj chat-id)
|
||||
(cond-> acc
|
||||
(and (not profile-public-key) (not timeline?) (not community-id) active)
|
||||
(update :chats-home-list conj chat-id)
|
||||
:always
|
||||
(assoc-in [:all-chats chat-id] chat))))
|
||||
{:all-chats {}
|
||||
:chats-home-list #{}
|
||||
:removed-chats #{}}
|
||||
(map (map-chats cofx) chats))]
|
||||
(rf/merge
|
||||
cofx
|
||||
(merge {:db (-> db
|
||||
(update :chats merge all-chats)
|
||||
@@ -143,62 +150,66 @@
|
||||
(get-in db [:multiaccount :remote-push-notifications-enabled?])]}))
|
||||
leave-removed-chat)))
|
||||
|
||||
(fx/defn clear-history
|
||||
(rf/defn clear-history
|
||||
"Clears history of the particular chat"
|
||||
[{:keys [db] :as cofx} chat-id remove-chat?]
|
||||
(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?)
|
||||
0
|
||||
(or (:clock-value last-message)
|
||||
deleted-at-clock-value
|
||||
(utils.clocks/send 0)))]
|
||||
{:db (-> db
|
||||
(assoc-in [:messages chat-id] {})
|
||||
(update-in [:message-lists] dissoc chat-id)
|
||||
(update :chats (fn [chats]
|
||||
(if (contains? chats chat-id)
|
||||
(update chats chat-id merge
|
||||
{:last-message nil
|
||||
:unviewed-messages-count 0
|
||||
:unviewed-mentions-count 0
|
||||
:deleted-at-clock-value last-message-clock-value})
|
||||
chats))))}))
|
||||
{:db (-> db
|
||||
(assoc-in [:messages chat-id] {})
|
||||
(update-in [:message-lists] dissoc chat-id)
|
||||
(update :chats
|
||||
(fn [chats]
|
||||
(if (contains? chats chat-id)
|
||||
(update chats
|
||||
chat-id
|
||||
merge
|
||||
{:last-message nil
|
||||
: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"
|
||||
{:events [:chat.ui/clear-history]}
|
||||
[{:keys [db] :as cofx} chat-id remove-chat?]
|
||||
(fx/merge cofx
|
||||
{:db db
|
||||
::json-rpc/call [{:method "wakuext_clearHistory"
|
||||
:params [{:id chat-id}]
|
||||
(rf/merge cofx
|
||||
{:db db
|
||||
::json-rpc/call [{:method "wakuext_clearHistory"
|
||||
:params [{:id 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?)))
|
||||
|
||||
(fx/defn chat-deactivated
|
||||
(rf/defn chat-deactivated
|
||||
{:events [::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"
|
||||
[{:keys [db now] :as cofx} chat-id]
|
||||
(fx/merge
|
||||
(rf/merge
|
||||
cofx
|
||||
{:db (-> (if (get-in db [:chats chat-id :muted])
|
||||
(assoc-in db [:chats chat-id :active] false)
|
||||
(update db :chats dissoc chat-id))
|
||||
(update :chats-home-list disj chat-id)
|
||||
(assoc :current-chat-id nil))
|
||||
::json-rpc/call [{:method "wakuext_deactivateChat"
|
||||
:params [{:id chat-id}]
|
||||
{:db (-> (if (get-in db [:chats chat-id :muted])
|
||||
(assoc-in db [:chats chat-id :active] false)
|
||||
(update db :chats dissoc chat-id))
|
||||
(update :chats-home-list disj chat-id)
|
||||
(assoc :current-chat-id nil))
|
||||
::json-rpc/call [{:method "wakuext_deactivateChat"
|
||||
:params [{:id 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)))
|
||||
|
||||
(fx/defn offload-messages
|
||||
(rf/defn offload-messages
|
||||
{:events [:offload-messages]}
|
||||
[{:keys [db]} chat-id]
|
||||
(merge {:db (-> db
|
||||
@@ -208,52 +219,52 @@
|
||||
(when (and (= chat-id constants/timeline-chat-id) (= (:view-id db) :status))
|
||||
{:dispatch [:init-timeline-chat]})))
|
||||
|
||||
(fx/defn close-chat
|
||||
(rf/defn close-chat
|
||||
{:events [:close-chat]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(when-let [chat-id (:current-chat-id db)]
|
||||
(chat.state/reset-visible-item)
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{:db (dissoc db :current-chat-id)}
|
||||
(delete-for-me/sync-all)
|
||||
(delete-message/send-all)
|
||||
(offload-messages chat-id))))
|
||||
|
||||
(fx/defn force-close-chat
|
||||
(rf/defn force-close-chat
|
||||
[{:keys [db] :as cofx} chat-id]
|
||||
(do
|
||||
(chat.state/reset-visible-item)
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{:db (dissoc db :current-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"
|
||||
{:events [:chat.ui/remove-chat]}
|
||||
[{:keys [db now] :as cofx} chat-id]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{:clear-message-notifications
|
||||
[[chat-id] (get-in db [:multiaccount :remote-push-notifications-enabled?])]}
|
||||
(deactivate-chat chat-id)
|
||||
(offload-messages chat-id)))
|
||||
|
||||
(fx/defn show-more-chats
|
||||
(rf/defn show-more-chats
|
||||
{:events [:chat.ui/show-more-chats]}
|
||||
[{:keys [db]}]
|
||||
(when (< (:home-items-show-number db) (count (:chats db)))
|
||||
{: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"
|
||||
{:events [:chat.ui/preload-chat-data]}
|
||||
[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"
|
||||
{:events [:chat.ui/navigate-to-chat]}
|
||||
[{db :db :as cofx} chat-id]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{:dispatch [:navigate-to :chat]}
|
||||
(navigation/change-tab :chat)
|
||||
(when-not (= (:view-id db) :community)
|
||||
@@ -266,11 +277,11 @@
|
||||
#(when (group-chat? cofx 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"
|
||||
{:events [:chat.ui/navigate-to-chat-nav2]}
|
||||
[{db :db :as cofx} chat-id from-shell?]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{:dispatch [:navigate-to-nav2 :chat chat-id from-shell?]}
|
||||
(when-not (= (:view-id db) :community)
|
||||
(navigation/pop-to-root-tab :shell-stack))
|
||||
@@ -282,65 +293,68 @@
|
||||
#(when (group-chat? cofx chat-id)
|
||||
(loading/load-chat % chat-id))))
|
||||
|
||||
(fx/defn handle-clear-history-response
|
||||
(rf/defn handle-clear-history-response
|
||||
{:events [::history-cleared]}
|
||||
[{:keys [db]} chat-id response]
|
||||
(let [chat (chats-store/<-rpc (first (:chats response)))]
|
||||
{: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]}
|
||||
[{: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 (when contact-rpc (contacts-store/<-rpc contact-rpc))]
|
||||
{:db (cond-> db
|
||||
contact
|
||||
(assoc-in [:contacts/contacts chat-id] contact)
|
||||
:always
|
||||
(assoc-in [:chats chat-id] chat)
|
||||
:always
|
||||
(update :chats-home-list conj chat-id))
|
||||
contact (when contact-rpc (contacts-store/<-rpc contact-rpc))]
|
||||
{:db (cond-> db
|
||||
contact
|
||||
(assoc-in [:contacts/contacts chat-id] contact)
|
||||
:always
|
||||
(assoc-in [:chats chat-id] chat)
|
||||
:always
|
||||
(update :chats-home-list conj 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"
|
||||
{:events [:chat.ui/navigate-to-pinned-messages]}
|
||||
[cofx 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"
|
||||
{:events [:chat.ui/start-chat]}
|
||||
[{:keys [db] :as cofx} chat-id ens-name]
|
||||
;; don't allow to open chat with yourself
|
||||
(when (not= (multiaccounts.model/current-public-key cofx) chat-id)
|
||||
{::json-rpc/call [{:method "wakuext_createOneToOneChat"
|
||||
:params [{:id chat-id :ensName ens-name}]
|
||||
{::json-rpc/call [{:method "wakuext_createOneToOneChat"
|
||||
:params [{:id chat-id :ensName ens-name}]
|
||||
: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))
|
||||
|
||||
(defn my-profile-chat-topic [db]
|
||||
(defn my-profile-chat-topic
|
||||
[db]
|
||||
(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]}
|
||||
[{:keys [db]} chat-id response]
|
||||
{:db (-> db
|
||||
(assoc-in [:chats chat-id] (chats-store/<-rpc (first (:chats response))))
|
||||
(update :chats-home-list conj chat-id))
|
||||
{:db (-> db
|
||||
(assoc-in [:chats chat-id] (chats-store/<-rpc (first (:chats response))))
|
||||
(update :chats-home-list conj chat-id))
|
||||
:dispatch [:chat.ui/navigate-to-chat chat-id]})
|
||||
|
||||
(fx/defn create-public-chat-go [_ chat-id]
|
||||
{::json-rpc/call [{:method "wakuext_createPublicChat"
|
||||
:params [{:id chat-id}]
|
||||
(rf/defn create-public-chat-go
|
||||
[_ chat-id]
|
||||
{::json-rpc/call [{:method "wakuext_createPublicChat"
|
||||
:params [{:id 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"
|
||||
{:events [:chat.ui/start-public-chat]}
|
||||
[cofx topic]
|
||||
@@ -353,10 +367,10 @@
|
||||
{:utils/show-popup {:title (i18n/label :t/cant-open-public-chat)
|
||||
:content (i18n/label :t/invalid-public-chat-topic)}}))
|
||||
|
||||
(fx/defn profile-chat-created
|
||||
(rf/defn profile-chat-created
|
||||
{:events [::profile-chat-created]}
|
||||
[{:keys [db] :as cofx} chat-id response navigate-to?]
|
||||
(fx/merge
|
||||
(rf/merge
|
||||
cofx
|
||||
{:db db}
|
||||
#(when response
|
||||
@@ -366,19 +380,19 @@
|
||||
{:dispatch-n [[:chat.ui/preload-chat-data chat-id]
|
||||
[:open-modal :profile]]})))
|
||||
|
||||
(fx/defn start-profile-chat
|
||||
(rf/defn start-profile-chat
|
||||
"Starts a new profile chat"
|
||||
{:events [:start-profile-chat]}
|
||||
[cofx profile-public-key navigate-to?]
|
||||
(let [chat-id (profile-chat-topic profile-public-key)]
|
||||
(if (active-chat? cofx chat-id)
|
||||
{:dispatch [::profile-chat-created chat-id nil navigate-to?]}
|
||||
{::json-rpc/call [{:method "wakuext_createProfileChat"
|
||||
:params [{:id profile-public-key}]
|
||||
{::json-rpc/call [{:method "wakuext_createProfileChat"
|
||||
:params [{:id profile-public-key}]
|
||||
: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)"
|
||||
{:events [:chat/disable-cooldown]}
|
||||
[{:keys [db]}]
|
||||
@@ -392,40 +406,40 @@
|
||||
(i18n/label :cooldown/warning-message)
|
||||
#())))
|
||||
|
||||
(fx/defn mute-chat-failed
|
||||
(rf/defn mute-chat-failed
|
||||
{:events [::mute-chat-failed]}
|
||||
[{:keys [db] :as cofx} chat-id muted? error]
|
||||
(log/error "mute chat failed" chat-id error)
|
||||
{: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]}
|
||||
[_ chat-id]
|
||||
(log/debug "muted chat successfully" chat-id))
|
||||
|
||||
(fx/defn mute-chat
|
||||
(rf/defn mute-chat
|
||||
{:events [::mute-chat-toggled]}
|
||||
[{:keys [db] :as cofx} chat-id muted?]
|
||||
(let [method (if muted? "wakuext_muteChat" "wakuext_unmuteChat")]
|
||||
{:db (assoc-in db [:chats chat-id :muted] muted?)
|
||||
::json-rpc/call [{:method method
|
||||
:params [chat-id]
|
||||
:on-error #(re-frame/dispatch [::mute-chat-failed chat-id muted? %])
|
||||
{:db (assoc-in db [:chats chat-id :muted] muted?)
|
||||
::json-rpc/call [{:method method
|
||||
:params [chat-id]
|
||||
:on-error #(re-frame/dispatch [::mute-chat-failed chat-id muted? %])
|
||||
:on-success #(re-frame/dispatch [::mute-chat-toggled-successfully chat-id])}]}))
|
||||
|
||||
(fx/defn show-profile
|
||||
(rf/defn show-profile
|
||||
{:events [:chat.ui/show-profile]}
|
||||
[{:keys [db] :as cofx} identity ens-name]
|
||||
(let [my-public-key (get-in db [:multiaccount :public-key])]
|
||||
(when (not= my-public-key identity)
|
||||
(fx/merge
|
||||
(rf/merge
|
||||
cofx
|
||||
{:db (-> db
|
||||
(assoc :contacts/identity identity)
|
||||
(assoc :contacts/ens-name ens-name))}
|
||||
(start-profile-chat identity true)))))
|
||||
|
||||
(fx/defn clear-history-pressed
|
||||
(rf/defn clear-history-pressed
|
||||
{:events [:chat.ui/clear-history-pressed]}
|
||||
[_ chat-id]
|
||||
{:ui/show-confirmation
|
||||
@@ -436,19 +450,19 @@
|
||||
(re-frame/dispatch [:bottom-sheet/hide])
|
||||
(re-frame/dispatch [:chat.ui/clear-history chat-id false]))}})
|
||||
|
||||
(fx/defn gaps-failed
|
||||
(rf/defn gaps-failed
|
||||
{:events [::gaps-failed]}
|
||||
[{:keys [db]} chat-id gap-ids error]
|
||||
(log/error "failed to fetch gaps" chat-id gap-ids error)
|
||||
{: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]}
|
||||
[{:keys [db]} chat-id error]
|
||||
(log/error "failed to sync chat" chat-id error)
|
||||
{: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]}
|
||||
[{:keys [db] :as cofx} 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)
|
||||
(dissoc :mailserver/fetching-gaps-in-progress))})
|
||||
|
||||
(fx/defn gaps-filled
|
||||
(rf/defn gaps-filled
|
||||
{:events [::gaps-filled]}
|
||||
[{:keys [db] :as cofx} chat-id message-ids]
|
||||
(fx/merge
|
||||
(rf/merge
|
||||
cofx
|
||||
{:db (-> db
|
||||
(update-in [:messages chat-id] (fn [messages] (apply dissoc messages message-ids)))
|
||||
(dissoc :mailserver/fetching-gaps-in-progress))}
|
||||
(message-list/rebuild-message-list chat-id)))
|
||||
|
||||
(fx/defn fill-gaps
|
||||
(rf/defn fill-gaps
|
||||
[cofx chat-id gap-ids]
|
||||
{::json-rpc/call [{:method "wakuext_fillGaps"
|
||||
:params [chat-id gap-ids]
|
||||
{::json-rpc/call [{:method "wakuext_fillGaps"
|
||||
:params [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]
|
||||
(log/debug "syncing chat from sync from")
|
||||
{::json-rpc/call [{:method "wakuext_syncChatFromSyncedFrom"
|
||||
:params [chat-id]
|
||||
{::json-rpc/call [{:method "wakuext_syncChatFromSyncedFrom"
|
||||
:params [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]}
|
||||
[{:keys [db] :as cofx} chat-id gap-ids]
|
||||
(let [use-status-nodes? (mailserver/fetch-use-mailservers? {:db db})]
|
||||
(log/info "filling gaps if use-status-nodes = true" chat-id gap-ids)
|
||||
(when use-status-nodes?
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{:db (assoc db :mailserver/fetching-gaps-in-progress gap-ids)}
|
||||
(if (= gap-ids #{:first-gap})
|
||||
(sync-chat-from-sync-from chat-id)
|
||||
(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]}
|
||||
[_ chat-id]
|
||||
{:ui/show-confirmation
|
||||
@@ -505,7 +519,7 @@
|
||||
(re-frame/dispatch [:bottom-sheet/hide])
|
||||
(re-frame/dispatch [:chat.ui/remove-chat chat-id]))}})
|
||||
|
||||
(fx/defn decrease-unviewed-count
|
||||
(rf/defn decrease-unviewed-count
|
||||
{:events [:chat/decrease-unviewed-count]}
|
||||
[{:keys [db]} chat-id {:keys [count countWithMentions]}]
|
||||
{:db (-> db
|
||||
|
||||
@@ -1,24 +1,25 @@
|
||||
(ns status-im.chat.models.images
|
||||
(:require [re-frame.core :as re-frame]
|
||||
[status-im.utils.fx :as fx]
|
||||
["@react-native-community/cameraroll" :as CameraRoll]
|
||||
(:require ["@react-native-community/cameraroll" :as CameraRoll]
|
||||
["react-native-blob-util" :default ReactNativeBlobUtil]
|
||||
[status-im.utils.types :as types]
|
||||
[status-im.utils.config :as config]
|
||||
[clojure.string :as string]
|
||||
[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.react :as react]
|
||||
[status-im.utils.image-processing :as image-processing]
|
||||
[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.config :as config]
|
||||
[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)
|
||||
|
||||
(defn- resize-and-call [uri cb]
|
||||
(defn- resize-and-call
|
||||
[uri cb]
|
||||
(react/image-get-size
|
||||
uri
|
||||
(fn [width height]
|
||||
@@ -34,16 +35,19 @@
|
||||
(cb path)))
|
||||
#(log/error "could not resize image" %))))))
|
||||
|
||||
(defn result->id [^js result]
|
||||
(defn result->id
|
||||
[^js result]
|
||||
(if platform/ios?
|
||||
(.-localIdentifier result)
|
||||
(.-path result)))
|
||||
|
||||
(def temp-image-url (str (fs/cache-dir) "/StatusIm_Image.jpeg"))
|
||||
|
||||
(defn download-image-http [base64-uri on-success]
|
||||
(-> (.config ReactNativeBlobUtil (clj->js {:trusty platform/ios?
|
||||
:path temp-image-url}))
|
||||
(defn download-image-http
|
||||
[base64-uri on-success]
|
||||
(-> (.config ReactNativeBlobUtil
|
||||
(clj->js {:trusty platform/ios?
|
||||
:path temp-image-url}))
|
||||
(.fetch "GET" base64-uri)
|
||||
(.then #(on-success (.path %)))
|
||||
(.catch #(log/error "could not save image"))))
|
||||
@@ -70,15 +74,16 @@
|
||||
::chat-open-image-picker-camera
|
||||
(fn [current-chat-id]
|
||||
(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
|
||||
::chat-open-image-picker
|
||||
(fn [chat-id]
|
||||
(react/show-image-picker
|
||||
(fn [^js images]
|
||||
;; NOTE(Ferossgp): Because we can't highlight the already selected images inside
|
||||
;; gallery, we just clean previous state and set all newly picked images
|
||||
;; NOTE(Ferossgp): Because we can't highlight the already selected images inside
|
||||
;; gallery, we just clean previous state and set all newly picked images
|
||||
(when (and platform/ios? (pos? (count images)))
|
||||
(re-frame/dispatch [:chat.ui/clear-sending-images chat-id]))
|
||||
(doseq [^js result (if platform/ios?
|
||||
@@ -86,8 +91,8 @@
|
||||
[images])]
|
||||
(resize-and-call (.-path 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
|
||||
;; selects too many images the app crashes.
|
||||
;; NOTE(Ferossgp): On android you cannot set max limit on images, when a user
|
||||
;; selects too many images the app crashes.
|
||||
{:media-type "photo"
|
||||
:multiple platform/ios?})))
|
||||
|
||||
@@ -105,13 +110,17 @@
|
||||
{:permissions [:read-external-storage]
|
||||
:on-allowed (fn []
|
||||
(-> (if end-cursor
|
||||
(.getPhotos CameraRoll #js {:first num :after end-cursor :assetType "Photos" :groupTypes "All"})
|
||||
(.getPhotos CameraRoll #js {:first num :assetType "Photos" :groupTypes "All"}))
|
||||
(.getPhotos
|
||||
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 %)]
|
||||
(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"))))})))
|
||||
|
||||
(fx/defn image-captured
|
||||
(rf/defn image-captured
|
||||
{:events [:chat.ui/image-captured]}
|
||||
[{:keys [db]} chat-id uri]
|
||||
(let [current-chat-id (or chat-id (:current-chat-id db))
|
||||
@@ -120,24 +129,24 @@
|
||||
(not (get images uri)))
|
||||
{::image-selected [uri current-chat-id]})))
|
||||
|
||||
(fx/defn on-end-reached
|
||||
(rf/defn on-end-reached
|
||||
{:events [:camera-roll/on-end-reached]}
|
||||
[_ end-cursor 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-get-photos 20 end-cursor])))
|
||||
|
||||
(fx/defn camera-roll-get-photos
|
||||
(rf/defn camera-roll-get-photos
|
||||
{:events [:chat.ui/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]}
|
||||
[{:keys [db]} 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]}
|
||||
[{:keys [db] :as cofx} photos page-info end-cursor]
|
||||
(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/loading-more false))}))
|
||||
|
||||
(fx/defn clear-sending-images
|
||||
(rf/defn clear-sending-images
|
||||
{:events [:chat.ui/clear-sending-images]}
|
||||
[{:keys [db]} current-chat-id]
|
||||
{: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]}
|
||||
[{:keys [db] :as cofx} chat-id]
|
||||
(let [current-chat-id (or chat-id (:current-chat-id db))]
|
||||
(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]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(cancel-sending-image cofx (chat/my-profile-chat-topic db)))
|
||||
|
||||
(fx/defn image-selected
|
||||
(rf/defn image-selected
|
||||
{:events [:chat.ui/image-selected]}
|
||||
[{:keys [db]} current-chat-id original 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]}
|
||||
[{:keys [db]} original]
|
||||
(let [current-chat-id (:current-chat-id db)]
|
||||
{: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]}
|
||||
[{:keys [db]} chat-id]
|
||||
(let [current-chat-id (or chat-id (:current-chat-id db))
|
||||
@@ -182,12 +191,12 @@
|
||||
(when (< (count images) config/max-images-batch)
|
||||
{::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]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(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]}
|
||||
[{:keys [db]} chat-id]
|
||||
(let [current-chat-id (or chat-id (:current-chat-id db))
|
||||
@@ -195,12 +204,12 @@
|
||||
(when (< (count images) config/max-images-batch)
|
||||
{::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]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(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]}
|
||||
[{:keys [db]} uri chat-id]
|
||||
(let [current-chat-id (or chat-id (:current-chat-id db))
|
||||
@@ -212,12 +221,12 @@
|
||||
(not (get images uri)))
|
||||
{::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]}
|
||||
[{:keys [db] :as cofx} uri]
|
||||
(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]}
|
||||
[_ base64-uri]
|
||||
{::save-image-to-gallery base64-uri})
|
||||
|
||||
@@ -12,9 +12,9 @@
|
||||
[status-im.ethereum.json-rpc :as json-rpc]
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
[status-im.utils.datetime :as datetime]
|
||||
[status-im.utils.fx :as fx]
|
||||
[status-im.utils.utils :as utils]
|
||||
[taoensso.timbre :as log]))
|
||||
[taoensso.timbre :as log]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(defn text->emoji
|
||||
"Replaces emojis in a specified `text`"
|
||||
@@ -26,7 +26,7 @@
|
||||
(.-char ^js emoji-map)
|
||||
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
|
||||
as arguments and returns new fx. Always clear all validation messages."
|
||||
{:events [:chat.ui/set-chat-input-text]}
|
||||
@@ -34,24 +34,25 @@
|
||||
(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))}))
|
||||
|
||||
(fx/defn set-timeline-input-text
|
||||
(rf/defn set-timeline-input-text
|
||||
{:events [:chat.ui/set-timeline-input-text]}
|
||||
[{db :db} new-input]
|
||||
{:db (assoc-in db [:chat/inputs (chat/my-profile-chat-topic db) :input-text]
|
||||
(text->emoji new-input))})
|
||||
{:db (assoc-in db
|
||||
[: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]}
|
||||
[{:keys [db] :as cofx} text-input-ref {:keys [alias name searched-text match] :as user}]
|
||||
(let [chat-id (:current-chat-id db)
|
||||
new-text (mentions/new-input-text-with-mention cofx user)
|
||||
at-sign-idx (get-in db [:chats/mentions chat-id :mentions :at-sign-idx])
|
||||
cursor (+ at-sign-idx (count name) 2)]
|
||||
(fx/merge
|
||||
(rf/merge
|
||||
cofx
|
||||
{:db (-> db
|
||||
(assoc-in [:chats/cursor chat-id] cursor)
|
||||
(assoc-in [:chats/mention-suggestions chat-id] nil))
|
||||
{:db (-> db
|
||||
(assoc-in [:chats/cursor chat-id] cursor)
|
||||
(assoc-in [:chats/mention-suggestions chat-id] nil))
|
||||
:set-text-input-value [chat-id new-text text-input-ref]}
|
||||
(set-chat-input-text new-text chat-id)
|
||||
;; NOTE(rasom): Some keyboards do not react on selection property passed to
|
||||
@@ -74,67 +75,77 @@
|
||||
:end end}))
|
||||
(mentions/recheck-at-idxs {alias user}))))
|
||||
|
||||
(defn- start-cooldown [{:keys [db]} cooldowns]
|
||||
(defn- start-cooldown
|
||||
[{:keys [db]} cooldowns]
|
||||
{:dispatch-later [{:dispatch [:chat/disable-cooldown]
|
||||
:ms (chat.constants/cooldown-periods-ms
|
||||
cooldowns
|
||||
chat.constants/default-cooldown-period-ms)}]
|
||||
:show-cooldown-warning nil
|
||||
:db (assoc db
|
||||
:chat/cooldowns (if (= chat.constants/cooldown-reset-threshold cooldowns)
|
||||
0
|
||||
cooldowns)
|
||||
:chat/cooldowns (if
|
||||
(=
|
||||
chat.constants/cooldown-reset-threshold
|
||||
cooldowns)
|
||||
0
|
||||
cooldowns)
|
||||
: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"
|
||||
[{{:keys [chat/last-outgoing-message-sent-at
|
||||
chat/cooldowns
|
||||
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)
|
||||
(let [spamming-fast? (< (- (datetime/timestamp) last-outgoing-message-sent-at)
|
||||
(+ chat.constants/spam-interval-ms (* 1000 cooldowns)))
|
||||
spamming-frequently? (= chat.constants/spam-message-frequency-threshold spam-messages-frequency)]
|
||||
(let [spamming-fast? (< (- (datetime/timestamp) last-outgoing-message-sent-at)
|
||||
(+ chat.constants/spam-interval-ms (* 1000 cooldowns)))
|
||||
spamming-frequently? (= chat.constants/spam-message-frequency-threshold
|
||||
spam-messages-frequency)]
|
||||
(cond-> {:db (assoc db
|
||||
:chat/last-outgoing-message-sent-at (datetime/timestamp)
|
||||
:chat/spam-messages-frequency (if spamming-fast?
|
||||
(inc spam-messages-frequency)
|
||||
0))}
|
||||
:chat/spam-messages-frequency (if spamming-fast?
|
||||
(inc spam-messages-frequency)
|
||||
0))}
|
||||
|
||||
(and spamming-fast? spamming-frequently?)
|
||||
(start-cooldown (inc cooldowns))))))
|
||||
|
||||
(fx/defn reply-to-message
|
||||
(rf/defn reply-to-message
|
||||
"Sets reference to previous chat message and focuses on input"
|
||||
{:events [:chat.ui/reply-to-message]}
|
||||
[{:keys [db] :as cofx} message]
|
||||
(let [current-chat-id (:current-chat-id db)]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{:db (-> db
|
||||
(assoc-in [:chat/inputs current-chat-id :metadata :responding-to-message]
|
||||
message)
|
||||
(assoc-in [:chat/inputs current-chat-id :metadata :editing-message] nil)
|
||||
(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"
|
||||
{:events [:chat.ui/edit-message]}
|
||||
[{:keys [db] :as cofx} message]
|
||||
(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]
|
||||
:db (-> db
|
||||
(assoc-in [:chat/inputs current-chat-id :metadata :editing-message]
|
||||
message)
|
||||
(assoc-in [:chat/inputs current-chat-id :metadata :responding-to-message] nil)
|
||||
(update-in [:chat/inputs current-chat-id :metadata]
|
||||
dissoc :sending-image))}))
|
||||
:db (-> db
|
||||
(assoc-in [:chat/inputs current-chat-id :metadata :editing-message]
|
||||
message)
|
||||
(assoc-in [:chat/inputs current-chat-id :metadata :responding-to-message] nil)
|
||||
(update-in [:chat/inputs current-chat-id :metadata]
|
||||
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"
|
||||
{:events [:chat.ui/send-contact-request]}
|
||||
[{:keys [db] :as cofx}]
|
||||
@@ -146,9 +157,10 @@
|
||||
nil)
|
||||
(assoc-in [:chat/inputs current-chat-id :metadata :editing-message] nil)
|
||||
(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"
|
||||
{:events [:chat.ui/cancel-message-reply]}
|
||||
[{:keys [db]}]
|
||||
@@ -160,16 +172,16 @@
|
||||
(when-not (string/blank? input-text)
|
||||
(let [{:keys [message-id]}
|
||||
(get-in db [:chat/inputs current-chat-id :metadata :responding-to-message])
|
||||
preferred-name (get-in db [:multiaccount :preferred-name])
|
||||
emoji? (message-content/emoji-only-content? {:text input-text
|
||||
:response-to message-id})]
|
||||
preferred-name (get-in db [:multiaccount :preferred-name])
|
||||
emoji? (message-content/emoji-only-content? {:text input-text
|
||||
:response-to message-id})]
|
||||
{:chat-id current-chat-id
|
||||
:content-type (if emoji?
|
||||
constants/content-type-emoji
|
||||
constants/content-type-text)
|
||||
:text input-text
|
||||
:response-to message-id
|
||||
:ens-name preferred-name})))
|
||||
:text input-text
|
||||
:response-to message-id
|
||||
:ens-name preferred-name})))
|
||||
|
||||
(defn build-image-messages
|
||||
[{db :db} chat-id]
|
||||
@@ -181,8 +193,9 @@
|
||||
:text (i18n/label :t/update-to-see-image {"locale" "en"})})
|
||||
images)))
|
||||
|
||||
(fx/defn clean-input [{:keys [db] :as cofx} current-chat-id]
|
||||
(fx/merge cofx
|
||||
(rf/defn clean-input
|
||||
[{:keys [db] :as cofx} current-chat-id]
|
||||
(rf/merge cofx
|
||||
{:db (-> db
|
||||
(assoc-in [:chat/inputs current-chat-id :metadata :sending-contact-request] 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))}
|
||||
(set-chat-input-text nil current-chat-id)))
|
||||
|
||||
(fx/defn cancel-message-edit
|
||||
(rf/defn cancel-message-edit
|
||||
"Cancels stage message edit"
|
||||
{:events [:chat.ui/cancel-message-edit]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(let [current-chat-id (:current-chat-id db)]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{:set-text-input-value [current-chat-id ""]}
|
||||
(clean-input current-chat-id)
|
||||
(mentions/clear-mentions)
|
||||
(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)
|
||||
text-message (build-text-message cofx input-text current-chat-id)
|
||||
messages (keep identity (conj image-messages text-message))]
|
||||
text-message (build-text-message cofx input-text current-chat-id)
|
||||
messages (keep identity (conj image-messages text-message))]
|
||||
(when (seq messages)
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(clean-input (:current-chat-id db))
|
||||
(process-cooldown)
|
||||
(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"
|
||||
{:events [:profile.ui/send-my-status-message]}
|
||||
[{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])
|
||||
image-messages (build-image-messages cofx current-chat-id)
|
||||
text-message (build-text-message cofx input-text current-chat-id)
|
||||
messages (keep identity (conj image-messages text-message))]
|
||||
image-messages (build-image-messages cofx current-chat-id)
|
||||
text-message (build-text-message cofx input-text current-chat-id)
|
||||
messages (keep identity (conj image-messages text-message))]
|
||||
(when (seq messages)
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(clean-input current-chat-id)
|
||||
(chat.message/send-messages messages)))))
|
||||
|
||||
(fx/defn send-audio-message
|
||||
(rf/defn send-audio-message
|
||||
[cofx audio-path duration current-chat-id]
|
||||
(when-not (string/blank? audio-path)
|
||||
(chat.message/send-message cofx {:chat-id current-chat-id
|
||||
:content-type constants/content-type-audio
|
||||
:audio-path audio-path
|
||||
:audio-duration-ms duration
|
||||
:text (i18n/label :t/update-to-listen-audio {"locale" "en"})})))
|
||||
(chat.message/send-message
|
||||
cofx
|
||||
{:chat-id current-chat-id
|
||||
:content-type constants/content-type-audio
|
||||
: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]
|
||||
(when-not (or (string/blank? hash) (and (string/blank? packID) (string/blank? pack)))
|
||||
(chat.message/send-message cofx {:chat-id current-chat-id
|
||||
:content-type constants/content-type-sticker
|
||||
:sticker {:hash hash
|
||||
(chat.message/send-message cofx
|
||||
{:chat-id current-chat-id
|
||||
:content-type constants/content-type-sticker
|
||||
:sticker {:hash hash
|
||||
: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]}]
|
||||
(fx/merge
|
||||
(rf/defn send-edited-message
|
||||
[{:keys [db] :as cofx} text {:keys [message-id quoted-message]}]
|
||||
(rf/merge
|
||||
cofx
|
||||
{::json-rpc/call [{:method "wakuext_editMessage"
|
||||
:params [{:id message-id
|
||||
:text text
|
||||
:content-type (if (message-content/emoji-only-content? {:text text :response-to quoted-message})
|
||||
constants/content-type-emoji
|
||||
constants/content-type-text)}]
|
||||
{::json-rpc/call [{:method "wakuext_editMessage"
|
||||
:params [{:id message-id
|
||||
:text text
|
||||
:content-type (if (message-content/emoji-only-content?
|
||||
{:text text :response-to quoted-message})
|
||||
constants/content-type-emoji
|
||||
constants/content-type-text)}]
|
||||
:js-response true
|
||||
:on-error #(log/error "failed to edit message " %)
|
||||
:on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %])}]}
|
||||
:on-error #(log/error "failed to edit message " %)
|
||||
:on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %])}]}
|
||||
(cancel-message-edit)
|
||||
(process-cooldown)))
|
||||
|
||||
(fx/defn send-current-message
|
||||
(rf/defn send-current-message
|
||||
"Sends message from current chat input"
|
||||
{:events [:chat.ui/send-current-message]}
|
||||
[{{:keys [current-chat-id] :as db} :db :as cofx}]
|
||||
(let [{:keys [input-text metadata]} (get-in db [:chat/inputs current-chat-id])
|
||||
editing-message (:editing-message metadata)
|
||||
input-text-with-mentions (mentions/check-mentions cofx input-text)]
|
||||
(fx/merge cofx
|
||||
editing-message (:editing-message metadata)
|
||||
input-text-with-mentions (mentions/check-mentions cofx input-text)]
|
||||
(rf/merge cofx
|
||||
(if editing-message
|
||||
(send-edited-message input-text-with-mentions editing-message)
|
||||
(send-messages input-text-with-mentions current-chat-id))
|
||||
(mentions/clear-mentions)
|
||||
(mentions/clear-cursor))))
|
||||
|
||||
(fx/defn send-contact-request
|
||||
(rf/defn send-contact-request
|
||||
{:events [:contacts/send-contact-request]}
|
||||
[{:keys [db] :as cofx} public-key message]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{:chat.ui/clear-inputs nil
|
||||
:chat.ui/clear-inputs-old nil
|
||||
::json-rpc/call [{:method "wakuext_sendContactRequest"
|
||||
::json-rpc/call [{:method "wakuext_sendContactRequest"
|
||||
:js-response true
|
||||
:params [{:id public-key :message message}]
|
||||
:on-error #(log/warn "failed to send a contact request" %)
|
||||
:on-success #(re-frame/dispatch [:transport/message-sent %])}]}
|
||||
:params [{:id public-key :message message}]
|
||||
:on-error #(log/warn "failed to send a contact request" %)
|
||||
:on-success #(re-frame/dispatch [:transport/message-sent %])}]}
|
||||
(mentions/clear-mentions)
|
||||
(mentions/clear-cursor)
|
||||
(clean-input (:current-chat-id db))
|
||||
(process-cooldown)))
|
||||
|
||||
(fx/defn cancel-contact-request
|
||||
(rf/defn cancel-contact-request
|
||||
"Cancels contact request"
|
||||
{:events [:chat.ui/cancel-contact-request]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(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)}
|
||||
(mentions/clear-mentions)
|
||||
(mentions/clear-cursor)
|
||||
(clean-input (:current-chat-id db))
|
||||
(process-cooldown))))
|
||||
|
||||
(fx/defn chat-send-sticker
|
||||
(rf/defn chat-send-sticker
|
||||
{:events [:chat/send-sticker]}
|
||||
[{{:keys [current-chat-id] :as db} :db :as cofx} {:keys [hash packID pack] :as sticker}]
|
||||
(fx/merge
|
||||
(rf/merge
|
||||
cofx
|
||||
{:db (update db
|
||||
:stickers/recent-stickers
|
||||
(fn [recent]
|
||||
(conj (remove #(= hash (:hash %)) recent) sticker)))
|
||||
{:db (update db
|
||||
:stickers/recent-stickers
|
||||
(fn [recent]
|
||||
(conj (remove #(= hash (:hash %)) recent) sticker)))
|
||||
::json-rpc/call [{:method "stickers_addRecent"
|
||||
:params [(int (if (string/blank? packID) pack packID)) hash]
|
||||
:on-success #()}]}
|
||||
(send-sticker-message sticker current-chat-id)))
|
||||
|
||||
(fx/defn chat-send-audio
|
||||
(rf/defn chat-send-audio
|
||||
{:events [:chat/send-audio]}
|
||||
[{{:keys [current-chat-id]} :db :as cofx} audio-path duration]
|
||||
(send-audio-message cofx audio-path duration current-chat-id))
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
(ns status-im.chat.models.link-preview
|
||||
(: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]
|
||||
[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]}
|
||||
[{{:keys [multiaccount]} :db :as cofx} site enabled?]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(multiaccounts.update/multiaccount-update
|
||||
:link-previews-enabled-sites
|
||||
(if enabled?
|
||||
@@ -17,10 +17,10 @@
|
||||
(disj (get multiaccount :link-previews-enabled-sites #{}) site))
|
||||
{})))
|
||||
|
||||
(fx/defn enable-all
|
||||
(rf/defn enable-all
|
||||
{:events [::enable-all]}
|
||||
[{{:keys [multiaccount]} :db :as cofx} link-previews-whitelist enabled?]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(multiaccounts.update/multiaccount-update
|
||||
:link-previews-enabled-sites
|
||||
(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))
|
||||
|
||||
(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))
|
||||
|
||||
(defn community-resolving [db community-id]
|
||||
(defn community-resolving
|
||||
[db community-id]
|
||||
(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]}
|
||||
[{:keys [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))
|
||||
|
||||
(fx/defn handle-community-resolved
|
||||
(rf/defn handle-community-resolved
|
||||
{:events [::community-resolved]}
|
||||
[{:keys [db] :as cofx} community-id community]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(cond-> {:db (community-resolved db community-id)}
|
||||
(some? community)
|
||||
(assoc :dispatch [::cache-link-preview-data
|
||||
(community-link community-id) community]))
|
||||
(assoc :dispatch
|
||||
[::cache-link-preview-data
|
||||
(community-link community-id) community]))
|
||||
(models.communities/handle-community community)))
|
||||
|
||||
(fx/defn resolve-community-info
|
||||
(rf/defn resolve-community-info
|
||||
{:events [::resolve-community-info]}
|
||||
[{:keys [db]} community-id]
|
||||
{:db (community-resolving db community-id)
|
||||
{:db (community-resolving db community-id)
|
||||
::json-rpc/call [{:method "wakuext_requestCommunityInfoFromMailserver"
|
||||
:params [community-id]
|
||||
:on-success #(re-frame/dispatch [::community-resolved community-id %])
|
||||
@@ -66,17 +71,18 @@
|
||||
(re-frame/dispatch [::community-failed-to-resolve community-id])
|
||||
(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]}
|
||||
[cofx link]
|
||||
{::json-rpc/call [{:method "wakuext_getLinkPreviewData"
|
||||
:params [link]
|
||||
:on-success #(re-frame/dispatch [::cache-link-preview-data link %])
|
||||
:on-error #(re-frame/dispatch [::cache-link-preview-data
|
||||
link
|
||||
{:error (str "Can't get preview data for " link)}])}]})
|
||||
:on-error #(re-frame/dispatch
|
||||
[::cache-link-preview-data
|
||||
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]}
|
||||
[{{:keys [multiaccount]} :db :as cofx} site data]
|
||||
(multiaccounts.update/optimistic
|
||||
@@ -90,23 +96,25 @@
|
||||
(community-link id)
|
||||
community]))
|
||||
|
||||
(fx/defn should-suggest-link-preview
|
||||
(rf/defn should-suggest-link-preview
|
||||
{:events [::should-suggest-link-preview]}
|
||||
[{:keys [db] :as cofx} enabled?]
|
||||
(multiaccounts.update/multiaccount-update
|
||||
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"
|
||||
:params []
|
||||
:on-success #(re-frame/dispatch [::link-preview-whitelist-received %])
|
||||
: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]}
|
||||
[{:keys [db]} whitelist]
|
||||
{:db (assoc db :link-previews-whitelist
|
||||
{:db (assoc db
|
||||
:link-previews-whitelist
|
||||
whitelist)})
|
||||
|
||||
@@ -1,24 +1,25 @@
|
||||
(ns status-im.chat.models.loading
|
||||
(:require [re-frame.core :as re-frame]
|
||||
[status-im.chat.models.message-list :as message-list]
|
||||
[status-im.constants :as constants]
|
||||
[status-im.data-store.chats :as data-store.chats]
|
||||
[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-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
|
||||
[^js cursor]
|
||||
(js/parseInt (.substring cursor 51 64)))
|
||||
|
||||
(defn clock-value->cursor [clock-value]
|
||||
(defn clock-value->cursor
|
||||
[clock-value]
|
||||
(str "000000000000000000000000000000000000000000000000000"
|
||||
clock-value
|
||||
"0x0000000000000000000000000000000000000000000000000000000000000000"))
|
||||
|
||||
(fx/defn update-chats-in-app-db
|
||||
(rf/defn update-chats-in-app-db
|
||||
{:events [:chats-list/load-success]}
|
||||
[{:keys [db]} ^js new-chats-js]
|
||||
(let [{:keys [all-chats chats-home-list]}
|
||||
@@ -30,27 +31,28 @@
|
||||
(update :chats-home-list conj chat-id)
|
||||
:always
|
||||
(assoc-in [:all-chats chat-id] chat))))
|
||||
{:all-chats {}
|
||||
{:all-chats {}
|
||||
:chats-home-list #{}}
|
||||
new-chats-js)]
|
||||
{:db (assoc db :chats all-chats
|
||||
{:db (assoc db
|
||||
:chats all-chats
|
||||
: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]}
|
||||
[{:keys [db]} ^js chat]
|
||||
(let [{:keys [chat-id] :as chat} (data-store.chats/<-rpc chat)]
|
||||
{:db (update-in db [:chats chat-id] merge chat)}))
|
||||
|
||||
(fx/defn load-chat
|
||||
(rf/defn load-chat
|
||||
[_ chat-id]
|
||||
{::json-rpc/call [{:method "wakuext_chat"
|
||||
:params [chat-id]
|
||||
{::json-rpc/call [{:method "wakuext_chat"
|
||||
:params [chat-id]
|
||||
: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]}
|
||||
[{:keys [db]} current-chat-id _ err]
|
||||
(log/error "failed loading messages" current-chat-id err)
|
||||
@@ -59,52 +61,55 @@
|
||||
|
||||
(defn mark-chat-all-read
|
||||
[db chat-id]
|
||||
(update-in db [:chats chat-id] assoc
|
||||
(update-in db
|
||||
[:chats chat-id]
|
||||
assoc
|
||||
:unviewed-messages-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]}
|
||||
[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]}
|
||||
[{:keys [db] :as cofx} chat-ids]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{:db (reduce mark-chat-all-read db chat-ids)}
|
||||
(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]}
|
||||
[{db :db} chat-id]
|
||||
{:db (mark-chat-all-read db chat-id)
|
||||
:clear-message-notifications [[chat-id]
|
||||
(get-in db [:multiaccount :remote-push-notifications-enabled?])]
|
||||
::json-rpc/call [{:method "wakuext_markAllRead"
|
||||
:params [chat-id]
|
||||
:on-success #(re-frame/dispatch [::mark-all-read-successful])}]})
|
||||
{:db (mark-chat-all-read db chat-id)
|
||||
:clear-message-notifications [[chat-id]
|
||||
(get-in db [:multiaccount :remote-push-notifications-enabled?])]
|
||||
::json-rpc/call [{:method "wakuext_markAllRead"
|
||||
:params [chat-id]
|
||||
: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]}
|
||||
[{db :db} community-id]
|
||||
(let [community-chat-ids (map #(str community-id %)
|
||||
(keys (get-in db [:communities community-id :chats])))]
|
||||
{:clear-message-notifications [community-chat-ids
|
||||
(get-in db [:multiaccount :remote-push-notifications-enabled?])]
|
||||
::json-rpc/call [{:method "wakuext_markAllReadInCommunity"
|
||||
:params [community-id]
|
||||
:on-success #(re-frame/dispatch [::mark-all-read-in-community-successful %])}]}))
|
||||
{:clear-message-notifications [community-chat-ids
|
||||
(get-in db [:multiaccount :remote-push-notifications-enabled?])]
|
||||
::json-rpc/call [{:method "wakuext_markAllReadInCommunity"
|
||||
:params [community-id]
|
||||
: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"
|
||||
{:events [::messages-loaded]}
|
||||
[{db :db} chat-id session-id {:keys [cursor messages]}]
|
||||
(when-not (and (get-in db [:pagination-info chat-id :messages-initialized?])
|
||||
(not= session-id
|
||||
(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
|
||||
{:keys [all-messages new-messages senders contacts]}
|
||||
(reduce (fn [{:keys [all-messages] :as acc}
|
||||
@@ -124,8 +129,11 @@
|
||||
:contacts {}
|
||||
:new-messages []}
|
||||
messages)
|
||||
current-clock-value (get-in db [:pagination-info chat-id :cursor-clock-value])
|
||||
clock-value (when cursor (cursor->clock-value cursor))]
|
||||
current-clock-value (get-in db
|
||||
[:pagination-info chat-id
|
||||
:cursor-clock-value])
|
||||
clock-value (when cursor
|
||||
(cursor->clock-value cursor))]
|
||||
{:dispatch [:chat/add-senders-to-chat-users (vals senders)]
|
||||
:db (-> db
|
||||
(update-in [:pagination-info chat-id :cursor-clock-value]
|
||||
@@ -134,7 +142,9 @@
|
||||
%))
|
||||
|
||||
(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
|
||||
%))
|
||||
(assoc-in [:pagination-info chat-id :loading-messages?] false)
|
||||
@@ -144,7 +154,7 @@
|
||||
(empty? cursor))
|
||||
(update :contacts/contacts merge contacts))})))
|
||||
|
||||
(fx/defn load-more-messages
|
||||
(rf/defn load-more-messages
|
||||
{:events [:chat.ui/load-more-messages]}
|
||||
[{:keys [db]} chat-id first-request]
|
||||
(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 [::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]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(load-more-messages cofx (:current-chat-id db) false))
|
||||
|
||||
(fx/defn load-messages
|
||||
(rf/defn load-messages
|
||||
[{:keys [db now] :as cofx} chat-id]
|
||||
(when-not (get-in db [:pagination-info chat-id :messages-initialized?])
|
||||
(fx/merge cofx
|
||||
{:db (assoc-in db [:pagination-info chat-id :messages-initialized?] now)
|
||||
(rf/merge cofx
|
||||
{: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]}
|
||||
(when-not (get-in cofx [:db :chats chat-id :public?])
|
||||
{:ms 100 :dispatch [:pin-message/load-pin-messages chat-id]})]}
|
||||
|
||||
@@ -1,23 +1,26 @@
|
||||
(ns status-im.chat.models.mentions
|
||||
(:require [clojure.string :as string]
|
||||
[quo.react :as react]
|
||||
[quo.react-native :as rn]
|
||||
[re-frame.core :as re-frame]
|
||||
[status-im.constants :as constants]
|
||||
[status-im.utils.fx :as fx]
|
||||
[status-im.contact.db :as contact.db]
|
||||
[status-im.utils.platform :as platform]
|
||||
[taoensso.timbre :as log]
|
||||
[status-im.utils.utils :as utils]
|
||||
[status-im.multiaccounts.core :as multiaccounts]
|
||||
[status-im.native-module.core :as status]
|
||||
[quo.react-native :as rn]
|
||||
[quo.react :as react]
|
||||
[status-im.multiaccounts.core :as multiaccounts]))
|
||||
[status-im.utils.platform :as platform]
|
||||
[status-im.utils.utils :as utils]
|
||||
[taoensso.timbre :as log]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(def at-sign "@")
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defn re-pos [re s]
|
||||
(loop [res [] s s last-idx 0]
|
||||
(defn re-pos
|
||||
[re s]
|
||||
(loop [res []
|
||||
s s
|
||||
last-idx 0]
|
||||
(if-let [m (.exec re s)]
|
||||
(let [new-idx (.-index m)
|
||||
idx (+ last-idx new-idx)
|
||||
@@ -25,7 +28,8 @@
|
||||
(recur (conj res [idx c]) (subs s (inc new-idx)) (inc idx)))
|
||||
res)))
|
||||
|
||||
(defn check-style-tag [text idxs idx]
|
||||
(defn check-style-tag
|
||||
[text idxs idx]
|
||||
(let [[pos c] (get idxs idx)
|
||||
[pos2 c2] (get idxs (inc idx))
|
||||
[pos3 c3] (get idxs (+ 2 idx))
|
||||
@@ -63,7 +67,8 @@
|
||||
(doall
|
||||
((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)
|
||||
tripple-tilde? (and (= "~" c) (= 3 len))]
|
||||
(cond
|
||||
@@ -87,8 +92,9 @@
|
||||
|
||||
start?
|
||||
{:data (-> data
|
||||
(assoc c {:len len
|
||||
:idx pos})
|
||||
(assoc c
|
||||
{:len len
|
||||
:idx pos})
|
||||
(clear-pending-at-signs pos))
|
||||
:next-idx (+ idx len)}
|
||||
|
||||
@@ -96,7 +102,8 @@
|
||||
{:data data
|
||||
:next-idx (+ idx len)})))
|
||||
|
||||
(defn code-tag-len [idxs idx]
|
||||
(defn code-tag-len
|
||||
[idxs idx]
|
||||
(let [[pos c] (get idxs idx)
|
||||
[pos2 c2] (get idxs (inc idx))
|
||||
[pos3 c3] (get idxs (+ 2 idx))]
|
||||
@@ -147,7 +154,8 @@
|
||||
(string/blank? (subs text (first prev-newlines) (dec pos)))))
|
||||
(recur (-> data
|
||||
(dissoc :newline "*" "_" "~" "`")
|
||||
(assoc ">" {:idx pos})) (inc idx))
|
||||
(assoc ">" {:idx pos}))
|
||||
(inc idx))
|
||||
(recur data (inc idx))))
|
||||
|
||||
at-sign?
|
||||
@@ -155,7 +163,7 @@
|
||||
(inc idx))
|
||||
|
||||
code-tag?
|
||||
(let [len (code-tag-len idxs idx)
|
||||
(let [len (code-tag-len idxs idx)
|
||||
{:keys [data next-idx]}
|
||||
(apply-style-tag data idx pos c len true true)]
|
||||
(recur data next-idx))
|
||||
@@ -167,7 +175,7 @@
|
||||
(apply-style-tag data idx pos c len can-be-start? can-be-end?)]
|
||||
(recur data next-idx))
|
||||
|
||||
:else (recur data (inc idx)))))))))
|
||||
:else (recur data (inc idx)))))))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
@@ -201,7 +209,8 @@
|
||||
:ens-verified ens-verified
|
||||
:public-key public-key})))
|
||||
|
||||
(defn mentionable-contacts [contacts]
|
||||
(defn mentionable-contacts
|
||||
[contacts]
|
||||
(reduce
|
||||
(fn [acc [key contact]]
|
||||
(let [mentionable-contact (add-searchable-phrases-to-contact contact false)]
|
||||
@@ -211,38 +220,49 @@
|
||||
{}
|
||||
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]
|
||||
(let [contact (multiaccounts/contact-by-identity
|
||||
contacts identity)
|
||||
contacts
|
||||
identity)
|
||||
contact (if (string/blank? (:alias contact))
|
||||
(assoc contact :alias
|
||||
(assoc contact
|
||||
:alias
|
||||
(get-in contact [:names :three-words-name]))
|
||||
contact)
|
||||
mentionable-contact (add-searchable-phrases-to-contact
|
||||
contact true)]
|
||||
(if (nil? mentionable-contact) acc
|
||||
(assoc acc identity mentionable-contact))))
|
||||
contact
|
||||
true)]
|
||||
(if (nil? mentionable-contact)
|
||||
acc
|
||||
(assoc acc identity mentionable-contact))))
|
||||
{}
|
||||
(remove #(= my-public-key %) identities)))
|
||||
|
||||
(defn get-mentionable-users [chat all-contacts current-multiaccount community-members]
|
||||
(let [{:keys [name preferred-name public-key]} current-multiaccount
|
||||
(defn get-mentionable-users
|
||||
[chat all-contacts current-multiaccount community-members]
|
||||
(let [{:keys [name preferred-name public-key]} current-multiaccount
|
||||
{:keys [chat-id users contacts chat-type]} chat
|
||||
mentionable-contacts (mentionable-contacts all-contacts)
|
||||
mentionable-users (assoc users public-key {:alias name
|
||||
:name (or preferred-name name)
|
||||
:public-key public-key})]
|
||||
mentionable-contacts (mentionable-contacts all-contacts)
|
||||
mentionable-users (assoc users
|
||||
public-key
|
||||
{:alias name
|
||||
:name (or preferred-name name)
|
||||
:public-key public-key})]
|
||||
(cond
|
||||
(= chat-type constants/private-group-chat-type)
|
||||
(merge mentionable-users
|
||||
(mentionable-contacts-from-identites all-contacts public-key contacts))
|
||||
|
||||
(= chat-type constants/one-to-one-chat-type)
|
||||
(assoc mentionable-users chat-id (get mentionable-contacts chat-id
|
||||
(-> chat-id
|
||||
contact.db/public-key->new-contact
|
||||
contact.db/enrich-contact)))
|
||||
(assoc mentionable-users
|
||||
chat-id
|
||||
(get mentionable-contacts
|
||||
chat-id
|
||||
(-> chat-id
|
||||
contact.db/public-key->new-contact
|
||||
contact.db/enrich-contact)))
|
||||
|
||||
(= chat-type constants/community-chat-type)
|
||||
(mentionable-contacts-from-identites
|
||||
@@ -253,13 +273,14 @@
|
||||
(= chat-type constants/public-chat-type)
|
||||
(merge mentionable-users (select-keys mentionable-contacts (keys mentionable-users)))
|
||||
|
||||
:else mentionable-users)))
|
||||
:else mentionable-users)))
|
||||
|
||||
(def ending-chars "[\\s\\.,;:]")
|
||||
(def ending-chars-regex (re-pattern ending-chars))
|
||||
(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)
|
||||
lcase-alias (string/lower-case alias)
|
||||
regex (re-pattern
|
||||
@@ -272,7 +293,8 @@
|
||||
lcase-text (string/lower-case text)]
|
||||
(re-find regex lcase-text)))
|
||||
|
||||
(defn get-user-suggestions [users searched-text]
|
||||
(defn get-user-suggestions
|
||||
[users searched-text]
|
||||
(reduce
|
||||
(fn [acc [k {:keys [alias name nickname searchable-phrases] :as user}]]
|
||||
(if-let [match
|
||||
@@ -301,9 +323,11 @@
|
||||
(string/lower-case name)
|
||||
searched-text)
|
||||
name))]
|
||||
(assoc acc k (assoc user
|
||||
:match match
|
||||
:searched-text searched-text))
|
||||
(assoc acc
|
||||
k
|
||||
(assoc user
|
||||
:match match
|
||||
:searched-text searched-text))
|
||||
acc))
|
||||
{}
|
||||
users))
|
||||
@@ -324,21 +348,25 @@
|
||||
text))
|
||||
user-suggestions (get-user-suggestions users searched-text)
|
||||
user-suggestions-cnt (count user-suggestions)]
|
||||
(cond (zero? user-suggestions-cnt)
|
||||
nil
|
||||
(cond
|
||||
(zero? user-suggestions-cnt)
|
||||
nil
|
||||
|
||||
(and (= 1 user-suggestions-cnt)
|
||||
(mentioned? (second (first user-suggestions))
|
||||
(subs text (inc mention-key-idx))))
|
||||
(second (first user-suggestions))
|
||||
(and (= 1 user-suggestions-cnt)
|
||||
(mentioned? (second (first user-suggestions))
|
||||
(subs text (inc mention-key-idx))))
|
||||
(second (first user-suggestions))
|
||||
|
||||
(> user-suggestions-cnt 1)
|
||||
(let [word-len (count word)
|
||||
text-len (count text)
|
||||
next-word-start (+ next-word-idx word-len)]
|
||||
(when (> text-len next-word-start)
|
||||
(match-mention text users mention-key-idx
|
||||
next-word-start new-words))))))))
|
||||
(> user-suggestions-cnt 1)
|
||||
(let [word-len (count word)
|
||||
text-len (count text)
|
||||
next-word-start (+ next-word-idx word-len)]
|
||||
(when (> text-len next-word-start)
|
||||
(match-mention text
|
||||
users
|
||||
mention-key-idx
|
||||
next-word-start
|
||||
new-words))))))))
|
||||
|
||||
(defn replace-mentions
|
||||
([text users]
|
||||
@@ -360,18 +388,25 @@
|
||||
(let [new-text (string/join
|
||||
[(subs text 0 (inc mention-key-idx))
|
||||
public-key
|
||||
(subs text (+ (inc mention-key-idx)
|
||||
(count match)))])]
|
||||
(recur new-text users (rest idxs)
|
||||
(subs text
|
||||
(+ (inc mention-key-idx)
|
||||
(count match)))])]
|
||||
(recur new-text
|
||||
users
|
||||
(rest idxs)
|
||||
(+ 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)
|
||||
chat (get-in db [:chats current-chat-id])
|
||||
all-contacts (:contacts/contacts db)
|
||||
current-multiaccount (:multiaccount db)
|
||||
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)))
|
||||
|
||||
(defn get-at-sign-idxs
|
||||
@@ -396,7 +431,7 @@
|
||||
{:from idx
|
||||
:checked? false})
|
||||
new-idxs)
|
||||
(let [diff (- new-text-len old-text-len)
|
||||
(let [diff (- new-text-len old-text-len)
|
||||
{:keys [state added?]}
|
||||
(->> at-idxs
|
||||
(keep (fn [{:keys [from to] :as entry}]
|
||||
@@ -406,7 +441,7 @@
|
||||
(>= from old-end)
|
||||
(assoc entry
|
||||
:from (+ from diff)
|
||||
:to (+ to diff))
|
||||
:to (+ to diff))
|
||||
|
||||
;; starts and end before change
|
||||
(and
|
||||
@@ -431,20 +466,22 @@
|
||||
(> from last-new-idx)
|
||||
(not added?))
|
||||
{:state (conj
|
||||
(into state (map (fn [idx]
|
||||
{:from idx
|
||||
:checked? false})
|
||||
new-idxs))
|
||||
(into state
|
||||
(map (fn [idx]
|
||||
{:from idx
|
||||
:checked? false})
|
||||
new-idxs))
|
||||
entry)
|
||||
:added? true}
|
||||
(update acc :state conj entry)))
|
||||
{:state []}))]
|
||||
(if added?
|
||||
state
|
||||
(into state (map (fn [idx]
|
||||
{:from idx
|
||||
:checked? false})
|
||||
new-idxs)))))))
|
||||
(into state
|
||||
(map (fn [idx]
|
||||
{:from idx
|
||||
:checked? false})
|
||||
new-idxs)))))))
|
||||
|
||||
(defn check-entry
|
||||
[text {:keys [from checked?] :as entry} mentionable-users]
|
||||
@@ -453,14 +490,14 @@
|
||||
(let [{user-match :match}
|
||||
(match-mention (str text "@") mentionable-users from)]
|
||||
(if user-match
|
||||
{:from from
|
||||
:to (+ from (count user-match))
|
||||
{:from from
|
||||
:to (+ from (count user-match))
|
||||
:checked? true
|
||||
:mention? true}
|
||||
{:from from
|
||||
:to (count text)
|
||||
{:from from
|
||||
:to (count text)
|
||||
:checked? true
|
||||
:mention false}))))
|
||||
:mention false}))))
|
||||
|
||||
(defn check-idx-for-mentions
|
||||
[text idxs mentionable-users]
|
||||
@@ -489,7 +526,7 @@
|
||||
(assoc-in [last-idx :to] (dec (count text)))
|
||||
(assoc-in [last-idx :checked?] false)))))))
|
||||
|
||||
(fx/defn on-text-input
|
||||
(rf/defn on-text-input
|
||||
{:events [::on-text-input]}
|
||||
[{:keys [db]} {:keys [previous-text start end] :as args}]
|
||||
(log/debug "[mentions] on-text-input args" args)
|
||||
@@ -499,31 +536,34 @@
|
||||
(if platform/android?
|
||||
previous-text
|
||||
(subs previous-text start end))
|
||||
chat-id (:current-chat-id db)
|
||||
previous-state (get-in db [:chats/mentions chat-id :mentions])
|
||||
new-state (-> previous-state
|
||||
(merge args)
|
||||
(assoc :previous-text normalized-previous-text))
|
||||
new-at-idxs (calc-at-idxs new-state)
|
||||
new-state (assoc new-state :at-idxs new-at-idxs)]
|
||||
chat-id (:current-chat-id db)
|
||||
previous-state (get-in db [:chats/mentions chat-id :mentions])
|
||||
new-state (-> previous-state
|
||||
(merge args)
|
||||
(assoc :previous-text normalized-previous-text))
|
||||
new-at-idxs (calc-at-idxs new-state)
|
||||
new-state (assoc new-state :at-idxs new-at-idxs)]
|
||||
(log/debug "[mentions] on-text-input state" 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
|
||||
[[:text text]]
|
||||
(let [idx-cnt (count idxs)
|
||||
(let [idx-cnt (count idxs)
|
||||
last-from (get-in idxs [(dec idx-cnt) :from])]
|
||||
(reduce
|
||||
(fn [acc {:keys [from to next-at-idx mention?]}]
|
||||
(cond
|
||||
(and mention? next-at-idx)
|
||||
(into acc [[:mention (subs text from (inc to))]
|
||||
[:text (subs text (inc to) next-at-idx)]])
|
||||
(into acc
|
||||
[[:mention (subs text from (inc to))]
|
||||
[:text (subs text (inc to) next-at-idx)]])
|
||||
|
||||
(and mention? (= last-from from))
|
||||
(into acc [[:mention (subs text from (inc to))]
|
||||
[:text (subs text (inc to))]])
|
||||
(into acc
|
||||
[[:mention (subs text from (inc to))]
|
||||
[:text (subs text (inc to))]])
|
||||
|
||||
:else
|
||||
(conj acc [:text (subs text from (inc to))])))
|
||||
@@ -533,79 +573,82 @@
|
||||
[[:text (subs text 0 first-from)]]))
|
||||
idxs))))
|
||||
|
||||
(fx/defn recheck-at-idxs
|
||||
(rf/defn recheck-at-idxs
|
||||
[{:keys [db]} mentionable-users]
|
||||
(let [chat-id (:current-chat-id db)
|
||||
text (get-in db [:chat/inputs chat-id :input-text])
|
||||
state (get-in db [:chats/mentions chat-id :mentions])
|
||||
new-at-idxs (check-idx-for-mentions
|
||||
text
|
||||
(:at-idxs state)
|
||||
mentionable-users)
|
||||
(let [chat-id (:current-chat-id db)
|
||||
text (get-in db [:chat/inputs chat-id :input-text])
|
||||
state (get-in db [:chats/mentions chat-id :mentions])
|
||||
new-at-idxs (check-idx-for-mentions
|
||||
text
|
||||
(:at-idxs state)
|
||||
mentionable-users)
|
||||
calculated-input (calculate-input text new-at-idxs)]
|
||||
(log/debug "[mentions] new-at-idxs" new-at-idxs calculated-input)
|
||||
{:db (-> db
|
||||
(update-in
|
||||
[:chats/mentions chat-id :mentions]
|
||||
assoc
|
||||
:at-idxs new-at-idxs)
|
||||
:at-idxs
|
||||
new-at-idxs)
|
||||
(assoc-in [:chat/inputs-with-mentions chat-id] calculated-input))}))
|
||||
|
||||
(fx/defn calculate-suggestions
|
||||
(rf/defn calculate-suggestions
|
||||
{:events [::calculate-suggestions]}
|
||||
[{:keys [db]} mentionable-users]
|
||||
(let [chat-id (:current-chat-id db)
|
||||
text (get-in db [:chat/inputs chat-id :input-text])
|
||||
(let [chat-id (:current-chat-id db)
|
||||
text (get-in db [:chat/inputs chat-id :input-text])
|
||||
{:keys [new-text at-idxs start end] :as state}
|
||||
(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"
|
||||
"state" state)
|
||||
"state"
|
||||
state)
|
||||
(if-not (seq at-idxs)
|
||||
{:db (-> db
|
||||
(assoc-in [:chats/mention-suggestions chat-id] nil)
|
||||
(assoc-in [:chats/mentions chat-id :mentions :at-idxs] nil)
|
||||
(assoc-in [:chat/inputs-with-mentions chat-id] [[:text text]]))}
|
||||
(let [new-at-idxs (check-idx-for-mentions
|
||||
text
|
||||
at-idxs
|
||||
mentionable-users)
|
||||
(let [new-at-idxs (check-idx-for-mentions
|
||||
text
|
||||
at-idxs
|
||||
mentionable-users)
|
||||
calculated-input (calculate-input text new-at-idxs)
|
||||
addition? (<= start end)
|
||||
end (if addition?
|
||||
(+ start (count new-text))
|
||||
start)
|
||||
at-sign-idx (string/last-index-of text at-sign start)
|
||||
searched-text (string/lower-case (subs text (inc at-sign-idx) end))
|
||||
addition? (<= start end)
|
||||
end (if addition?
|
||||
(+ start (count new-text))
|
||||
start)
|
||||
at-sign-idx (string/last-index-of text at-sign start)
|
||||
searched-text (string/lower-case (subs text (inc at-sign-idx) end))
|
||||
mentions
|
||||
(when (and (not (> at-sign-idx start))
|
||||
(not (> (- end at-sign-idx) 100)))
|
||||
(get-user-suggestions mentionable-users searched-text))]
|
||||
(log/debug "[mentions] mention check"
|
||||
"addition" addition?
|
||||
"at-sign-idx" at-sign-idx
|
||||
"start" start
|
||||
"end" end
|
||||
"addition" addition?
|
||||
"at-sign-idx" at-sign-idx
|
||||
"start" start
|
||||
"end" end
|
||||
"searched-text" (pr-str searched-text)
|
||||
"mentions" (count mentions))
|
||||
"mentions" (count mentions))
|
||||
(log/debug "[mentions] new-at-idxs" new-at-idxs calculated-input)
|
||||
{:db (-> db
|
||||
(update-in [:chats/mentions chat-id :mentions]
|
||||
assoc
|
||||
:at-sign-idx at-sign-idx
|
||||
:at-idxs new-at-idxs
|
||||
:at-idxs new-at-idxs
|
||||
:mention-end end)
|
||||
(assoc-in [:chat/inputs-with-mentions chat-id] calculated-input)
|
||||
(assoc-in [:chats/mention-suggestions chat-id] mentions))}))))
|
||||
|
||||
(defn new-input-text-with-mention
|
||||
[{:keys [db]} {:keys [name]}]
|
||||
(let [chat-id (:current-chat-id db)
|
||||
text (get-in db [:chat/inputs chat-id :input-text])
|
||||
(let [chat-id (:current-chat-id db)
|
||||
text (get-in db [:chat/inputs chat-id :input-text])
|
||||
{:keys [mention-end at-sign-idx]}
|
||||
(get-in db [:chats/mentions chat-id :mentions])]
|
||||
(log/debug "[mentions] clear suggestions"
|
||||
"state" new-input-text-with-mention)
|
||||
"state"
|
||||
new-input-text-with-mention)
|
||||
(string/join
|
||||
[(subs text 0 (inc at-sign-idx))
|
||||
name
|
||||
@@ -616,36 +659,36 @@
|
||||
" "))
|
||||
(subs text mention-end)])))
|
||||
|
||||
(fx/defn clear-suggestions
|
||||
(rf/defn clear-suggestions
|
||||
[{:keys [db]}]
|
||||
(log/debug "[mentions] clear suggestions")
|
||||
(let [chat-id (:current-chat-id db)]
|
||||
{:db (update db :chats/mention-suggestions dissoc chat-id)}))
|
||||
|
||||
(fx/defn clear-mentions
|
||||
(rf/defn clear-mentions
|
||||
[{:keys [db] :as cofx}]
|
||||
(log/debug "[mentions] clear mentions")
|
||||
(let [chat-id (:current-chat-id db)]
|
||||
(fx/merge
|
||||
(rf/merge
|
||||
cofx
|
||||
{:db (-> db
|
||||
(update-in [:chats/mentions chat-id] dissoc :mentions)
|
||||
(update :chat/inputs-with-mentions dissoc chat-id))}
|
||||
(clear-suggestions))))
|
||||
|
||||
(fx/defn clear-cursor
|
||||
(rf/defn clear-cursor
|
||||
{:events [::clear-cursor]}
|
||||
[{:keys [db]}]
|
||||
(log/debug "[mentions] clear cursor")
|
||||
{:db
|
||||
(update db :chats/cursor dissoc (:current-chat-id db))})
|
||||
|
||||
(fx/defn check-selection
|
||||
(rf/defn check-selection
|
||||
{:events [::on-selection-change]}
|
||||
[{:keys [db] :as cofx}
|
||||
{:keys [start end] :as selection}
|
||||
mentionable-users]
|
||||
(let [chat-id (:current-chat-id db)
|
||||
(let [chat-id (:current-chat-id db)
|
||||
{:keys [mention-end at-idxs]}
|
||||
(get-in db [:chats/mentions chat-id :mentions])]
|
||||
(when (seq at-idxs)
|
||||
@@ -655,12 +698,13 @@
|
||||
(<= (dec end) to))
|
||||
idx))
|
||||
at-idxs)
|
||||
(fx/merge
|
||||
(rf/merge
|
||||
cofx
|
||||
{:db (update-in db [:chats/mentions chat-id :mentions]
|
||||
{:db (update-in db
|
||||
[:chats/mentions chat-id :mentions]
|
||||
assoc
|
||||
:start end
|
||||
:end end
|
||||
:start end
|
||||
:end end
|
||||
:new-text "")}
|
||||
(calculate-suggestions mentionable-users))
|
||||
(clear-suggestions cofx)))))
|
||||
@@ -673,26 +717,28 @@
|
||||
(rn/find-node-handle (react/current-ref ref))
|
||||
cursor))))
|
||||
|
||||
(fx/defn reset-text-input-cursor
|
||||
(rf/defn 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
|
||||
"\t" true ; tab
|
||||
"\t" true ; tab
|
||||
"\n" true ; newline
|
||||
"\f" true ; new page
|
||||
"\r" true ; carriage return
|
||||
" " true ; whitespace
|
||||
"," true
|
||||
"." true
|
||||
":" true
|
||||
";" true
|
||||
" " true ; whitespace
|
||||
"," true
|
||||
"." true
|
||||
":" true
|
||||
";" true
|
||||
false))
|
||||
|
||||
(def hex-reg #"[0-9a-f]")
|
||||
|
||||
(defn is-public-key-character? [c]
|
||||
(defn is-public-key-character?
|
||||
[c]
|
||||
(.test hex-reg c))
|
||||
|
||||
(def mention-length 133)
|
||||
@@ -710,7 +756,7 @@
|
||||
current-text
|
||||
current-mention
|
||||
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)]
|
||||
(cond
|
||||
;; It's a valid mention.
|
||||
@@ -720,13 +766,13 @@
|
||||
(and (= current-mention-length mention-length)
|
||||
is-termination-character)
|
||||
{:current-mention-length 0
|
||||
:current-mention ""
|
||||
:current-text character
|
||||
:text (cond-> text
|
||||
(seq current-text)
|
||||
(conj [:text current-text])
|
||||
:always
|
||||
(conj [:mention current-mention]))}
|
||||
:current-mention ""
|
||||
:current-text character
|
||||
:text (cond-> text
|
||||
(seq current-text)
|
||||
(conj [:text current-text])
|
||||
:always
|
||||
(conj [:mention current-mention]))}
|
||||
|
||||
|
||||
;; It's either a pk character, or the `x` in the pk
|
||||
@@ -739,9 +785,9 @@
|
||||
(and (= 2 current-mention-length)
|
||||
(= "x" character)))
|
||||
{:current-mention-length (inc current-mention-length)
|
||||
:current-text current-text
|
||||
:current-mention (str current-mention character)
|
||||
:text text}
|
||||
:current-text current-text
|
||||
:current-mention (str current-mention character)
|
||||
:text text}
|
||||
|
||||
|
||||
;; The beginning of a mention, discard the @ sign
|
||||
@@ -750,29 +796,29 @@
|
||||
|
||||
(= "@" character)
|
||||
{:current-mention-length 1
|
||||
:current-mention ""
|
||||
:current-text current-text
|
||||
:text text}
|
||||
:current-mention ""
|
||||
:current-text current-text
|
||||
:text text}
|
||||
|
||||
;; Not a mention character, but we were following a mention
|
||||
;; discard everything up to know an count as text
|
||||
(and (not is-pk-character)
|
||||
(pos? current-mention-length))
|
||||
{:current-mention-length 0
|
||||
:current-text (str current-text "@" current-mention character)
|
||||
:current-mention ""
|
||||
:text text}
|
||||
:current-text (str current-text "@" current-mention character)
|
||||
:current-mention ""
|
||||
:text text}
|
||||
|
||||
;; Just a normal text character
|
||||
:else
|
||||
{:current-mention-length 0
|
||||
:current-mention ""
|
||||
:current-text (str current-text character)
|
||||
:text text})))
|
||||
:current-mention ""
|
||||
:current-text (str current-text character)
|
||||
:text text})))
|
||||
{:current-mention-length 0
|
||||
:current-text ""
|
||||
:current-mention ""
|
||||
:text []}
|
||||
:current-text ""
|
||||
:current-mention ""
|
||||
:text []}
|
||||
text)]
|
||||
;; Process any remaining mention/text
|
||||
(cond-> text
|
||||
@@ -788,32 +834,32 @@
|
||||
[m]
|
||||
(reduce (fn [{:keys [start end at-idxs at-sign-idx mention-end]} [t text]]
|
||||
(if (= :mention t)
|
||||
(let [new-mention {:checked? true
|
||||
:mention? true
|
||||
:from mention-end
|
||||
:to (+ start (count text))}
|
||||
(let [new-mention {:checked? true
|
||||
:mention? true
|
||||
:from mention-end
|
||||
:to (+ start (count text))}
|
||||
has-previous? (seq at-idxs)]
|
||||
{:new-text (last text)
|
||||
{:new-text (last text)
|
||||
:previous-text ""
|
||||
:start (+ start (count text))
|
||||
:end (+ end (count text))
|
||||
:at-idxs (cond-> at-idxs
|
||||
has-previous?
|
||||
(-> pop
|
||||
(conj (assoc (peek at-idxs) :next-at-idx mention-end)))
|
||||
:always
|
||||
(conj new-mention))
|
||||
:at-sign-idx mention-end
|
||||
:mention-end (+ mention-end (count text))})
|
||||
{:new-text (last text)
|
||||
:start (+ start (count text))
|
||||
:end (+ end (count text))
|
||||
:at-idxs (cond-> at-idxs
|
||||
has-previous?
|
||||
(-> pop
|
||||
(conj (assoc (peek at-idxs) :next-at-idx mention-end)))
|
||||
:always
|
||||
(conj new-mention))
|
||||
:at-sign-idx mention-end
|
||||
:mention-end (+ mention-end (count text))})
|
||||
{:new-text (last text)
|
||||
:previous-text ""
|
||||
:start (+ start (count text))
|
||||
:end (+ end (count text))
|
||||
:at-idxs at-idxs
|
||||
:at-sign-idx at-sign-idx
|
||||
:mention-end (+ mention-end (count text))}))
|
||||
{:start -1
|
||||
:end -1
|
||||
:at-idxs []
|
||||
:start (+ start (count text))
|
||||
:end (+ end (count text))
|
||||
:at-idxs at-idxs
|
||||
:at-sign-idx at-sign-idx
|
||||
:mention-end (+ mention-end (count text))}))
|
||||
{:start -1
|
||||
:end -1
|
||||
:at-idxs []
|
||||
:mention-end 0}
|
||||
m))
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
(:require [clojure.string :as string]
|
||||
[re-frame.core :as re-frame]
|
||||
[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.mentions :as mentions]
|
||||
[status-im.chat.models.message-list :as message-list]
|
||||
@@ -11,11 +10,12 @@
|
||||
[status-im.ethereum.json-rpc :as json-rpc]
|
||||
[status-im.transport.message.protocol :as protocol]
|
||||
[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.platform :as platform]
|
||||
[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?
|
||||
[db chat-id message-id]
|
||||
@@ -25,7 +25,8 @@
|
||||
[db chat-id 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
|
||||
(update-in [:db :messages chat-id] assoc message-id 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
|
||||
(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]}
|
||||
[{:keys [db]} messages]
|
||||
(reduce (fn [acc {:keys [chat-id alias name identicon from]}]
|
||||
(let [alias (if (string/blank? alias)
|
||||
(gfycat/generate-gfy from)
|
||||
alias)]
|
||||
(update-in acc [:db :chats chat-id :users] assoc
|
||||
(update-in acc
|
||||
[:db :chats chat-id :users]
|
||||
assoc
|
||||
from
|
||||
(mentions/add-searchable-phrases
|
||||
{:alias alias
|
||||
@@ -54,7 +57,8 @@
|
||||
{:db db}
|
||||
messages))
|
||||
|
||||
(defn timeline-message? [db chat-id]
|
||||
(defn timeline-message?
|
||||
[db chat-id]
|
||||
(and
|
||||
(get-in db [:pagination-info constants/timeline-chat-id :messages-initialized?])
|
||||
(or
|
||||
@@ -62,11 +66,13 @@
|
||||
(when-let [pub-key (get-in db [:chats chat-id :profile-public-key])]
|
||||
(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)
|
||||
(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}
|
||||
(data-store.messages/<-rpc (types/js->clj message-js))]
|
||||
(if (message-loaded? db chat-id message-id)
|
||||
@@ -82,8 +88,9 @@
|
||||
(update-in [:db :message-lists chat-id] message-list/add message)
|
||||
|
||||
(or (not cursor-clock-value) (< clock-value cursor-clock-value))
|
||||
(update-in [:db :pagination-info chat-id] assoc
|
||||
:cursor (chat.loading/clock-value->cursor clock-value)
|
||||
(update-in [:db :pagination-info chat-id]
|
||||
assoc
|
||||
:cursor (chat.loading/clock-value->cursor clock-value)
|
||||
:cursor-clock-value clock-value)
|
||||
|
||||
;;conj sender for add-sender-to-chat-users
|
||||
@@ -94,11 +101,12 @@
|
||||
;;TODO this is expensive
|
||||
(hide-message chat-id replace)))))
|
||||
|
||||
(defn reduce-js-messages [{:keys [db] :as acc} ^js message-js]
|
||||
(let [chat-id (.-localChatId message-js)
|
||||
clock-value (.-clock message-js)
|
||||
message-id (.-id message-js)
|
||||
current-chat-id (:current-chat-id db)
|
||||
(defn reduce-js-messages
|
||||
[{:keys [db] :as acc} ^js message-js]
|
||||
(let [chat-id (.-localChatId message-js)
|
||||
clock-value (.-clock message-js)
|
||||
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])]
|
||||
;;ignore not opened chats and earlier clock
|
||||
(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)
|
||||
;; Not in the current view, set all-loaded to false
|
||||
;; 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,
|
||||
;; we need to drop other messages with (< clock-value cursor-clock-value) from response-js so we don't update
|
||||
;;TODO if we'll offload messages , it will conflict with end reached, so probably if we reached
|
||||
;;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
|
||||
{:db (cond-> (assoc-in db [:pagination-info chat-id :all-loaded?] false)
|
||||
(> clock-value cursor-clock-value)
|
||||
;;TODO cut older messages from messages-list
|
||||
(update-in [:pagination-info chat-id] assoc
|
||||
:cursor (chat.loading/clock-value->cursor clock-value)
|
||||
(update-in [:pagination-info chat-id]
|
||||
assoc
|
||||
:cursor (chat.loading/clock-value->cursor clock-value)
|
||||
:cursor-clock-value clock-value))})
|
||||
acc)))
|
||||
|
||||
(defn receive-many [{:keys [db]} ^js response-js]
|
||||
(let [messages-js ^js (.splice (.-messages response-js) 0 (if platform/low-device? 3 10))
|
||||
(defn receive-many
|
||||
[{:keys [db]} ^js response-js]
|
||||
(let [messages-js ^js (.splice (.-messages response-js) 0 (if platform/low-device? 3 10))
|
||||
{:keys [db senders]}
|
||||
(reduce reduce-js-messages
|
||||
{:db db :chats #{} :senders {} :transactions #{}}
|
||||
messages-js)]
|
||||
;;we want to render new messages as soon as possible
|
||||
;;so we dispatch later all other events which can be handled async
|
||||
{:db db
|
||||
{:db db
|
||||
:utils/dispatch-later
|
||||
(concat [{:ms 20 :dispatch [:process-response response-js]}]
|
||||
(when (and (:current-chat-id db) (= "active" (:app-state db)))
|
||||
@@ -137,7 +149,8 @@
|
||||
(when (seq 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)
|
||||
profile-initialized (get-in db [:pagination-info chat-id :messages-initialized?])
|
||||
timeline-message (timeline-message? db chat-id)
|
||||
@@ -155,60 +168,66 @@
|
||||
(update-in [:message-lists constants/timeline-chat-id] message-list/add message)))
|
||||
db)))
|
||||
|
||||
(fx/defn process-statuses
|
||||
(rf/defn process-statuses
|
||||
{:events [:process-statuses]}
|
||||
[{:keys [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]
|
||||
(when (get-in db [:messages chat-id message-id])
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{:db (assoc-in db
|
||||
[:messages chat-id message-id :outgoing-status]
|
||||
status)})))
|
||||
[:messages chat-id message-id :outgoing-status]
|
||||
status)})))
|
||||
|
||||
(fx/defn update-message-status
|
||||
(rf/defn update-message-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)))
|
||||
|
||||
(fx/defn resend-message
|
||||
(rf/defn resend-message
|
||||
[{:keys [db] :as cofx} chat-id message-id]
|
||||
(fx/merge cofx
|
||||
{::json-rpc/call [{:method "wakuext_reSendChatMessage"
|
||||
:params [message-id]
|
||||
(rf/merge cofx
|
||||
{::json-rpc/call [{:method "wakuext_reSendChatMessage"
|
||||
:params [message-id]
|
||||
: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)))
|
||||
|
||||
(fx/defn send-message
|
||||
(rf/defn send-message
|
||||
[cofx message]
|
||||
(protocol/send-chat-messages cofx [message]))
|
||||
|
||||
(fx/defn send-messages
|
||||
(rf/defn send-messages
|
||||
[cofx messages]
|
||||
(protocol/send-chat-messages cofx messages))
|
||||
|
||||
(fx/defn handle-removed-messages
|
||||
(rf/defn handle-removed-messages
|
||||
{:events [::handle-removed-messages]}
|
||||
[{:keys [db] :as cofx} removed-messages]
|
||||
(let [mark-as-deleted-fx (->> removed-messages
|
||||
(map #(assoc % :message-id (:messageId %)))
|
||||
(group-by :chatId)
|
||||
(mapv (fn [[chat-id messages]] (delete-message/delete-messages-localy messages chat-id))))
|
||||
mark-as-seen-fx (mapv (fn [removed-message]
|
||||
(let [chat-id (:chatId removed-message)
|
||||
message-id (:messageId removed-message)]
|
||||
(data-store.messages/mark-messages-seen chat-id
|
||||
[message-id]
|
||||
#(re-frame/dispatch [:chat/decrease-unviewed-count chat-id %3]))))
|
||||
removed-messages)
|
||||
(mapv (fn [[chat-id messages]]
|
||||
(delete-message/delete-messages-localy messages chat-id))))
|
||||
mark-as-seen-fx (mapv
|
||||
(fn [removed-message]
|
||||
(let [chat-id (:chatId removed-message)
|
||||
message-id (:messageId removed-message)]
|
||||
(data-store.messages/mark-messages-seen chat-id
|
||||
[message-id]
|
||||
#(re-frame/dispatch
|
||||
[:chat/decrease-unviewed-count
|
||||
chat-id %3]))))
|
||||
removed-messages)
|
||||
remove-messages-fx (fn [{:keys [db]}]
|
||||
{:dispatch [:activity-center.notifications/fetch-unread-count]})]
|
||||
(apply fx/merge cofx (-> mark-as-deleted-fx
|
||||
(concat mark-as-seen-fx)
|
||||
(conj remove-messages-fx)))))
|
||||
(apply rf/merge
|
||||
cofx
|
||||
(-> mark-as-deleted-fx
|
||||
(concat mark-as-seen-fx)
|
||||
(conj remove-messages-fx)))))
|
||||
|
||||
(comment
|
||||
(handle-removed-messages
|
||||
@@ -218,15 +237,20 @@
|
||||
:m4 {:chat-id :c2 :message-id :m4}}}}}
|
||||
[:m1 :m3]))
|
||||
|
||||
(defn remove-cleared-message [messages cleared-at]
|
||||
(into {} (remove #(let [message-clock (:clock-value (second %))]
|
||||
(<= message-clock cleared-at))
|
||||
messages)))
|
||||
(defn remove-cleared-message
|
||||
[messages cleared-at]
|
||||
(into {}
|
||||
(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]}
|
||||
[{:keys [db]} cleared-histories]
|
||||
{: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
|
||||
cleared-histories)})
|
||||
|
||||
@@ -2,22 +2,25 @@
|
||||
(:require ["functional-red-black-tree" :as rb-tree]
|
||||
[status-im.constants :as constants]
|
||||
[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
|
||||
(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)))
|
||||
|
||||
(defn prepare-message [{:keys [message-id
|
||||
clock-value
|
||||
message-type
|
||||
from
|
||||
outgoing
|
||||
whisper-timestamp
|
||||
deleted-for-me?]}]
|
||||
(defn prepare-message
|
||||
[{:keys [message-id
|
||||
clock-value
|
||||
message-type
|
||||
from
|
||||
outgoing
|
||||
whisper-timestamp
|
||||
deleted-for-me?]}]
|
||||
(-> {:whisper-timestamp whisper-timestamp
|
||||
:from from
|
||||
: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
|
||||
in a group."
|
||||
[{:keys [system-message?
|
||||
one-to-one? outgoing] :as current-message}
|
||||
one-to-one? outgoing]
|
||||
:as current-message}
|
||||
{:keys [outgoing-seen?] :as previous-message}
|
||||
next-message]
|
||||
(let [last-in-group? (or (nil? next-message)
|
||||
@@ -87,7 +91,7 @@
|
||||
(not system-message?)
|
||||
(not outgoing)
|
||||
(not one-to-one?))
|
||||
:display-photo? (display-photo? current-message))))
|
||||
:display-photo? (display-photo? current-message))))
|
||||
|
||||
(defn update-next-message
|
||||
"Update next message in the list, we set :first? to false, and check if it
|
||||
@@ -95,26 +99,28 @@
|
||||
[current-message next-message]
|
||||
(assoc
|
||||
next-message
|
||||
:first? false
|
||||
:first? false
|
||||
:first-outgoing? (and
|
||||
(not (:first-outgoing? current-message))
|
||||
(:first-outgoing? next-message))
|
||||
:outgoing-seen? (:outgoing-seen? current-message)
|
||||
:outgoing-seen? (:outgoing-seen? current-message)
|
||||
:first-in-group?
|
||||
(not (same-group? current-message next-message))))
|
||||
|
||||
(defn update-previous-message
|
||||
"If this is a new group, we mark the previous as the last one in the group"
|
||||
[current-message {:keys [one-to-one?
|
||||
system-message?
|
||||
outgoing] :as previous-message}]
|
||||
[current-message
|
||||
{:keys [one-to-one?
|
||||
system-message?
|
||||
outgoing]
|
||||
:as previous-message}]
|
||||
(let [last-in-group? (not (same-group? current-message previous-message))]
|
||||
(assoc previous-message
|
||||
:display-username? (and last-in-group?
|
||||
(not system-message?)
|
||||
(not outgoing)
|
||||
(not one-to-one?))
|
||||
:last-in-group? last-in-group?)))
|
||||
:last-in-group? last-in-group?)))
|
||||
|
||||
(defn get-prev-element
|
||||
"Get previous item in the iterator, and wind it back to the initial state"
|
||||
@@ -135,11 +141,11 @@
|
||||
(defn update-message
|
||||
"Update the message and siblings with positional info"
|
||||
[^js tree message]
|
||||
(let [^js iter (.find tree message)
|
||||
^js previous-message (when (.-hasPrev iter)
|
||||
(get-prev-element iter))
|
||||
^js next-message (when (.-hasNext iter)
|
||||
(get-next-element iter))
|
||||
(let [^js iter (.find tree message)
|
||||
^js previous-message (when (.-hasPrev iter)
|
||||
(get-prev-element iter))
|
||||
^js next-message (when (.-hasNext iter)
|
||||
(get-next-element iter))
|
||||
^js message-with-pos-data (add-group-info message previous-message next-message)]
|
||||
(cond-> (.update iter message-with-pos-data)
|
||||
next-message
|
||||
@@ -157,9 +163,9 @@
|
||||
(let [iter (.find tree prepared-message)]
|
||||
(if (not iter)
|
||||
tree
|
||||
(let [^js new-tree (.remove iter)
|
||||
^js next-message (when (.-hasNext iter)
|
||||
(get-next-element iter))]
|
||||
(let [^js new-tree (.remove iter)
|
||||
^js next-message (when (.-hasNext iter)
|
||||
(get-next-element iter))]
|
||||
(if (not next-message)
|
||||
new-tree
|
||||
(update-message new-tree next-message))))))
|
||||
@@ -173,21 +179,26 @@
|
||||
(let [^js tree (.insert old-message-list prepared-message 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)))
|
||||
|
||||
(defn add-many [message-list messages]
|
||||
(defn add-many
|
||||
[message-list messages]
|
||||
(reduce add
|
||||
message-list
|
||||
messages))
|
||||
|
||||
(defn ->seq [^js message-list]
|
||||
(defn ->seq
|
||||
[^js message-list]
|
||||
(if 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
|
||||
(fx/defn rebuild-message-list
|
||||
;; NOTE(performance): this is too expensive, probably we could mark message somehow and just hide it in
|
||||
;; the UI
|
||||
(rf/defn rebuild-message-list
|
||||
[{:keys [db]} chat-id]
|
||||
{:db (assoc-in db [:message-lists chat-id]
|
||||
(add-many nil (vals (get-in db [:messages chat-id]))))})
|
||||
{:db (assoc-in db
|
||||
[:message-lists chat-id]
|
||||
(add-many nil (vals (get-in db [:messages chat-id]))))})
|
||||
|
||||
@@ -1,28 +1,36 @@
|
||||
(ns status-im.chat.models.reactions
|
||||
(:require [status-im.constants :as constants]
|
||||
[re-frame.core :as re-frame]
|
||||
[status-im.utils.fx :as fx]
|
||||
[taoensso.timbre :as log]
|
||||
(:require [re-frame.core :as re-frame]
|
||||
[status-im.constants :as constants]
|
||||
[status-im.data-store.reactions :as data-store.reactions]
|
||||
[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
|
||||
;; retraction will always come after the reaction so there shouldn't be a conflict
|
||||
(if retracted
|
||||
(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)))
|
||||
|
||||
(defn process-reactions [chats]
|
||||
(defn process-reactions
|
||||
[chats]
|
||||
(fn [reactions new-reactions]
|
||||
;; TODO(Ferossgp): handling own reaction in subscription could be expensive,
|
||||
;; for better performance we can here separate own reaction into 2 maps
|
||||
(reduce
|
||||
(fn [acc {:keys [chat-id message-id emoji-id emoji-reaction-id retracted]
|
||||
:as reaction}]
|
||||
(fn [acc
|
||||
{: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)
|
||||
(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
|
||||
new-reactions)))
|
||||
|
||||
@@ -32,12 +40,12 @@
|
||||
(get-in db [:chats chat-id])]
|
||||
(>= deleted-at-clock-value clock-value)))
|
||||
|
||||
(fx/defn receive-signal
|
||||
(rf/defn receive-signal
|
||||
[{:keys [db] :as cofx} reactions]
|
||||
(let [reactions (filter (partial earlier-than-deleted-at? cofx) reactions)]
|
||||
{:db (update db :reactions (process-reactions (:chats db)) reactions)}))
|
||||
|
||||
(fx/defn load-more-reactions
|
||||
(rf/defn load-more-reactions
|
||||
{:events [:load-more-reactions]}
|
||||
[{:keys [db]} cursor chat-id]
|
||||
(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 %])
|
||||
#(log/error "failed loading reactions" chat-id %))))
|
||||
|
||||
(fx/defn reactions-loaded
|
||||
(rf/defn reactions-loaded
|
||||
{:events [::reactions-loaded]}
|
||||
[{db :db}
|
||||
chat-id
|
||||
@@ -64,33 +72,36 @@
|
||||
;; Send reactions
|
||||
|
||||
|
||||
(fx/defn send-emoji-reaction
|
||||
(rf/defn send-emoji-reaction
|
||||
{:events [::send-emoji-reaction]}
|
||||
[{{:keys [current-chat-id]} :db :as cofx} reaction]
|
||||
(message.protocol/send-reaction cofx
|
||||
(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]}
|
||||
[{{:keys [current-chat-id]} :db :as cofx} reaction]
|
||||
(message.protocol/send-retract-reaction cofx
|
||||
(update reaction :chat-id #(or % current-chat-id))))
|
||||
|
||||
(fx/defn receive-one
|
||||
(rf/defn receive-one
|
||||
{:events [::receive-one]}
|
||||
[{:keys [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
|
||||
(fn [acc [emoji-id reactions]]
|
||||
(if (pos? (count reactions))
|
||||
(let [own (first (filter (fn [[_ {:keys [from]}]]
|
||||
(= from current-public-key)) reactions))]
|
||||
(conj acc {:emoji-id emoji-id
|
||||
:own (boolean (seq own))
|
||||
:emoji-reaction-id (:emoji-reaction-id (second own))
|
||||
:quantity (count reactions)}))
|
||||
(= from current-public-key))
|
||||
reactions))]
|
||||
(conj acc
|
||||
{:emoji-id emoji-id
|
||||
:own (boolean (seq own))
|
||||
:emoji-reaction-id (:emoji-reaction-id (second own))
|
||||
:quantity (count reactions)}))
|
||||
acc))
|
||||
[]
|
||||
reactions))
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
;;this ns is needed because of cycled deps
|
||||
(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.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]}
|
||||
[{:keys [db] :as cofx} chat-id message-id]
|
||||
(let [message (get-in db [:messages chat-id message-id])]
|
||||
(fx/merge
|
||||
(rf/merge
|
||||
cofx
|
||||
(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))))
|
||||
|
||||
@@ -2,44 +2,44 @@
|
||||
(:require [re-frame.core :as re-frame]
|
||||
[status-im.ethereum.core :as ethereum]
|
||||
[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]}
|
||||
[{:keys [db]} message]
|
||||
{:db (assoc db
|
||||
:commands/select-account
|
||||
{:message message
|
||||
:from (ethereum/get-default-account (:multiaccount/accounts db))})
|
||||
{:db (assoc db
|
||||
:commands/select-account
|
||||
{:message message
|
||||
:from (ethereum/get-default-account (:multiaccount/accounts db))})
|
||||
:show-select-acc-sheet nil})
|
||||
|
||||
(fx/defn set-selected-account
|
||||
(rf/defn set-selected-account
|
||||
{:events [::set-selected-account]}
|
||||
[{:keys [db]} _ account]
|
||||
{:db (-> (assoc-in db [:commands/select-account :from] account)
|
||||
(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]}
|
||||
[{:keys [db]} message-id address]
|
||||
{:db (dissoc db :commands/select-account)
|
||||
::json-rpc/call [{:method "wakuext_acceptRequestAddressForTransaction"
|
||||
:params [message-id address]
|
||||
{:db (dissoc db :commands/select-account)
|
||||
::json-rpc/call [{:method "wakuext_acceptRequestAddressForTransaction"
|
||||
:params [message-id address]
|
||||
: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]}
|
||||
[_ message-id]
|
||||
{::json-rpc/call [{:method "wakuext_declineRequestAddressForTransaction"
|
||||
:params [message-id]
|
||||
{::json-rpc/call [{:method "wakuext_declineRequestAddressForTransaction"
|
||||
:params [message-id]
|
||||
: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]}
|
||||
[cofx message-id]
|
||||
{::json-rpc/call [{:method "wakuext_declineRequestTransaction"
|
||||
:params [message-id]
|
||||
{::json-rpc/call [{:method "wakuext_declineRequestTransaction"
|
||||
:params [message-id]
|
||||
:js-response true
|
||||
:on-success #(re-frame/dispatch [:transport/message-sent %])}]})
|
||||
:on-success #(re-frame/dispatch [:transport/message-sent %])}]})
|
||||
|
||||
+201
-155
@@ -1,25 +1,24 @@
|
||||
(ns status-im.communities.core
|
||||
(:require
|
||||
[re-frame.core :as re-frame]
|
||||
[clojure.walk :as walk]
|
||||
[clojure.string :as string]
|
||||
[clojure.set :as clojure.set]
|
||||
[taoensso.timbre :as log]
|
||||
[status-im.async-storage.core :as async-storage]
|
||||
[status-im.utils.fx :as fx]
|
||||
[status-im.constants :as constants]
|
||||
[status-im.bottom-sheet.core :as bottom-sheet]
|
||||
[status-im.utils.universal-links.core :as universal-links]
|
||||
[status-im.ethereum.json-rpc :as json-rpc]
|
||||
[quo.design-system.colors :as colors]
|
||||
[status-im2.navigation.events :as navigation]
|
||||
[status-im.utils.handlers :refer [>evt]]
|
||||
[status-im.ui.components.emoji-thumbnail.styles :as emoji-thumbnail-styles]
|
||||
[status-im2.contexts.activity-center.events :as activity-center]))
|
||||
(:require [clojure.set :as clojure.set]
|
||||
[clojure.string :as string]
|
||||
[clojure.walk :as walk]
|
||||
[quo.design-system.colors :as colors]
|
||||
[re-frame.core :as re-frame]
|
||||
[status-im.async-storage.core :as async-storage]
|
||||
[status-im.bottom-sheet.core :as bottom-sheet]
|
||||
[status-im.constants :as constants]
|
||||
[status-im.ethereum.json-rpc :as json-rpc]
|
||||
[status-im.ui.components.emoji-thumbnail.styles :as emoji-thumbnail-styles]
|
||||
[status-im.utils.universal-links.core :as universal-links]
|
||||
[status-im2.contexts.activity-center.events :as activity-center]
|
||||
[status-im2.navigation.events :as navigation]
|
||||
[taoensso.timbre :as log]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(def crop-size 1000)
|
||||
|
||||
(defn universal-link [community-id]
|
||||
(defn universal-link
|
||||
[community-id]
|
||||
(str (:external universal-links/domains)
|
||||
"/c/"
|
||||
community-id))
|
||||
@@ -28,18 +27,22 @@
|
||||
[{:name "Status"
|
||||
:id constants/status-community-id}])
|
||||
|
||||
(defn <-request-to-join-community-rpc [r]
|
||||
(clojure.set/rename-keys r {:communityId :community-id
|
||||
:publicKey :public-key
|
||||
:chatId :chat-id}))
|
||||
(defn <-request-to-join-community-rpc
|
||||
[r]
|
||||
(clojure.set/rename-keys r
|
||||
{: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]
|
||||
(assoc acc (:id r) (<-request-to-join-community-rpc r)))
|
||||
{}
|
||||
requests))
|
||||
|
||||
(defn <-chats-rpc [chats]
|
||||
(defn <-chats-rpc
|
||||
[chats]
|
||||
(reduce-kv (fn [acc k v]
|
||||
(assoc acc
|
||||
(name k)
|
||||
@@ -50,7 +53,8 @@
|
||||
{}
|
||||
chats))
|
||||
|
||||
(defn <-categories-rpc [categ]
|
||||
(defn <-categories-rpc
|
||||
[categ]
|
||||
(reduce-kv (fn [acc k v]
|
||||
(assoc acc
|
||||
(name k)
|
||||
@@ -58,7 +62,8 @@
|
||||
{}
|
||||
categ))
|
||||
|
||||
(defn <-rpc [c]
|
||||
(defn <-rpc
|
||||
[c]
|
||||
(-> c
|
||||
(clojure.set/rename-keys {:canRequestAccess :can-request-access?
|
||||
:canManageUsers :can-manage-users?
|
||||
@@ -70,25 +75,28 @@
|
||||
(update :chats <-chats-rpc)
|
||||
(update :categories <-categories-rpc)))
|
||||
|
||||
(defn fetch-community-id-input [{:keys [db]}]
|
||||
(defn fetch-community-id-input
|
||||
[{:keys [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)]
|
||||
{: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]
|
||||
(update db :chats dissoc chat-id))
|
||||
db
|
||||
chat-ids)})
|
||||
|
||||
(fx/defn handle-community
|
||||
(rf/defn handle-community
|
||||
[{:keys [db]} {:keys [id] :as community}]
|
||||
(when id
|
||||
{:db (assoc-in db [:communities id] (<-rpc community))}))
|
||||
|
||||
(fx/defn handle-communities
|
||||
(rf/defn handle-communities
|
||||
{:events [::fetched]}
|
||||
[{:keys [db]} communities]
|
||||
{:db (reduce (fn [db {:keys [id] :as community}]
|
||||
@@ -96,34 +104,36 @@
|
||||
db
|
||||
communities)})
|
||||
|
||||
(fx/defn handle-response [_ response-js]
|
||||
(rf/defn handle-response
|
||||
[_ response-js]
|
||||
{:dispatch [:sanitize-messages-and-process-response response-js]})
|
||||
|
||||
(fx/defn left
|
||||
(rf/defn left
|
||||
{:events [::left]}
|
||||
[cofx response-js]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(handle-response cofx response-js)
|
||||
(navigation/pop-to-root-tab :chat-stack)
|
||||
(activity-center/notifications-fetch-unread-count)))
|
||||
|
||||
(fx/defn joined
|
||||
(rf/defn joined
|
||||
{:events [::joined ::requested-to-join]}
|
||||
[cofx response-js]
|
||||
(handle-response cofx response-js))
|
||||
|
||||
(fx/defn export
|
||||
(rf/defn export
|
||||
{:events [::export-pressed]}
|
||||
[cofx community-id]
|
||||
{::json-rpc/call [{:method "wakuext_exportCommunity"
|
||||
:params [community-id]
|
||||
:on-success #(re-frame/dispatch [:show-popover {:view :export-community
|
||||
:community-key %}])
|
||||
:on-success #(re-frame/dispatch [:show-popover
|
||||
{:view :export-community
|
||||
:community-key %}])
|
||||
:on-error #(do
|
||||
(log/error "failed to export community" community-id %)
|
||||
(re-frame/dispatch [::failed-to-export %]))}]})
|
||||
|
||||
(fx/defn import-community
|
||||
(rf/defn import-community
|
||||
{:events [::import]}
|
||||
[cofx community-key]
|
||||
{::json-rpc/call [{:method "wakuext_importCommunity"
|
||||
@@ -134,7 +144,7 @@
|
||||
(log/error "failed to import community" %)
|
||||
(re-frame/dispatch [::failed-to-import %]))}]})
|
||||
|
||||
(fx/defn join
|
||||
(rf/defn join
|
||||
{:events [:communities/join]}
|
||||
[cofx community-id]
|
||||
{::json-rpc/call [{:method "wakuext_joinCommunity"
|
||||
@@ -145,7 +155,7 @@
|
||||
(log/error "failed to join community" community-id %)
|
||||
(re-frame/dispatch [::failed-to-join %]))}]})
|
||||
|
||||
(fx/defn request-to-join
|
||||
(rf/defn request-to-join
|
||||
{:events [::request-to-join]}
|
||||
[cofx community-id]
|
||||
{::json-rpc/call [{:method "wakuext_requestToJoinCommunity"
|
||||
@@ -156,7 +166,7 @@
|
||||
(log/error "failed to request to join community" community-id %)
|
||||
(re-frame/dispatch [::failed-to-request-to-join %]))}]})
|
||||
|
||||
(fx/defn leave
|
||||
(rf/defn leave
|
||||
{:events [:communities/leave]}
|
||||
[{:keys [db]} community-id]
|
||||
(let [community-chat-ids (map #(str community-id %)
|
||||
@@ -168,10 +178,13 @@
|
||||
:js-response true
|
||||
:on-success #(re-frame/dispatch [::left %])
|
||||
: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 %]))}]}))
|
||||
|
||||
(fx/defn fetch [_]
|
||||
(rf/defn fetch
|
||||
[_]
|
||||
{::json-rpc/call [{:method "wakuext_communities"
|
||||
:params []
|
||||
:on-success #(re-frame/dispatch [::fetched %])
|
||||
@@ -179,7 +192,7 @@
|
||||
(log/error "failed to fetch communities" %)
|
||||
(re-frame/dispatch [::failed-to-fetch %]))}]})
|
||||
|
||||
(fx/defn chat-created
|
||||
(rf/defn chat-created
|
||||
{:events [::chat-created]}
|
||||
[_ community-id user-pk]
|
||||
{::json-rpc/call [{:method "wakuext_sendChatMessage"
|
||||
@@ -193,7 +206,7 @@
|
||||
:on-error
|
||||
#(log/error "failed to send a message" %)}]})
|
||||
|
||||
(fx/defn invite-users
|
||||
(rf/defn invite-users
|
||||
{:events [::invite-people-confirmation-pressed]}
|
||||
[cofx user-pk contacts]
|
||||
(let [community-id (fetch-community-id-input cofx)
|
||||
@@ -209,7 +222,7 @@
|
||||
:on-error #(do
|
||||
(log/error "failed to invite-user community" %)
|
||||
(re-frame/dispatch [::failed-to-invite-people %]))}]})))
|
||||
(fx/defn share-community
|
||||
(rf/defn share-community
|
||||
{:events [::share-community-confirmation-pressed]}
|
||||
[cofx user-pk contacts]
|
||||
(let [community-id (fetch-community-id-input cofx)
|
||||
@@ -226,7 +239,7 @@
|
||||
(log/error "failed to invite-user community" %)
|
||||
(re-frame/dispatch [::failed-to-share-community %]))}]})))
|
||||
|
||||
(fx/defn create
|
||||
(rf/defn create
|
||||
{:events [::create-confirmation-pressed]}
|
||||
[{:keys [db]}]
|
||||
(let [{:keys [name description membership image]} (get db :communities/create)]
|
||||
@@ -248,7 +261,7 @@
|
||||
(log/error "failed to create community" %)
|
||||
(re-frame/dispatch [::failed-to-create-community %]))}]})))
|
||||
|
||||
(fx/defn edit
|
||||
(rf/defn edit
|
||||
{:events [::edit-confirmation-pressed]}
|
||||
[{:keys [db]}]
|
||||
(let [{:keys [id name description membership new-image color]} (get db :communities/create)]
|
||||
@@ -268,10 +281,10 @@
|
||||
(log/error "failed to edit community" %)
|
||||
(re-frame/dispatch [::failed-to-edit-community %]))}]}))
|
||||
|
||||
(fx/defn create-channel
|
||||
(rf/defn create-channel
|
||||
{:events [::create-channel-confirmation-pressed]}
|
||||
[{: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)]
|
||||
{::json-rpc/call [{:method "wakuext_createCommunityChat"
|
||||
:params [community-id
|
||||
@@ -279,7 +292,8 @@
|
||||
:description description
|
||||
:color color
|
||||
:emoji emoji}
|
||||
:permissions {:access constants/community-channel-access-no-membership}}]
|
||||
:permissions {:access
|
||||
constants/community-channel-access-no-membership}}]
|
||||
:js-response true
|
||||
:on-success #(re-frame/dispatch [::community-channel-created %])
|
||||
:on-error #(do
|
||||
@@ -288,10 +302,11 @@
|
||||
|
||||
(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))
|
||||
|
||||
(fx/defn edit-channel
|
||||
(rf/defn edit-channel
|
||||
{:events [::edit-channel-confirmation-pressed]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(let [{:keys [name description color community-id emoji edit-channel-id category-id position]}
|
||||
@@ -305,161 +320,171 @@
|
||||
:emoji emoji}
|
||||
:category_id category-id
|
||||
:position position
|
||||
:permissions {:access constants/community-channel-access-no-membership}}]
|
||||
:permissions {:access
|
||||
constants/community-channel-access-no-membership}}]
|
||||
:js-response true
|
||||
:on-success #(re-frame/dispatch [::community-channel-edited %])
|
||||
:on-error #(do
|
||||
(log/error "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)))
|
||||
|
||||
(defn can-post? [community _ local-chat-id]
|
||||
(defn can-post?
|
||||
[community _ local-chat-id]
|
||||
(let [chat-id (to-community-chat-id local-chat-id)]
|
||||
(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)})
|
||||
|
||||
(fx/defn reset-channel-info [{:keys [db]}]
|
||||
(rf/defn reset-channel-info
|
||||
[{:keys [db]}]
|
||||
{:db (assoc db :communities/create-channel {})})
|
||||
|
||||
(fx/defn invite-people-pressed
|
||||
(rf/defn invite-people-pressed
|
||||
{:events [:communities/invite-people-pressed]}
|
||||
[cofx id]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(reset-community-id-input id)
|
||||
(bottom-sheet/hide-bottom-sheet)
|
||||
(navigation/open-modal :invite-people-community {:invite? true})))
|
||||
|
||||
(fx/defn share-community-pressed
|
||||
(rf/defn share-community-pressed
|
||||
{:events [:communities/share-community-pressed]}
|
||||
[cofx id]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(reset-community-id-input id)
|
||||
(bottom-sheet/hide-bottom-sheet)
|
||||
(navigation/open-modal :invite-people-community {})))
|
||||
|
||||
(fx/defn create-channel-pressed
|
||||
(rf/defn create-channel-pressed
|
||||
{:events [::create-channel-pressed]}
|
||||
[{:keys [db] :as cofx} id]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(reset-community-id-input id)
|
||||
(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})))
|
||||
|
||||
(fx/defn edit-channel-pressed
|
||||
(rf/defn edit-channel-pressed
|
||||
{:events [::edit-channel-pressed]}
|
||||
[{:keys [db] :as cofx} community-id chat-name description color emoji chat-id category-id position]
|
||||
(let [{:keys [color emoji]} (if (string/blank? emoji)
|
||||
(rand-nth emoji-thumbnail-styles/emoji-picker-default-thumbnails)
|
||||
{:color color :emoji emoji})]
|
||||
(fx/merge cofx
|
||||
{:db (assoc db :communities/create-channel {:name chat-name
|
||||
:description description
|
||||
:color color
|
||||
:community-id community-id
|
||||
:emoji emoji
|
||||
:edit-channel-id chat-id
|
||||
:category-id category-id
|
||||
:position position})}
|
||||
(rf/merge cofx
|
||||
{:db (assoc db
|
||||
:communities/create-channel
|
||||
{:name chat-name
|
||||
:description description
|
||||
:color color
|
||||
:community-id community-id
|
||||
:emoji emoji
|
||||
:edit-channel-id chat-id
|
||||
:category-id category-id
|
||||
:position position})}
|
||||
(navigation/open-modal :edit-community-channel nil))))
|
||||
|
||||
(fx/defn community-created
|
||||
(rf/defn community-created
|
||||
{:events [::community-created]}
|
||||
[cofx response-js]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(navigation/navigate-back)
|
||||
(handle-response response-js)))
|
||||
|
||||
(fx/defn community-edited
|
||||
(rf/defn community-edited
|
||||
{:events [::community-edited]}
|
||||
[cofx response-js]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(navigation/navigate-back)
|
||||
(handle-response response-js)))
|
||||
|
||||
(fx/defn open-create-community
|
||||
(rf/defn open-create-community
|
||||
{:events [::open-create-community]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{:db (assoc db :communities/create {:membership constants/community-no-membership-access})}
|
||||
(navigation/navigate-to :community-create nil)))
|
||||
|
||||
(fx/defn open-edit-community
|
||||
(rf/defn open-edit-community
|
||||
{:events [::open-edit-community]}
|
||||
[{:keys [db] :as cofx} id]
|
||||
(let [{:keys [name description images permissions color]} (get-in db [:communities id])
|
||||
{:keys [access]} permissions]
|
||||
(fx/merge cofx
|
||||
{:db (assoc db :communities/create {:id id
|
||||
:name name
|
||||
:description description
|
||||
:image (get-in images [:large :uri])
|
||||
:membership access
|
||||
:color color
|
||||
:editing? true})}
|
||||
{:keys [access]} permissions]
|
||||
(rf/merge cofx
|
||||
{:db (assoc db
|
||||
:communities/create
|
||||
{:id id
|
||||
:name name
|
||||
:description description
|
||||
:image (get-in images [:large :uri])
|
||||
:membership access
|
||||
:color color
|
||||
:editing? true})}
|
||||
(navigation/navigate-to :community-edit nil))))
|
||||
|
||||
(fx/defn community-imported
|
||||
(rf/defn community-imported
|
||||
{:events [::community-imported]}
|
||||
[cofx response-js]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(navigation/navigate-back)
|
||||
(handle-response response-js)))
|
||||
|
||||
(fx/defn people-invited
|
||||
(rf/defn people-invited
|
||||
{:events [::people-invited]}
|
||||
[cofx response-js]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(navigation/navigate-back)
|
||||
(handle-response response-js)))
|
||||
|
||||
(fx/defn community-channel-created
|
||||
(rf/defn community-channel-created
|
||||
{:events [::community-channel-created]}
|
||||
[cofx response-js]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(navigation/navigate-back)
|
||||
(handle-response response-js)))
|
||||
|
||||
(fx/defn community-channel-edited
|
||||
(rf/defn community-channel-edited
|
||||
{:events [::community-channel-edited]}
|
||||
[cofx response-js]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(navigation/navigate-back)
|
||||
(handle-response response-js)))
|
||||
|
||||
(fx/defn create-field
|
||||
(rf/defn create-field
|
||||
{:events [::create-field]}
|
||||
[{:keys [db]} field value]
|
||||
{:db (assoc-in db [:communities/create field] value)})
|
||||
|
||||
(fx/defn remove-field
|
||||
(rf/defn remove-field
|
||||
{:events [::remove-field]}
|
||||
[{:keys [db]} field]
|
||||
{:db (update-in db [:communities/create] dissoc field)})
|
||||
|
||||
(fx/defn create-channel-field
|
||||
(rf/defn create-channel-field
|
||||
{:events [::create-channel-field]}
|
||||
[{:keys [db]} 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]}
|
||||
[{:keys [db]} field-values]
|
||||
{:db (update-in db [:communities/create-channel] merge field-values)})
|
||||
|
||||
(fx/defn member-banned
|
||||
(rf/defn member-banned
|
||||
{:events [::member-banned]}
|
||||
[cofx response-js]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(bottom-sheet/hide-bottom-sheet)
|
||||
(handle-response response-js)
|
||||
(activity-center/notifications-fetch-unread-count)))
|
||||
|
||||
(fx/defn member-ban
|
||||
(rf/defn member-ban
|
||||
{:events [::member-ban]}
|
||||
[cofx community-id public-key]
|
||||
{::json-rpc/call [{:method "wakuext_banUserFromCommunity"
|
||||
@@ -467,35 +492,43 @@
|
||||
:user public-key}]
|
||||
:js-response true
|
||||
: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]}
|
||||
[cofx response-js]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(bottom-sheet/hide-bottom-sheet)
|
||||
(handle-response response-js)))
|
||||
|
||||
(fx/defn member-kick
|
||||
(rf/defn member-kick
|
||||
{:events [::member-kick]}
|
||||
[cofx community-id public-key]
|
||||
{::json-rpc/call [{:method "wakuext_removeUserFromCommunity"
|
||||
:params [community-id public-key]
|
||||
:js-response true
|
||||
: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]}
|
||||
[cofx community-id]
|
||||
(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]}
|
||||
[{: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]}
|
||||
[cofx community-id]
|
||||
{::json-rpc/call [{:method "wakuext_pendingRequestsToJoinForCommunity"
|
||||
@@ -503,48 +536,56 @@
|
||||
:on-success #(re-frame/dispatch [::requests-to-join-fetched 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]))
|
||||
|
||||
(fx/defn request-to-join-accepted
|
||||
(rf/defn request-to-join-accepted
|
||||
{:events [::request-to-join-accepted]}
|
||||
[{: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)}
|
||||
(handle-response response-js)))
|
||||
|
||||
(fx/defn request-to-join-declined
|
||||
(rf/defn request-to-join-declined
|
||||
{:events [::request-to-join-declined]}
|
||||
[{: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)}
|
||||
(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]}
|
||||
[cofx community-id request-id]
|
||||
{::json-rpc/call [{:method "wakuext_acceptRequestToJoinCommunity"
|
||||
:params [{:id request-id}]
|
||||
:js-response true
|
||||
: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-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
|
||||
%)}]})
|
||||
|
||||
(fx/defn decline-request-to-join-pressed
|
||||
(rf/defn decline-request-to-join-pressed
|
||||
{:events [:communities.ui/decline-request-to-join-pressed]}
|
||||
[cofx community-id request-id]
|
||||
{::json-rpc/call [{:method "wakuext_declineRequestToJoinCommunity"
|
||||
:params [{:id request-id}]
|
||||
:js-response true
|
||||
: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-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)}]})
|
||||
|
||||
(fx/defn switch-communities-enabled
|
||||
(rf/defn switch-communities-enabled
|
||||
{:events [:multiaccounts.ui/switch-communities-enabled]}
|
||||
[{:keys [db]} enabled?]
|
||||
{::async-storage/set! {:communities-enabled? enabled?}
|
||||
:db (assoc db :communities/enabled? enabled?)})
|
||||
|
||||
(fx/defn create-category
|
||||
(rf/defn create-category
|
||||
{:events [::create-category-confirmation-pressed]}
|
||||
[_ community-id category-title chat-ids]
|
||||
{::json-rpc/call [{:method "wakuext_createCommunityCategory"
|
||||
@@ -557,12 +598,13 @@
|
||||
(re-frame/dispatch [:sanitize-messages-and-process-response %]))
|
||||
: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]}
|
||||
[{:keys [db]} community-id id categoryID]
|
||||
(let [category (get-in db [:communities community-id :categories categoryID])
|
||||
category-chats (map :id (filter #(and (= (:categoryID %) categoryID) (not= id (:id %)))
|
||||
(vals (get-in db [:communities community-id :chats]))))]
|
||||
category-chats (map :id
|
||||
(filter #(and (= (:categoryID %) categoryID) (not= id (:id %)))
|
||||
(vals (get-in db [:communities community-id :chats]))))]
|
||||
{::json-rpc/call [{:method "wakuext_editCommunityCategory"
|
||||
:params [{:communityId community-id
|
||||
:categoryId categoryID
|
||||
@@ -572,7 +614,7 @@
|
||||
:on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %])
|
||||
:on-error #(log/error "failed to remove chat from community" %)}]}))
|
||||
|
||||
(fx/defn delete-community-chat
|
||||
(rf/defn delete-community-chat
|
||||
{:events [:delete-community-chat]}
|
||||
[_ community-id chat-id]
|
||||
{::json-rpc/call [{:method "wakuext_deleteCommunityChat"
|
||||
@@ -581,7 +623,7 @@
|
||||
:on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %])
|
||||
:on-error #(log/error "failed to delete community chat" %)}]})
|
||||
|
||||
(fx/defn delete-category
|
||||
(rf/defn delete-category
|
||||
{:events [:delete-community-category]}
|
||||
[_ community-id category-id]
|
||||
{::json-rpc/call [{:method "wakuext_deleteCommunityCategory"
|
||||
@@ -591,7 +633,7 @@
|
||||
:on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %])
|
||||
:on-error #(log/error "failed to delete community category" %)}]})
|
||||
|
||||
(fx/defn change-category
|
||||
(rf/defn change-category
|
||||
{:events [::change-category-confirmation-pressed]}
|
||||
[cofx community-id category-id {:keys [id position categoryID]}]
|
||||
(if (not (string/blank? category-id))
|
||||
@@ -605,11 +647,11 @@
|
||||
(re-frame/dispatch [:navigate-back])
|
||||
(re-frame/dispatch [:sanitize-messages-and-process-response %]))
|
||||
:on-error #(log/error "failed to change community category" %)}]}
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(navigation/navigate-back)
|
||||
(remove-chat-from-category community-id id categoryID))))
|
||||
|
||||
(fx/defn reorder-category-chat
|
||||
(rf/defn reorder-category-chat
|
||||
{:events [::reorder-community-category-chat]}
|
||||
[_ community-id category-id chat-id new-position]
|
||||
{::json-rpc/call [{:method "wakuext_reorderCommunityChat"
|
||||
@@ -621,7 +663,7 @@
|
||||
:on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %])
|
||||
:on-error #(log/error "failed to reorder community category chat" %)}]})
|
||||
|
||||
(fx/defn reorder-category
|
||||
(rf/defn reorder-category
|
||||
{:events [::reorder-community-category]}
|
||||
[_ community-id category-id new-position]
|
||||
{::json-rpc/call [{:method "wakuext_reorderCommunityCategories"
|
||||
@@ -632,17 +674,18 @@
|
||||
:on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %])
|
||||
: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)))
|
||||
|
||||
(fx/defn store-category-state
|
||||
(rf/defn store-category-state
|
||||
{:events [::store-category-state]}
|
||||
[{:keys [db]} community-id category-id state-open?]
|
||||
(let [public-key (get-in db [:multiaccount :public-key])
|
||||
hash (category-hash public-key community-id category-id)]
|
||||
{::async-storage/set! {hash state-open?}}))
|
||||
|
||||
(fx/defn update-category-states-in-db
|
||||
(rf/defn update-category-states-in-db
|
||||
{:events [::category-states-loaded]}
|
||||
[{:keys [db]} community-id hashes states]
|
||||
(let [public-key (get-in db [:multiaccount :public-key])
|
||||
@@ -652,10 +695,11 @@
|
||||
state (get states hash)
|
||||
category-updated (assoc category :state state)]
|
||||
(assoc acc category-id category-updated)))
|
||||
{} categories-old)]
|
||||
{}
|
||||
categories-old)]
|
||||
{: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]}
|
||||
[{:keys [db]} community-id]
|
||||
(let [public-key (get-in db [:multiaccount :public-key])
|
||||
@@ -669,26 +713,27 @@
|
||||
(-> acc
|
||||
(assoc-in [:hashes category-id] hash)
|
||||
(update :keys #(conj % hash)))))
|
||||
{} categories)]
|
||||
{}
|
||||
categories)]
|
||||
{::async-storage/get {:keys keys
|
||||
:cb #(re-frame/dispatch
|
||||
[::category-states-loaded community-id hashes %])}}))
|
||||
|
||||
(fx/defn navigate-to-community
|
||||
(rf/defn navigate-to-community
|
||||
{:events [:communities/navigate-to-community]}
|
||||
[cofx community-id]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(navigation/pop-to-root-tab :shell-stack)
|
||||
(navigation/navigate-to-nav2 :community community-id true)))
|
||||
|
||||
(fx/defn member-role-updated
|
||||
(rf/defn member-role-updated
|
||||
{:events [:community.member/role-updated]}
|
||||
[cofx response-js]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(bottom-sheet/hide-bottom-sheet)
|
||||
(handle-response response-js)))
|
||||
|
||||
(fx/defn add-role-to-member
|
||||
(rf/defn add-role-to-member
|
||||
{:events [:community.member/add-role]}
|
||||
[cofx community-id public-key role-id]
|
||||
{::json-rpc/call [{:method "wakuext_addRoleToMember"
|
||||
@@ -702,7 +747,7 @@
|
||||
:public-key public-key
|
||||
:role-id role-id})}]})
|
||||
|
||||
(fx/defn remove-role-from-member
|
||||
(rf/defn remove-role-from-member
|
||||
{:events [:community.member/remove-role]}
|
||||
[_ community-id public-key role-id]
|
||||
{::json-rpc/call [{:method "wakuext_removeRoleFromMember"
|
||||
@@ -710,8 +755,9 @@
|
||||
:user public-key
|
||||
:role role-id}]}
|
||||
:on-success #(re-frame/dispatch [:community.member/role-updated %])
|
||||
:on-error #(log/error "failed to remove role from member"
|
||||
{:error %
|
||||
:community-id community-id
|
||||
:public-key public-key
|
||||
:role-id role-id})]})
|
||||
:on-error
|
||||
#(log/error "failed to remove role from member"
|
||||
{:error %
|
||||
:community-id community-id
|
||||
:public-key public-key
|
||||
:role-id role-id})]})
|
||||
|
||||
@@ -4,12 +4,12 @@
|
||||
[status-im.contact.db :as contact.db]
|
||||
[status-im.data-store.chats :as chats-store]
|
||||
[status-im.data-store.contacts :as contacts-store]
|
||||
[status-im2.navigation.events :as navigation]
|
||||
[status-im.utils.types :as types]
|
||||
[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}
|
||||
public-key
|
||||
{:keys [chat-id
|
||||
@@ -21,60 +21,63 @@
|
||||
(when (= from public-key)
|
||||
message-id))
|
||||
(get-in db [:messages chat-id]))
|
||||
db (-> db
|
||||
;; remove messages
|
||||
(update-in [:messages chat-id]
|
||||
#(apply dissoc % removed-messages-ids))
|
||||
(update-in [:chats chat-id]
|
||||
assoc
|
||||
:unviewed-messages-count unviewed-messages-count
|
||||
:unviewed-mentions-count unviewed-mentions-count
|
||||
:last-message last-message))]
|
||||
{:db (assoc-in db [:message-lists chat-id]
|
||||
(message-list/add-many nil (vals (get-in db [:messages chat-id]))))}))
|
||||
db (-> db
|
||||
;; remove messages
|
||||
(update-in [:messages chat-id]
|
||||
#(apply dissoc % removed-messages-ids))
|
||||
(update-in [:chats chat-id]
|
||||
assoc
|
||||
:unviewed-messages-count unviewed-messages-count
|
||||
:unviewed-mentions-count unviewed-mentions-count
|
||||
:last-message last-message))]
|
||||
{:db (assoc-in db
|
||||
[: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]}
|
||||
[{:keys [db] :as cofx} {:keys [public-key]} chats]
|
||||
(let [fxs (when chats
|
||||
(map #(->> (chats-store/<-rpc %)
|
||||
(clean-up-chat public-key)) (types/js->clj chats)))]
|
||||
(apply fx/merge
|
||||
(clean-up-chat public-key))
|
||||
(types/js->clj chats)))]
|
||||
(apply rf/merge
|
||||
cofx
|
||||
{:db (-> db
|
||||
(update :chats dissoc public-key)
|
||||
(update :chats-home-list disj public-key)
|
||||
(assoc-in [:contacts/contacts public-key :added] false))
|
||||
{:db (-> db
|
||||
(update :chats dissoc public-key)
|
||||
(update :chats-home-list disj public-key)
|
||||
(assoc-in [:contacts/contacts public-key :added] false))
|
||||
:clear-message-notifications
|
||||
[[public-key] (get-in db [:multiaccount :remote-push-notifications-enabled?])]}
|
||||
(activity-center/notifications-fetch-unread-count)
|
||||
fxs)))
|
||||
|
||||
(fx/defn block-contact
|
||||
(rf/defn block-contact
|
||||
{:events [:contact.ui/block-contact-confirmed]}
|
||||
[{:keys [db] :as cofx} public-key]
|
||||
(let [contact (-> (contact.db/public-key->contact
|
||||
(:contacts/contacts db)
|
||||
public-key)
|
||||
(assoc :blocked true
|
||||
:added false))
|
||||
(let [contact (-> (contact.db/public-key->contact
|
||||
(:contacts/contacts db)
|
||||
public-key)
|
||||
(assoc :blocked true
|
||||
:added false))
|
||||
from-one-to-one-chat? (not (get-in db [:chats (:current-chat-id db) :group-chat]))]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{:db (-> db
|
||||
;; add the contact to blocked contacts
|
||||
(update :contacts/blocked (fnil conj #{}) public-key)
|
||||
;; update the contact in contacts list
|
||||
(assoc-in [:contacts/contacts public-key] contact))}
|
||||
(contacts-store/block
|
||||
public-key #(do (re-frame/dispatch [::contact-blocked contact (.-chats %)])
|
||||
(re-frame/dispatch [:sanitize-messages-and-process-response %])
|
||||
(re-frame/dispatch [:hide-popover])))
|
||||
public-key
|
||||
#(do (re-frame/dispatch [::contact-blocked contact (.-chats %)])
|
||||
(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
|
||||
(if from-one-to-one-chat?
|
||||
(navigation/pop-to-root-tab :chat-stack)
|
||||
(navigation/navigate-back)))))
|
||||
|
||||
(fx/defn contact-unblocked
|
||||
(rf/defn contact-unblocked
|
||||
{:events [::contact-unblocked]}
|
||||
[{:keys [db]} contact-id]
|
||||
(let [contact (-> (get-in db [:contacts/contacts contact-id])
|
||||
@@ -83,7 +86,7 @@
|
||||
(update :contacts/blocked disj contact-id)
|
||||
(assoc-in [:contacts/contacts contact-id] contact))}))
|
||||
|
||||
(fx/defn unblock-contact
|
||||
(rf/defn unblock-contact
|
||||
{:events [:contact.ui/unblock-contact-pressed]}
|
||||
[cofx contact-id]
|
||||
(contacts-store/unblock
|
||||
|
||||
@@ -1,29 +1,29 @@
|
||||
(ns status-im.contact.chat
|
||||
(: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.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]
|
||||
:interceptors [(re-frame/inject-cofx :random-id-generator)]}
|
||||
[cofx {:keys [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]
|
||||
:interceptors [(re-frame/inject-cofx :random-id-generator)]}
|
||||
[{{:contacts/keys [new-identity]} :db :as cofx} new-contact? nickname]
|
||||
(let [{:keys [public-key ens-name]} new-identity]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
#(if new-contact?
|
||||
(contact/add-contact % public-key nickname ens-name)
|
||||
(chat/start-chat % public-key ens-name))
|
||||
#(when new-contact?
|
||||
(navigation/navigate-back %)))))
|
||||
|
||||
(fx/defn pinned-messages-pressed
|
||||
{:events [:contact.ui/pinned-messages-pressed]}
|
||||
(rf/defn pinned-messages-pressed
|
||||
{:events [:contact.ui/pinned-messages-pressed]}
|
||||
[cofx public-key]
|
||||
(chat/navigate-to-user-pinned-messages cofx public-key))
|
||||
|
||||
@@ -1,42 +1,44 @@
|
||||
(ns status-im.contact.core
|
||||
(: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.data-store.contacts :as contacts-store]
|
||||
[status-im.ethereum.json-rpc :as json-rpc]
|
||||
[status-im2.navigation.events :as navigation]
|
||||
[status-im.multiaccounts.update.core :as multiaccounts.update]
|
||||
[status-im.utils.fx :as fx]
|
||||
[status-im2.navigation.events :as navigation]
|
||||
[taoensso.timbre :as log]
|
||||
[status-im.constants :as constants]
|
||||
[status-im.contact.block :as contact.block]))
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(fx/defn load-contacts
|
||||
(rf/defn load-contacts
|
||||
{:events [::contacts-loaded]}
|
||||
[{:keys [db] :as cofx} all-contacts]
|
||||
(let [contacts-list (map #(vector (:public-key %) (if (empty? (:address %))
|
||||
(dissoc % :address)
|
||||
%))
|
||||
(let [contacts-list (map #(vector (:public-key %)
|
||||
(if (empty? (:address %))
|
||||
(dissoc % :address)
|
||||
%))
|
||||
all-contacts)
|
||||
contacts (into {} contacts-list)]
|
||||
contacts (into {} contacts-list)]
|
||||
{:db (cond-> (-> db
|
||||
(update :contacts/contacts #(merge contacts %))
|
||||
(assoc :contacts/blocked (contact.db/get-blocked-contacts all-contacts))))}))
|
||||
|
||||
(defn build-contact
|
||||
[{{:keys [multiaccount]
|
||||
:contacts/keys [contacts]} :db} public-key]
|
||||
[{{:keys [multiaccount]
|
||||
:contacts/keys [contacts]}
|
||||
:db} public-key]
|
||||
(cond-> (contact.db/public-key->contact contacts public-key)
|
||||
(= public-key (:public-key multiaccount))
|
||||
(assoc :name (:name multiaccount))))
|
||||
|
||||
(fx/defn ensure-contacts
|
||||
(rf/defn ensure-contacts
|
||||
[{:keys [db]} contacts chats]
|
||||
(let [events
|
||||
(reduce
|
||||
(fn [acc {:keys [public-key] :as contact}]
|
||||
(let [added (:added contact)
|
||||
was-added (contact.db/added? db public-key)
|
||||
blocked (:blocked contact)
|
||||
(let [added (:added contact)
|
||||
was-added (contact.db/added? db public-key)
|
||||
blocked (:blocked contact)
|
||||
was-blocked (contact.db/blocked? db public-key)]
|
||||
(cond-> acc
|
||||
(and added (not was-added))
|
||||
@@ -51,7 +53,8 @@
|
||||
[:activity-center.notifications/fetch-unread-count]]
|
||||
contacts)]
|
||||
(merge
|
||||
{:db (update db :contacts/contacts
|
||||
{:db (update db
|
||||
:contacts/contacts
|
||||
#(reduce (fn [acc {:keys [public-key] :as contact}]
|
||||
(-> acc
|
||||
(update public-key merge contact)
|
||||
@@ -68,15 +71,15 @@
|
||||
:profile-image identicon
|
||||
:address address}))
|
||||
|
||||
(fx/defn send-contact-request
|
||||
(rf/defn send-contact-request
|
||||
{:events [::send-contact-request]}
|
||||
[{:keys [db] :as cofx} public-key]
|
||||
(let [{:keys [name profile-image]} (own-info db)]
|
||||
{::json-rpc/call [{:method "wakuext_sendContactUpdate"
|
||||
:params [public-key name profile-image]
|
||||
{::json-rpc/call [{:method "wakuext_sendContactUpdate"
|
||||
:params [public-key name profile-image]
|
||||
: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"
|
||||
{:events [:contact.ui/add-to-contact-pressed]}
|
||||
[{:keys [db] :as cofx} public-key nickname ens-name]
|
||||
@@ -90,60 +93,62 @@
|
||||
(re-frame/dispatch [:sanitize-messages-and-process-response %])
|
||||
(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"
|
||||
{:events [:contact.ui/remove-contact-pressed]}
|
||||
[{:keys [db]} {:keys [public-key]}]
|
||||
{:db (-> db
|
||||
(assoc-in [:contacts/contacts public-key :added] false)
|
||||
(assoc-in [:contacts/contacts public-key :contact-request-state] constants/contact-request-state-none))
|
||||
::json-rpc/call [{:method "wakuext_removeContact"
|
||||
:params [public-key]
|
||||
{:db (-> db
|
||||
(assoc-in [:contacts/contacts public-key :added] false)
|
||||
(assoc-in [:contacts/contacts public-key :contact-request-state]
|
||||
constants/contact-request-state-none))
|
||||
::json-rpc/call [{:method "wakuext_removeContact"
|
||||
:params [public-key]
|
||||
:on-success #(log/debug "contact removed successfully")}
|
||||
{:method "wakuext_retractContactRequest"
|
||||
:params [{:contactId public-key}]
|
||||
{:method "wakuext_retractContactRequest"
|
||||
:params [{:contactId public-key}]
|
||||
: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]}
|
||||
[{:keys [db]} id]
|
||||
{::json-rpc/call [{:method "wakuext_acceptContactRequest"
|
||||
:params [{:id id}]
|
||||
{::json-rpc/call [{:method "wakuext_acceptContactRequest"
|
||||
:params [{:id id}]
|
||||
: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]}
|
||||
[{:keys [db]} id]
|
||||
{::json-rpc/call [{:method "wakuext_dismissContactRequest"
|
||||
:params [{:id id}]
|
||||
{::json-rpc/call [{:method "wakuext_dismissContactRequest"
|
||||
:params [{:id id}]
|
||||
: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 %])))
|
||||
|
||||
(fx/defn open-contact-toggle-list
|
||||
(rf/defn open-contact-toggle-list
|
||||
{:events [:contact.ui/start-group-chat-pressed]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{:db (assoc db
|
||||
:group/selected-contacts #{}
|
||||
:new-chat-name "")}
|
||||
:new-chat-name "")}
|
||||
(navigation/navigate-to-cofx :contact-toggle-list nil)))
|
||||
|
||||
(fx/defn update-nickname
|
||||
(rf/defn update-nickname
|
||||
{:events [:contacts/update-nickname]}
|
||||
[{:keys [db] :as cofx} public-key nickname]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(contacts-store/set-nickname
|
||||
public-key
|
||||
nickname
|
||||
#(re-frame/dispatch [:sanitize-messages-and-process-response %]))
|
||||
(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]}
|
||||
[cofx enabled?]
|
||||
(multiaccounts.update/multiaccount-update
|
||||
@@ -152,7 +157,7 @@
|
||||
enabled?
|
||||
nil))
|
||||
|
||||
(fx/defn set-search-query
|
||||
(rf/defn set-search-query
|
||||
{:events [:contacts/set-search-query]}
|
||||
[{:keys [db] :as cofx} value]
|
||||
{:db (assoc db :contacts/search-query value)})
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
(ns status-im.currency.core
|
||||
(: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))
|
||||
|
||||
(fx/defn set-currency
|
||||
(rf/defn set-currency
|
||||
{:events [:wallet.settings.ui/currency-selected]}
|
||||
[{:keys [db] :as cofx} currency]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(multiaccounts.update/multiaccount-update
|
||||
:currency currency
|
||||
:currency
|
||||
currency
|
||||
{})
|
||||
(prices/update-prices)))
|
||||
|
||||
@@ -1,31 +1,34 @@
|
||||
(ns status-im.data-store.chats
|
||||
(:require [clojure.set :as clojure.set]
|
||||
[status-im.constants :as constants]
|
||||
[status-im.data-store.messages :as messages]
|
||||
[status-im.ethereum.json-rpc :as json-rpc]
|
||||
[status-im.constants :as constants]
|
||||
[status-im.utils.fx :as fx]
|
||||
[status-im.utils.types :as types]
|
||||
[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
|
||||
(or (= constants/public-chat-type chat-type)
|
||||
(= constants/profile-chat-type chat-type)
|
||||
(= constants/timeline-chat-type chat-type)) (assoc chat
|
||||
:chat-name (str "#" name)
|
||||
:public? true
|
||||
:group-chat true
|
||||
:timeline? (= constants/timeline-chat-type chat-type))
|
||||
(= constants/timeline-chat-type chat-type))
|
||||
(assoc chat
|
||||
:chat-name (str "#" name)
|
||||
:public? true
|
||||
:group-chat true
|
||||
:timeline? (= constants/timeline-chat-type chat-type))
|
||||
(= constants/community-chat-type chat-type) (assoc chat
|
||||
:chat-name name
|
||||
:chat-name name
|
||||
:group-chat true)
|
||||
(= constants/private-group-chat-type chat-type) (assoc chat
|
||||
:chat-name name
|
||||
:public? false
|
||||
:chat-name name
|
||||
:public? false
|
||||
:group-chat true)
|
||||
:else (assoc chat :public? false :group-chat false)))
|
||||
|
||||
(defn members-reducer [acc member]
|
||||
(defn members-reducer
|
||||
[acc member]
|
||||
(cond-> acc
|
||||
(:admin member)
|
||||
(update :admins conj (:id member))
|
||||
@@ -34,45 +37,48 @@
|
||||
:always
|
||||
(update :contacts conj (:id member))))
|
||||
|
||||
(defn- unmarshal-members [{:keys [members chat-type] :as chat}]
|
||||
(defn- unmarshal-members
|
||||
[{:keys [members chat-type] :as chat}]
|
||||
(cond
|
||||
(= constants/public-chat-type chat-type) (assoc chat
|
||||
:contacts #{}
|
||||
:admins #{}
|
||||
:members-joined #{})
|
||||
(= constants/public-chat-type chat-type) (assoc chat
|
||||
:contacts #{}
|
||||
:admins #{}
|
||||
:members-joined #{})
|
||||
(= constants/private-group-chat-type chat-type) (merge chat
|
||||
(reduce members-reducer
|
||||
{:admins #{}
|
||||
{:admins #{}
|
||||
:members-joined #{}
|
||||
:contacts #{}}
|
||||
:contacts #{}}
|
||||
members))
|
||||
:else
|
||||
(assoc chat
|
||||
:contacts #{(:id chat)}
|
||||
:admins #{}
|
||||
:contacts #{(:id chat)}
|
||||
:admins #{}
|
||||
:members-joined #{})))
|
||||
|
||||
(defn <-rpc [chat]
|
||||
(defn <-rpc
|
||||
[chat]
|
||||
(-> chat
|
||||
(clojure.set/rename-keys {:id :chat-id
|
||||
:communityId :community-id
|
||||
:syncedFrom :synced-from
|
||||
:syncedTo :synced-to
|
||||
(clojure.set/rename-keys {:id :chat-id
|
||||
:communityId :community-id
|
||||
:syncedFrom :synced-from
|
||||
:syncedTo :synced-to
|
||||
:membershipUpdateEvents :membership-update-events
|
||||
:deletedAtClockValue :deleted-at-clock-value
|
||||
:chatType :chat-type
|
||||
:unviewedMessagesCount :unviewed-messages-count
|
||||
:unviewedMentionsCount :unviewed-mentions-count
|
||||
:lastMessage :last-message
|
||||
:lastClockValue :last-clock-value
|
||||
:invitationAdmin :invitation-admin
|
||||
:profile :profile-public-key})
|
||||
:deletedAtClockValue :deleted-at-clock-value
|
||||
:chatType :chat-type
|
||||
:unviewedMessagesCount :unviewed-messages-count
|
||||
:unviewedMentionsCount :unviewed-mentions-count
|
||||
:lastMessage :last-message
|
||||
:lastClockValue :last-clock-value
|
||||
:invitationAdmin :invitation-admin
|
||||
:profile :profile-public-key})
|
||||
rpc->type
|
||||
unmarshal-members
|
||||
(update :last-message #(when % (messages/<-rpc %)))
|
||||
(dissoc :members)))
|
||||
|
||||
(defn <-rpc-js [^js chat]
|
||||
(defn <-rpc-js
|
||||
[^js chat]
|
||||
(-> {:name (.-name chat)
|
||||
:description (.-description chat)
|
||||
:color (.-color chat)
|
||||
@@ -103,9 +109,10 @@
|
||||
rpc->type
|
||||
unmarshal-members))
|
||||
|
||||
(fx/defn fetch-chats-rpc [_ {:keys [on-success]}]
|
||||
{::json-rpc/call [{:method "wakuext_chatsPreview"
|
||||
:params []
|
||||
(rf/defn fetch-chats-rpc
|
||||
[_ {:keys [on-success]}]
|
||||
{::json-rpc/call [{:method "wakuext_chatsPreview"
|
||||
:params []
|
||||
:js-response true
|
||||
:on-success #(on-success ^js %)
|
||||
:on-error #(log/error "failed to fetch chats" 0 -1 %)}]})
|
||||
:on-success #(on-success ^js %)
|
||||
:on-error #(log/error "failed to fetch chats" 0 -1 %)}]})
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
(ns status-im.data-store.contacts
|
||||
(:require [clojure.set :as clojure.set]
|
||||
[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
|
||||
(clojure.set/rename-keys
|
||||
{:id :public-key
|
||||
@@ -17,48 +18,54 @@
|
||||
:lastENSClockValue :last-ens-clock-value
|
||||
:lastUpdated :last-updated
|
||||
:localNickname :nickname})
|
||||
(assoc :mutual? (and (:added contact)
|
||||
(:hasAddedUs contact)))))
|
||||
(assoc :mutual?
|
||||
(and (:added contact)
|
||||
(:hasAddedUs contact)))))
|
||||
|
||||
(fx/defn fetch-contacts-rpc
|
||||
(rf/defn fetch-contacts-rpc
|
||||
[_ on-success]
|
||||
{::json-rpc/call [{:method "wakuext_contacts"
|
||||
:params []
|
||||
{::json-rpc/call [{:method "wakuext_contacts"
|
||||
:params []
|
||||
: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]
|
||||
{::json-rpc/call [{:method "wakuext_addContact"
|
||||
:params [{:id public-key :nickname nickname :ensName ens-name}]
|
||||
{::json-rpc/call [{:method "wakuext_addContact"
|
||||
:params [{:id public-key :nickname nickname :ensName ens-name}]
|
||||
:js-response true
|
||||
:on-success #(do
|
||||
(log/info "saved contact" public-key "successfuly")
|
||||
(when on-success
|
||||
(on-success %)))
|
||||
:on-error #(log/error "failed to add contact" public-key %)}]})
|
||||
:on-success #(do
|
||||
(log/info "saved contact" public-key "successfuly")
|
||||
(when on-success
|
||||
(on-success %)))
|
||||
:on-error #(log/error "failed to add contact" public-key %)}]})
|
||||
|
||||
(fx/defn set-nickname
|
||||
(rf/defn set-nickname
|
||||
[_ public-key nickname on-success]
|
||||
{::json-rpc/call [{:method "wakuext_setContactLocalNickname"
|
||||
:params [{:id public-key :nickname nickname}]
|
||||
{::json-rpc/call [{:method "wakuext_setContactLocalNickname"
|
||||
:params [{:id public-key :nickname nickname}]
|
||||
:js-response true
|
||||
:on-success #(do
|
||||
(log/debug "set contact nickname" public-key "successfuly" nickname)
|
||||
(when on-success
|
||||
(on-success %)))
|
||||
:on-error #(log/error "failed to set contact nickname " public-key nickname %)}]})
|
||||
:on-success #(do
|
||||
(log/debug "set contact nickname" public-key "successfuly" nickname)
|
||||
(when on-success
|
||||
(on-success %)))
|
||||
:on-error #(log/error "failed to set contact nickname "
|
||||
public-key
|
||||
nickname
|
||||
%)}]})
|
||||
|
||||
(fx/defn block [_ contact-id on-success]
|
||||
{::json-rpc/call [{:method "wakuext_blockContact"
|
||||
:params [contact-id]
|
||||
(rf/defn block
|
||||
[_ contact-id on-success]
|
||||
{::json-rpc/call [{:method "wakuext_blockContact"
|
||||
:params [contact-id]
|
||||
:js-response true
|
||||
:on-success on-success
|
||||
:on-error #(log/error "failed to block contact" % contact-id)}]})
|
||||
:on-success on-success
|
||||
:on-error #(log/error "failed to block contact" % contact-id)}]})
|
||||
|
||||
(fx/defn unblock [_ contact-id on-success]
|
||||
{::json-rpc/call [{:method "wakuext_unblockContact"
|
||||
:params [contact-id]
|
||||
:on-success on-success
|
||||
(rf/defn unblock
|
||||
[_ contact-id on-success]
|
||||
{::json-rpc/call [{:method "wakuext_unblockContact"
|
||||
:params [contact-id]
|
||||
:on-success on-success
|
||||
:js-response true
|
||||
:on-error #(log/error "failed to unblock contact" % contact-id)}]})
|
||||
:on-error #(log/error "failed to unblock contact" % contact-id)}]})
|
||||
|
||||
@@ -1,103 +1,118 @@
|
||||
(ns status-im.data-store.messages
|
||||
(:require [clojure.set :as clojure.set]
|
||||
[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
|
||||
content
|
||||
(assoc :text (:text content)
|
||||
(assoc :text (:text content)
|
||||
:sticker (:sticker content))
|
||||
:always
|
||||
(clojure.set/rename-keys {:chat-id :chat_id
|
||||
(clojure.set/rename-keys {:chat-id :chat_id
|
||||
:whisper-timestamp :whisperTimestamp
|
||||
:community-id :communityId
|
||||
:clock-value :clock})))
|
||||
:community-id :communityId
|
||||
:clock-value :clock})))
|
||||
|
||||
(defn <-rpc [message]
|
||||
(defn <-rpc
|
||||
[message]
|
||||
(-> message
|
||||
(clojure.set/rename-keys {:id :message-id
|
||||
:whisperTimestamp :whisper-timestamp
|
||||
:editedAt :edited-at
|
||||
(clojure.set/rename-keys {:id :message-id
|
||||
:whisperTimestamp :whisper-timestamp
|
||||
:editedAt :edited-at
|
||||
:contactVerificationState :contact-verification-state
|
||||
:contactRequestState :contact-request-state
|
||||
:commandParameters :command-parameters
|
||||
:gapParameters :gap-parameters
|
||||
:messageType :message-type
|
||||
:localChatId :chat-id
|
||||
:communityId :community-id
|
||||
:contentType :content-type
|
||||
:clock :clock-value
|
||||
:quotedMessage :quoted-message
|
||||
:outgoingStatus :outgoing-status
|
||||
:audioDurationMs :audio-duration-ms
|
||||
:deleted :deleted?
|
||||
:deletedForMe :deleted-for-me?
|
||||
:new :new?})
|
||||
:contactRequestState :contact-request-state
|
||||
:commandParameters :command-parameters
|
||||
:gapParameters :gap-parameters
|
||||
:messageType :message-type
|
||||
:localChatId :chat-id
|
||||
:communityId :community-id
|
||||
:contentType :content-type
|
||||
:clock :clock-value
|
||||
:quotedMessage :quoted-message
|
||||
:outgoingStatus :outgoing-status
|
||||
:audioDurationMs :audio-duration-ms
|
||||
:deleted :deleted?
|
||||
:deletedForMe :deleted-for-me?
|
||||
: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 :command-parameters clojure.set/rename-keys {:transactionHash :transaction-hash
|
||||
:commandState :command-state})
|
||||
(assoc :content {:chat-id (:chatId message)
|
||||
:text (:text message)
|
||||
:image (:image message)
|
||||
:sticker (:sticker message)
|
||||
:ens-name (:ensName message)
|
||||
:line-count (:lineCount message)
|
||||
:parsed-text (:parsedText message)
|
||||
:links (:links message)
|
||||
:rtl? (:rtl message)
|
||||
:response-to (:responseTo message)}
|
||||
(update :command-parameters
|
||||
clojure.set/rename-keys
|
||||
{:transactionHash :transaction-hash
|
||||
:commandState :command-state})
|
||||
(assoc :content {:chat-id (:chatId message)
|
||||
:text (:text message)
|
||||
:image (:image message)
|
||||
:sticker (:sticker message)
|
||||
:ens-name (:ensName message)
|
||||
:line-count (:lineCount message)
|
||||
:parsed-text (:parsedText message)
|
||||
:links (:links message)
|
||||
:rtl? (:rtl message)
|
||||
:response-to (:responseTo message)}
|
||||
:outgoing (boolean (:outgoingStatus message)))
|
||||
(dissoc :ensName :chatId :text :rtl :responseTo :image :sticker :lineCount :parsedText :links)))
|
||||
|
||||
(defn messages-by-chat-id-rpc [chat-id
|
||||
cursor
|
||||
limit
|
||||
on-success
|
||||
on-error]
|
||||
(defn messages-by-chat-id-rpc
|
||||
[chat-id
|
||||
cursor
|
||||
limit
|
||||
on-success
|
||||
on-error]
|
||||
{::json-rpc/call [{:method "wakuext_chatMessages"
|
||||
:params [chat-id cursor limit]
|
||||
:on-success (fn [result]
|
||||
(on-success (update result :messages #(map <-rpc %))))
|
||||
:on-error on-error}]})
|
||||
:on-error on-error}]})
|
||||
|
||||
(defn mark-seen-rpc [chat-id ids on-success]
|
||||
{::json-rpc/call [{:method "wakuext_markMessagesSeen"
|
||||
:params [chat-id ids]
|
||||
(defn mark-seen-rpc
|
||||
[chat-id ids on-success]
|
||||
{::json-rpc/call [{:method "wakuext_markMessagesSeen"
|
||||
:params [chat-id ids]
|
||||
:on-success #(do
|
||||
(log/debug "successfully marked as seen" %)
|
||||
(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]
|
||||
{::json-rpc/call [{:method "wakuext_deleteMessage"
|
||||
:params [id]
|
||||
(defn delete-message-rpc
|
||||
[id]
|
||||
{::json-rpc/call [{:method "wakuext_deleteMessage"
|
||||
:params [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]
|
||||
{::json-rpc/call [{:method "wakuext_deleteMessagesFrom"
|
||||
:params [author]
|
||||
(defn delete-messages-from-rpc
|
||||
[author]
|
||||
{::json-rpc/call [{:method "wakuext_deleteMessagesFrom"
|
||||
:params [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]
|
||||
{::json-rpc/call [{:method "wakuext_deleteMessagesByChatID"
|
||||
:params [chat-id]
|
||||
(defn delete-messages-by-chat-id-rpc
|
||||
[chat-id]
|
||||
{::json-rpc/call [{:method "wakuext_deleteMessagesByChatID"
|
||||
:params [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))
|
||||
|
||||
(fx/defn delete-messages-from [cofx author]
|
||||
(rf/defn delete-messages-from
|
||||
[cofx 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))
|
||||
|
||||
(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))
|
||||
|
||||
@@ -1,31 +1,36 @@
|
||||
(ns status-im.data-store.pin-messages
|
||||
(:require [clojure.set :as clojure.set]
|
||||
[status-im.data-store.messages :as messages]
|
||||
[status-im.ethereum.json-rpc :as json-rpc]
|
||||
[status-im.utils.fx :as fx]
|
||||
[taoensso.timbre :as log]
|
||||
[status-im.data-store.messages :as messages]))
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(defn <-rpc [message]
|
||||
(defn <-rpc
|
||||
[message]
|
||||
(-> message
|
||||
(merge (messages/<-rpc (message :message)))
|
||||
(clojure.set/rename-keys {:pinnedAt :pinned-at
|
||||
:pinnedBy :pinned-by})
|
||||
(dissoc :message)))
|
||||
|
||||
(defn pinned-message-by-chat-id-rpc [chat-id
|
||||
cursor
|
||||
limit
|
||||
on-success
|
||||
on-error]
|
||||
(defn pinned-message-by-chat-id-rpc
|
||||
[chat-id
|
||||
cursor
|
||||
limit
|
||||
on-success
|
||||
on-error]
|
||||
{::json-rpc/call [{:method "wakuext_chatPinnedMessages"
|
||||
:params [chat-id cursor limit]
|
||||
: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-error on-error}]})
|
||||
:on-error on-error}]})
|
||||
|
||||
(fx/defn send-pin-message [cofx pin-message]
|
||||
{::json-rpc/call [{:method "wakuext_sendPinMessage"
|
||||
:params [(messages/->rpc pin-message)]
|
||||
(rf/defn send-pin-message
|
||||
[cofx pin-message]
|
||||
{::json-rpc/call [{:method "wakuext_sendPinMessage"
|
||||
:params [(messages/->rpc 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]
|
||||
[re-frame.core :as re-frame]
|
||||
[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]
|
||||
(clojure.set/rename-keys visibility-status-update {:publicKey :public-key
|
||||
:statusType :status-type}))
|
||||
(defn <-rpc-settings [settings]
|
||||
(defn <-rpc
|
||||
[visibility-status-update]
|
||||
(clojure.set/rename-keys visibility-status-update
|
||||
{:publicKey :public-key
|
||||
:statusType :status-type}))
|
||||
(defn <-rpc-settings
|
||||
[settings]
|
||||
(-> settings
|
||||
(clojure.set/rename-keys
|
||||
{:current-user-status :current-user-visibility-status})
|
||||
(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"
|
||||
:params []
|
||||
:on-success #(re-frame/dispatch
|
||||
[:visibility-status-updates/visibility-status-updates-loaded
|
||||
(:statusUpdates ^js %)])
|
||||
:on-error #(log/error
|
||||
"failed to fetch visibility-status-updates" %)}]})
|
||||
:on-error #(log/error
|
||||
"failed to fetch visibility-status-updates"
|
||||
%)}]})
|
||||
|
||||
+104
-74
@@ -2,18 +2,19 @@
|
||||
(:refer-clojure :exclude [name])
|
||||
(: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.eip55 :as eip55]
|
||||
[status-im.ethereum.ens :as ens]
|
||||
[status-im.ethereum.stateofus :as stateofus]
|
||||
[status-im.multiaccounts.update.core :as multiaccounts.update]
|
||||
[status-im2.navigation.events :as navigation]
|
||||
[status-im.utils.datetime :as datetime]
|
||||
[status-im.utils.fx :as fx]
|
||||
[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?
|
||||
username
|
||||
(stateofus/subdomain username)))
|
||||
@@ -38,14 +39,16 @@
|
||||
(fn [[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]}
|
||||
[{:keys [db]} new-state username custom-domain? tx-hash]
|
||||
{:db (assoc-in db [:ens/registrations tx-hash] {:state new-state
|
||||
:username username
|
||||
:custom-domain? custom-domain?})})
|
||||
{:db (assoc-in db
|
||||
[:ens/registrations tx-hash]
|
||||
{: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]}
|
||||
[cofx]
|
||||
;; we reset navigation so that navigate back doesn't return
|
||||
@@ -55,19 +58,19 @@
|
||||
[:my-profile
|
||||
: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]}
|
||||
[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)
|
||||
(redirect-to-ens-summary)))
|
||||
|
||||
(fx/defn clear-ens-registration
|
||||
(rf/defn clear-ens-registration
|
||||
{:events [:clear-ens-registration]}
|
||||
[{:keys [db]} tx-hash]
|
||||
{:db (update db :ens/registrations dissoc tx-hash)})
|
||||
|
||||
(fx/defn set-state
|
||||
(rf/defn set-state
|
||||
{:events [::name-resolved]}
|
||||
[{:keys [db]} username state address]
|
||||
(when (= username
|
||||
@@ -76,37 +79,43 @@
|
||||
(assoc-in [:ens/registration :state] state)
|
||||
(assoc-in [:ens/registration :address] address))}))
|
||||
|
||||
(fx/defn save-username
|
||||
(rf/defn save-username
|
||||
{:events [::save-username]}
|
||||
[{:keys [db] :as cofx} custom-domain? username redirectToSummary]
|
||||
(let [name (fullname custom-domain? username)
|
||||
names (get-in db [:multiaccount :usernames] [])
|
||||
(let [name (fullname custom-domain? username)
|
||||
names (get-in db [:multiaccount :usernames] [])
|
||||
new-names (conj names name)]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(multiaccounts.update/multiaccount-update
|
||||
:usernames new-names
|
||||
:usernames
|
||||
new-names
|
||||
(when redirectToSummary
|
||||
{:on-success #(re-frame/dispatch [::redirect-to-ens-summary])}))
|
||||
(when (empty? names)
|
||||
(multiaccounts.update/multiaccount-update
|
||||
:preferred-name name {})))))
|
||||
:preferred-name
|
||||
name
|
||||
{})))))
|
||||
|
||||
(fx/defn set-pub-key
|
||||
(rf/defn set-pub-key
|
||||
{:events [::set-pub-key]}
|
||||
[{:keys [db]}]
|
||||
(let [{:keys [username address custom-domain?]} (:ens/registration db)
|
||||
address (or address (ethereum/default-address db))
|
||||
{:keys [public-key]} (:multiaccount db)
|
||||
chain-id (ethereum/chain-id db)
|
||||
username (fullname custom-domain? username)]
|
||||
address (or address (ethereum/default-address db))
|
||||
{:keys [public-key]} (:multiaccount db)
|
||||
chain-id (ethereum/chain-id db)
|
||||
username (fullname custom-domain? username)]
|
||||
(ens/set-pub-key-prepare-tx
|
||||
chain-id address username public-key
|
||||
chain-id
|
||||
address
|
||||
username
|
||||
public-key
|
||||
#(re-frame/dispatch [:signing.ui/sign
|
||||
{:tx-obj %
|
||||
:on-result [::save-username custom-domain? username true]
|
||||
:on-error [::on-registration-failure]}]))))
|
||||
|
||||
(fx/defn on-input-submitted
|
||||
(rf/defn on-input-submitted
|
||||
{:events [::input-submitted]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(let [{:keys [state username custom-domain?]} (:ens/registration db)]
|
||||
@@ -135,14 +144,16 @@
|
||||
(ens/pubkey
|
||||
chain-id
|
||||
(fullname custom-domain? username)
|
||||
#(re-frame/dispatch [::name-resolved username
|
||||
(cond
|
||||
(not public-key) :owned
|
||||
(and (string/ends-with? % "0000000000000000000000000000000000000000000000000000000000000000") (not custom-domain?))
|
||||
:invalid-ens
|
||||
(= % public-key) :connected
|
||||
:else :connected-with-different-key)
|
||||
(eip55/address->checksum response)]))
|
||||
#(re-frame/dispatch
|
||||
[::name-resolved username
|
||||
(cond
|
||||
(not public-key) :owned
|
||||
(and (string/ends-with? % "0000000000000000000000000000000000000000000000000000000000000000")
|
||||
(not custom-domain?))
|
||||
:invalid-ens
|
||||
(= % public-key) :connected
|
||||
:else :connected-with-different-key)
|
||||
(eip55/address->checksum response)]))
|
||||
:else
|
||||
(re-frame/dispatch [::name-resolved username :taken]))))
|
||||
|
||||
@@ -153,33 +164,40 @@
|
||||
5 10
|
||||
1 10))
|
||||
|
||||
(fx/defn register-name
|
||||
(rf/defn register-name
|
||||
{:events [::register-name-pressed]}
|
||||
[{:keys [db]} address]
|
||||
(let [{:keys [username]}
|
||||
(:ens/registration db)
|
||||
{:keys [public-key]} (:multiaccount db)
|
||||
chain-id (ethereum/chain-id db)]
|
||||
chain-id (ethereum/chain-id db)]
|
||||
(ens/register-prepare-tx
|
||||
chain-id address username public-key
|
||||
chain-id
|
||||
address
|
||||
username
|
||||
public-key
|
||||
#(re-frame/dispatch [:signing.ui/sign
|
||||
{:tx-obj %
|
||||
:on-result [:update-ens-tx-state-and-redirect :submitted username false]
|
||||
:on-error [::on-registration-failure]}]))))
|
||||
|
||||
(defn- valid-custom-domain? [username]
|
||||
(defn- valid-custom-domain?
|
||||
[username]
|
||||
(and (ens/is-valid-eth-name? username)
|
||||
(stateofus/lower-case? username)))
|
||||
|
||||
(defn- valid-username? [custom-domain? username]
|
||||
(defn- valid-username?
|
||||
[custom-domain? username]
|
||||
(if custom-domain?
|
||||
(valid-custom-domain? username)
|
||||
(stateofus/valid-username? username)))
|
||||
|
||||
(defn- state [custom-domain? username usernames]
|
||||
(defn- state
|
||||
[custom-domain? username usernames]
|
||||
(cond
|
||||
(or (string/blank? username)
|
||||
(> 4 (count username))) :too-short
|
||||
(> 4 (count username)))
|
||||
:too-short
|
||||
(valid-username? custom-domain? username)
|
||||
(if (usernames (fullname custom-domain? username))
|
||||
:already-added
|
||||
@@ -189,79 +207,88 @@
|
||||
;;NOTE we want to handle only last resolve
|
||||
(def resolve-last-id (atom nil))
|
||||
|
||||
(fx/defn set-username-candidate
|
||||
(rf/defn set-username-candidate
|
||||
{:events [::set-username-candidate]}
|
||||
[{:keys [db]} username]
|
||||
(let [{:keys [custom-domain?]} (:ens/registration db)
|
||||
usernames (into #{} (get-in db [:multiaccount :usernames]))
|
||||
state (state custom-domain? username usernames)]
|
||||
usernames (into #{} (get-in db [:multiaccount :usernames]))
|
||||
state (state custom-domain? username usernames)]
|
||||
(reset! resolve-last-id (random/id))
|
||||
(merge
|
||||
{:db (update db :ens/registration assoc
|
||||
:username username
|
||||
:state state)}
|
||||
{:db (update db
|
||||
:ens/registration assoc
|
||||
:username username
|
||||
:state state)}
|
||||
(when (= state :searching)
|
||||
(let [{:keys [multiaccount]} db
|
||||
{:keys [public-key]} multiaccount
|
||||
addresses (ethereum/addresses-without-watch db)
|
||||
chain-id (ethereum/chain-id db)]
|
||||
{:keys [public-key]} multiaccount
|
||||
addresses (ethereum/addresses-without-watch db)
|
||||
chain-id (ethereum/chain-id db)]
|
||||
{::resolve-owner [chain-id
|
||||
(fullname custom-domain? username)
|
||||
#(on-resolve-owner
|
||||
chain-id custom-domain? username addresses public-key %
|
||||
resolve-last-id @resolve-last-id)]})))))
|
||||
chain-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]}
|
||||
[{:keys [db] :as cofx} _]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
;; clear registration data
|
||||
{:db (dissoc db :ens/registration)}
|
||||
;; we reset navigation so that navigate back doesn't return
|
||||
;; into the registration flow
|
||||
(navigation/set-stack-root :profile-stack [:my-profile
|
||||
:ens-main])))
|
||||
(navigation/set-stack-root :profile-stack
|
||||
[:my-profile
|
||||
:ens-main])))
|
||||
|
||||
(fx/defn switch-domain-type
|
||||
(rf/defn switch-domain-type
|
||||
{:events [::switch-domain-type]}
|
||||
[{:keys [db] :as cofx} _]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{:db (-> db
|
||||
(update :ens/registration dissoc :username :state)
|
||||
(update-in [:ens/registration :custom-domain?] not))}))
|
||||
|
||||
(fx/defn change-address
|
||||
(rf/defn change-address
|
||||
{:events [::change-address]}
|
||||
[{:keys [db] :as cofx} _ {:keys [address]}]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{:db (assoc-in db [:ens/registration :address] address)}
|
||||
(bottom-sheet/hide-bottom-sheet)))
|
||||
|
||||
(fx/defn save-preferred-name
|
||||
(rf/defn save-preferred-name
|
||||
{:events [::save-preferred-name]}
|
||||
[cofx name]
|
||||
(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
|
||||
it should only be called if the user cancels the signing
|
||||
Actual registration failure has not been implemented properly"
|
||||
{:events [::on-registration-failure]}
|
||||
[_ _])
|
||||
|
||||
(fx/defn store-name-address
|
||||
(rf/defn store-name-address
|
||||
{:events [::address-resolved]}
|
||||
[{:keys [db]} username 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]}
|
||||
[{:keys [db]} username 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]}
|
||||
[{:keys [now db]} username timestamp]
|
||||
{:db (-> db
|
||||
@@ -269,11 +296,11 @@
|
||||
(datetime/timestamp->year-month-day-date timestamp))
|
||||
(assoc-in [:ens/names username :releasable?] (<= timestamp now)))})
|
||||
|
||||
(fx/defn navigate-to-name
|
||||
(rf/defn navigate-to-name
|
||||
{:events [::navigate-to-name]}
|
||||
[{:keys [db] :as cofx} username]
|
||||
(let [chain-id (ethereum/chain-id db)]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{::get-expiration-time
|
||||
[chain-id
|
||||
(stateofus/username username)
|
||||
@@ -286,24 +313,27 @@
|
||||
#(re-frame/dispatch [::public-key-resolved username %])]}
|
||||
(navigation/navigate-to-cofx :ens-name-details username))))
|
||||
|
||||
(fx/defn start-registration
|
||||
(rf/defn start-registration
|
||||
{:events [::add-username-pressed ::get-started-pressed]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(set-username-candidate (get-in db [:ens/registration :username] ""))
|
||||
(navigation/navigate-to-cofx :ens-search {})))
|
||||
|
||||
(fx/defn remove-username
|
||||
(rf/defn remove-username
|
||||
{:events [::remove-username]}
|
||||
[{:keys [db] :as cofx} name]
|
||||
(let [names (get-in db [:multiaccount :usernames] [])
|
||||
preferred-name (get-in db [:multiaccount :preferred-name])
|
||||
new-names (remove #(= name %) names)]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(multiaccounts.update/multiaccount-update
|
||||
:usernames new-names
|
||||
:usernames
|
||||
new-names
|
||||
{})
|
||||
(when (= name preferred-name)
|
||||
(multiaccounts.update/multiaccount-update
|
||||
:preferred-name (first new-names) {}))
|
||||
:preferred-name
|
||||
(first new-names)
|
||||
{}))
|
||||
(navigation/navigate-back))))
|
||||
|
||||
@@ -1,22 +1,23 @@
|
||||
(ns status-im.ethereum.subscriptions
|
||||
(: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.utils.fx :as fx]
|
||||
[taoensso.timbre :as log]))
|
||||
[status-im.wallet.core :as wallet.core]
|
||||
[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]
|
||||
(log/debug "[wallet-subs] new-transfers"
|
||||
"accounts" accounts
|
||||
"block" block-number)
|
||||
"block" block-number)
|
||||
(transactions/check-watched-transactions cofx))
|
||||
|
||||
(fx/defn recent-history-fetching-started
|
||||
(rf/defn recent-history-fetching-started
|
||||
[{:keys [db]} accounts]
|
||||
(log/debug "[wallet-subs] recent-history-fetching-started"
|
||||
"accounts" accounts)
|
||||
"accounts"
|
||||
accounts)
|
||||
(let [event (get db :wallet/on-recent-history-fetching)]
|
||||
(cond-> {:db (-> db
|
||||
(transactions/update-fetching-status accounts :recent? true)
|
||||
@@ -25,20 +26,21 @@
|
||||
event
|
||||
(assoc :dispatch event))))
|
||||
|
||||
(fx/defn recent-history-fetching-ended
|
||||
(rf/defn recent-history-fetching-ended
|
||||
[{:keys [db]} {:keys [accounts blockNumber]}]
|
||||
(log/debug "[wallet-subs] recent-history-fetching-ended"
|
||||
"accounts" accounts
|
||||
"block" blockNumber)
|
||||
{:db (-> db
|
||||
(assoc :ethereum/current-block blockNumber)
|
||||
(update-in [:wallet :accounts]
|
||||
wallet/remove-transactions-since-block blockNumber)
|
||||
(transactions/update-fetching-status accounts :recent? false)
|
||||
(dissoc :wallet/waiting-for-recent-history?
|
||||
:wallet/refreshing-history?
|
||||
:wallet/fetching-error
|
||||
:wallet/recent-history-fetching-started?))
|
||||
"block" blockNumber)
|
||||
{:db (-> db
|
||||
(assoc :ethereum/current-block blockNumber)
|
||||
(update-in [:wallet :accounts]
|
||||
wallet/remove-transactions-since-block
|
||||
blockNumber)
|
||||
(transactions/update-fetching-status accounts :recent? false)
|
||||
(dissoc :wallet/waiting-for-recent-history?
|
||||
:wallet/refreshing-history?
|
||||
:wallet/fetching-error
|
||||
:wallet/recent-history-fetching-started?))
|
||||
:transactions/get-transfers
|
||||
{:chain-tokens (:wallet/all-tokens db)
|
||||
:addresses (reduce
|
||||
@@ -53,27 +55,27 @@
|
||||
:before-block blockNumber
|
||||
:limit 20}})
|
||||
|
||||
(fx/defn fetching-error
|
||||
(rf/defn fetching-error
|
||||
[{:keys [db] :as cofx} {:keys [message]}]
|
||||
(fx/merge
|
||||
(rf/merge
|
||||
cofx
|
||||
{:db (assoc db :wallet/fetching-error message)}
|
||||
{:db (assoc db :wallet/fetching-error message)}
|
||||
(wallet.core/after-checking-history)))
|
||||
|
||||
(fx/defn non-archival-node-detected
|
||||
(rf/defn non-archival-node-detected
|
||||
[{:keys [db]} _]
|
||||
{: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}]
|
||||
(log/info "[wallet-subs] new-wallet-event"
|
||||
"event-type" type
|
||||
"event-type" type
|
||||
"blockNumber" blockNumber
|
||||
"accounts" accounts)
|
||||
"accounts" accounts)
|
||||
(case type
|
||||
"new-transfers" (new-transfers cofx blockNumber accounts)
|
||||
"recent-history-fetching" (recent-history-fetching-started cofx accounts)
|
||||
"recent-history-ready" (recent-history-fetching-ended cofx event)
|
||||
"fetching-history-error" (fetching-error cofx event)
|
||||
"new-transfers" (new-transfers cofx blockNumber accounts)
|
||||
"recent-history-fetching" (recent-history-fetching-started cofx accounts)
|
||||
"recent-history-ready" (recent-history-fetching-ended cofx event)
|
||||
"fetching-history-error" (fetching-error cofx event)
|
||||
"non-archival-node-detected" (non-archival-node-detected cofx event)
|
||||
(log/warn ::unknown-wallet-event :type type :event event)))
|
||||
|
||||
@@ -7,10 +7,10 @@
|
||||
[status-im.ethereum.eip55 :as eip55]
|
||||
[status-im.ethereum.encode :as encode]
|
||||
[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.wallet.core :as wallet]
|
||||
[taoensso.timbre :as log]))
|
||||
[taoensso.timbre :as log]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(def confirmations-count-threshold 12)
|
||||
|
||||
@@ -23,8 +23,9 @@
|
||||
|
||||
(def network->subdomain {5 "goerli"})
|
||||
|
||||
(defn get-transaction-details-url [chain-id hash]
|
||||
{:pre [(number? chain-id) (string? hash)]
|
||||
(defn get-transaction-details-url
|
||||
[chain-id hash]
|
||||
{:pre [(number? chain-id) (string? hash)]
|
||||
:post [(or (nil? %) (string? %))]}
|
||||
(cond
|
||||
(etherscan-supported? chain-id)
|
||||
@@ -51,15 +52,16 @@
|
||||
|
||||
(defn- parse-token-transfer
|
||||
[chain-tokens contract]
|
||||
(let [{:keys [symbol] :as token} (get chain-tokens contract
|
||||
(let [{:keys [symbol] :as token} (get chain-tokens
|
||||
contract
|
||||
default-erc20-token)]
|
||||
{:symbol symbol
|
||||
:token token
|
||||
{:symbol symbol
|
||||
:token token
|
||||
;; NOTE(goranjovic) - just a flag we need when we merge this entry
|
||||
;; with the existing entry in the app, e.g. transaction info with
|
||||
;; gas details, or a previous transfer entry with old confirmations
|
||||
;; count.
|
||||
:transfer true}))
|
||||
:transfer true}))
|
||||
|
||||
(defn enrich-transfer
|
||||
[chain-tokens
|
||||
@@ -105,7 +107,7 @@
|
||||
|
||||
(def default-transfers-limit 20)
|
||||
|
||||
(fx/defn watch-transaction
|
||||
(rf/defn watch-transaction
|
||||
"Set a watch for the given transaction
|
||||
`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
|
||||
@@ -114,10 +116,11 @@
|
||||
[{:keys [db]} transaction-id {:keys [trigger-fn on-trigger] :as watch-params}]
|
||||
(when (and (fn? trigger-fn)
|
||||
(fn? on-trigger))
|
||||
{:db (assoc-in db [:ethereum/watched-transactions transaction-id]
|
||||
watch-params)}))
|
||||
{:db (assoc-in db
|
||||
[: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
|
||||
by `on-trigger` if that is the case"
|
||||
[{:keys [db] :as cofx} {:keys [hash] :as transaction}]
|
||||
@@ -125,12 +128,14 @@
|
||||
(get-in db [:ethereum/watched-transactions hash])]
|
||||
(let [{:keys [trigger-fn on-trigger]} watch-params]
|
||||
(when (trigger-fn db transaction)
|
||||
(fx/merge cofx
|
||||
{:db (update db :ethereum/watched-transactions
|
||||
dissoc hash)}
|
||||
(rf/merge cofx
|
||||
{:db (update db
|
||||
:ethereum/watched-transactions
|
||||
dissoc
|
||||
hash)}
|
||||
(on-trigger transaction))))))
|
||||
|
||||
(fx/defn check-watched-transactions
|
||||
(rf/defn check-watched-transactions
|
||||
[{:keys [db] :as cofx}]
|
||||
(let [watched-transactions
|
||||
(reduce-kv (fn [acc _ {:keys [transactions]}]
|
||||
@@ -139,13 +144,13 @@
|
||||
(keys (get db :ethereum/watched-transactions)))))
|
||||
{}
|
||||
(get-in db [:wallet :accounts]))]
|
||||
(apply fx/merge
|
||||
(apply rf/merge
|
||||
cofx
|
||||
(map (fn [[_ transaction]]
|
||||
(check-transaction transaction))
|
||||
watched-transactions))))
|
||||
|
||||
(fx/defn add-transfer
|
||||
(rf/defn add-transfer
|
||||
"We determine a unique id for the transfer before adding it because some
|
||||
transaction can contain multiple transfers and they would overwrite each other
|
||||
in the transfer map if identified by hash"
|
||||
@@ -157,25 +162,30 @@
|
||||
(:type transfer-by-hash))))
|
||||
id
|
||||
hash))]
|
||||
(fx/merge cofx
|
||||
{:db (assoc-in db [:wallet :accounts address :transactions unique-id]
|
||||
(assoc transfer :hash unique-id))}
|
||||
(rf/merge cofx
|
||||
{:db (assoc-in db
|
||||
[:wallet :accounts address :transactions unique-id]
|
||||
(assoc transfer :hash unique-id))}
|
||||
(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]))
|
||||
|
||||
(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]))
|
||||
|
||||
(defn min-block-transfers-count [db address]
|
||||
(get-in db [:wallet :accounts
|
||||
(eip55/address->checksum address)
|
||||
:min-block-transfers-count]))
|
||||
(defn min-block-transfers-count
|
||||
[db address]
|
||||
(get-in db
|
||||
[: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]
|
||||
(let [checksum (eip55/address->checksum address)
|
||||
(let [checksum (eip55/address->checksum address)
|
||||
{:keys [min-block min-block-transfers-count]}
|
||||
(reduce
|
||||
(fn [{:keys [min-block] :as acc}
|
||||
@@ -198,43 +208,47 @@
|
||||
(min-block-transfers-count db address)}
|
||||
transfers)]
|
||||
(log/debug "[transactions] set-lowest-fetched-block"
|
||||
"address" address
|
||||
"min-block" min-block
|
||||
"address" address
|
||||
"min-block" min-block
|
||||
"min-block-transfers-count" min-block-transfers-count)
|
||||
{:db (update-in db [:wallet :accounts checksum] assoc
|
||||
:min-block min-block
|
||||
{:db (update-in db
|
||||
[:wallet :accounts checksum]
|
||||
assoc
|
||||
:min-block min-block
|
||||
:min-block-transfers-count min-block-transfers-count)}))
|
||||
|
||||
(defn update-fetching-status
|
||||
[db addresses fetching-type state]
|
||||
(update-in
|
||||
db [:wallet :fetching]
|
||||
db
|
||||
[:wallet :fetching]
|
||||
(fn [accounts]
|
||||
(reduce
|
||||
(fn [accounts address]
|
||||
(assoc-in accounts
|
||||
[(eip55/address->checksum address) fetching-type]
|
||||
state))
|
||||
[(eip55/address->checksum address) fetching-type]
|
||||
state))
|
||||
accounts
|
||||
addresses))))
|
||||
|
||||
(fx/defn tx-fetching-in-progress
|
||||
(rf/defn tx-fetching-in-progress
|
||||
[{:keys [db]} addresses]
|
||||
{:db (update-fetching-status db addresses :history? true)})
|
||||
|
||||
(fx/defn tx-fetching-ended
|
||||
(rf/defn tx-fetching-ended
|
||||
[{:keys [db]} addresses]
|
||||
{: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]
|
||||
(let [syncing-allowed? (utils.mobile-sync/syncing-allowed? cofx)]
|
||||
{:db (assoc-in db [:wallet :fetching address :all-fetched?]
|
||||
(if syncing-allowed?
|
||||
:all
|
||||
:all-preloaded))}))
|
||||
{:db (assoc-in db
|
||||
[:wallet :fetching address :all-fetched?]
|
||||
(if syncing-allowed?
|
||||
:all
|
||||
:all-preloaded))}))
|
||||
|
||||
(fx/defn delete-pending-transactions
|
||||
(rf/defn delete-pending-transactions
|
||||
[{:keys [db]} address transactions]
|
||||
(let [all-transactions
|
||||
(get-in db [:wallet :accounts (eip55/address->checksum address) :transactions])
|
||||
@@ -247,38 +261,38 @@
|
||||
transactions)]
|
||||
{: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]}]
|
||||
(log/debug "[transfers] new-transfers"
|
||||
"address" address
|
||||
"count" (count transfers)
|
||||
"limit" limit)
|
||||
(let [checksum (eip55/address->checksum address)
|
||||
"count" (count transfers)
|
||||
"limit" limit)
|
||||
(let [checksum (eip55/address->checksum address)
|
||||
max-known-block (get-max-block-with-transfers db address)
|
||||
effects (cond-> [(when (seq transfers)
|
||||
(set-lowest-fetched-block checksum transfers))
|
||||
(wallet/set-max-block-with-transfers checksum transfers)]
|
||||
effects (cond-> [(when (seq transfers)
|
||||
(set-lowest-fetched-block checksum transfers))
|
||||
(wallet/set-max-block-with-transfers checksum transfers)]
|
||||
|
||||
(seq transfers)
|
||||
(concat
|
||||
[(delete-pending-transactions address transfers)]
|
||||
(mapv add-transfer transfers))
|
||||
(seq transfers)
|
||||
(concat
|
||||
[(delete-pending-transactions address transfers)]
|
||||
(mapv add-transfer transfers))
|
||||
|
||||
(and max-known-block
|
||||
(some #(> (:block %) max-known-block) transfers))
|
||||
(conj (wallet/update-balances
|
||||
[address]
|
||||
(zero? max-known-block)))
|
||||
(and max-known-block
|
||||
(some #(> (:block %) max-known-block) transfers))
|
||||
(conj (wallet/update-balances
|
||||
[address]
|
||||
(zero? max-known-block)))
|
||||
|
||||
(and (zero? max-known-block)
|
||||
(empty? transfers))
|
||||
(conj (wallet/set-zero-balances {:address address}))
|
||||
(and (zero? max-known-block)
|
||||
(empty? transfers))
|
||||
(conj (wallet/set-zero-balances {:address address}))
|
||||
|
||||
(< (count transfers) limit)
|
||||
(conj (tx-history-end-reached checksum)))]
|
||||
(apply fx/merge cofx (tx-fetching-ended [checksum]) effects)))
|
||||
(< (count transfers) limit)
|
||||
(conj (tx-history-end-reached checksum)))]
|
||||
(apply rf/merge cofx (tx-fetching-ended [checksum]) effects)))
|
||||
|
||||
(fx/defn check-ens-transactions
|
||||
(rf/defn check-ens-transactions
|
||||
[{:keys [db] :as cofx} transfers]
|
||||
(let [set-of-transactions-hash (reduce (fn [acc {:keys [hash]}] (conj acc hash)) #{} transfers)
|
||||
registrations (filter
|
||||
@@ -288,12 +302,15 @@
|
||||
(contains? set-of-transactions-hash hash)))
|
||||
(get db :ens/registrations))
|
||||
fxs (map (fn [[hash {:keys [username custom-domain?]}]]
|
||||
(let [transfer (first (filter (fn [transfer] (let [transfer-hash (get transfer :hash)] (= transfer-hash hash))) transfers))
|
||||
type (get transfer :type)
|
||||
(let [transfer (first (filter (fn [transfer]
|
||||
(let [transfer-hash (get transfer :hash)]
|
||||
(= transfer-hash hash)))
|
||||
transfers))
|
||||
type (get transfer :type)
|
||||
transaction-success (get transfer :transfer)]
|
||||
(cond
|
||||
(= transaction-success true)
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(ens/clear-ens-registration hash)
|
||||
(ens/save-username custom-domain? username false))
|
||||
(= type :failed)
|
||||
@@ -301,69 +318,72 @@
|
||||
:else
|
||||
nil)))
|
||||
registrations)]
|
||||
(apply fx/merge cofx fxs)))
|
||||
(apply rf/merge cofx fxs)))
|
||||
|
||||
(fx/defn new-transfers
|
||||
(rf/defn new-transfers
|
||||
{:events [::new-transfers]}
|
||||
[cofx transfers params]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(handle-new-transfer transfers params)
|
||||
(wallet/stop-fetching-on-empty-tx-history transfers)
|
||||
(check-ens-transactions transfers)))
|
||||
|
||||
(fx/defn tx-fetching-failed
|
||||
(rf/defn tx-fetching-failed
|
||||
{:events [::tx-fetching-failed]}
|
||||
[cofx error address]
|
||||
(log/debug "[transactions] tx-fetching-failed"
|
||||
"address" address
|
||||
"error" error)
|
||||
"error" error)
|
||||
(tx-fetching-ended cofx [address]))
|
||||
|
||||
(re-frame/reg-fx
|
||||
:transactions/get-transfers
|
||||
(fn [{:keys [chain-tokens addresses before-block limit
|
||||
limit-per-address fetch-more?]
|
||||
:as params
|
||||
:or {limit default-transfers-limit
|
||||
(fn
|
||||
[{:keys [chain-tokens addresses before-block limit
|
||||
limit-per-address fetch-more?]
|
||||
:as params
|
||||
:or {limit default-transfers-limit
|
||||
fetch-more? true}}]
|
||||
{:pre [(spec/valid?
|
||||
(spec/coll-of string?)
|
||||
addresses)]}
|
||||
(log/debug "[transactions] get-transfers"
|
||||
"addresses" addresses
|
||||
"block" before-block
|
||||
"limit" limit
|
||||
"addresses" addresses
|
||||
"block" before-block
|
||||
"limit" limit
|
||||
"limit-per-address" limit-per-address
|
||||
"fetch-more?" fetch-more?)
|
||||
"fetch-more?" fetch-more?)
|
||||
(doseq [address addresses]
|
||||
(let [limit (or (get limit-per-address address)
|
||||
limit)]
|
||||
(json-rpc/call
|
||||
{:method "wallet_getTransfersByAddress"
|
||||
:params [address (encode/uint before-block) (encode/uint limit) fetch-more?]
|
||||
{:method "wallet_getTransfersByAddress"
|
||||
:params [address (encode/uint before-block) (encode/uint limit) fetch-more?]
|
||||
:on-success #(re-frame/dispatch
|
||||
[::new-transfers
|
||||
(enrich-transfers chain-tokens %)
|
||||
(assoc params :address address
|
||||
:limit limit)])
|
||||
:on-error #(re-frame/dispatch [::tx-fetching-failed address])})))))
|
||||
(assoc params
|
||||
:address 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])))
|
||||
|
||||
(fx/defn fetch-more-tx
|
||||
(rf/defn fetch-more-tx
|
||||
{:events [:transactions/fetch-more]}
|
||||
[{:keys [db] :as cofx} address]
|
||||
(let [min-known-block (or (get-min-known-block db address)
|
||||
(:ethereum/current-block db))
|
||||
(let [min-known-block (or (get-min-known-block db address)
|
||||
(:ethereum/current-block db))
|
||||
min-block-transfers-count (or (min-block-transfers-count db address) 0)]
|
||||
(fx/merge
|
||||
(rf/merge
|
||||
cofx
|
||||
{:transactions/get-transfers
|
||||
{:chain-tokens (:wallet/all-tokens db)
|
||||
:addresses [address]
|
||||
:before-block min-known-block
|
||||
:fetch-more? (utils.mobile-sync/syncing-allowed? cofx)
|
||||
{:chain-tokens (:wallet/all-tokens db)
|
||||
:addresses [address]
|
||||
:before-block min-known-block
|
||||
:fetch-more? (utils.mobile-sync/syncing-allowed? cofx)
|
||||
;; Transfers are requested before and including `min-known-block` because
|
||||
;; there is no guarantee that all transfers from that block are shown
|
||||
;; already. To make sure that we fetch the whole `default-transfers-limit`
|
||||
@@ -373,7 +393,7 @@
|
||||
min-block-transfers-count)}}}
|
||||
(tx-fetching-in-progress [address]))))
|
||||
|
||||
(fx/defn get-fetched-transfers
|
||||
(rf/defn get-fetched-transfers
|
||||
{:events [:transaction/get-fetched-transfers]}
|
||||
[{:keys [db]}]
|
||||
{:transactions/get-transfers
|
||||
|
||||
+137
-122
@@ -1,64 +1,65 @@
|
||||
(ns status-im.events
|
||||
(:require clojure.set
|
||||
[re-frame.core :as re-frame]
|
||||
status-im.add-new.core
|
||||
[status-im.async-storage.core :as async-storage]
|
||||
status-im.backup.core
|
||||
status-im.bootnodes.core
|
||||
status-im.browser.core
|
||||
status-im.browser.permissions
|
||||
[status-im.chat.models :as chat]
|
||||
status-im.chat.models.images
|
||||
status-im.chat.models.input
|
||||
status-im.chat.models.loading
|
||||
status-im.chat.models.transport
|
||||
[status-im.constants :as constants]
|
||||
status-im.contact.block
|
||||
status-im.contact.chat
|
||||
status-im.contact.core
|
||||
status-im.currency.core
|
||||
[status-im.ethereum.json-rpc :as json-rpc]
|
||||
status-im.ethereum.subscriptions
|
||||
status-im.fleet.core
|
||||
status-im.http.core
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
[status-im.keycard.core :as keycard]
|
||||
status-im.log-level.core
|
||||
status-im.mailserver.constants
|
||||
[status-im.mailserver.core :as mailserver]
|
||||
[status-im.multiaccounts.biometric.core :as biometric]
|
||||
status-im.multiaccounts.login.core
|
||||
status-im.multiaccounts.logout.core
|
||||
status-im.multiaccounts.update.core
|
||||
[status-im.native-module.core :as status]
|
||||
[status-im2.navigation.events :as navigation]
|
||||
status-im2.contexts.activity-center.events
|
||||
status-im.pairing.core
|
||||
status-im.profile.core
|
||||
status-im.search.core
|
||||
status-im.signals.core
|
||||
status-im.stickers.core
|
||||
status-im.transport.core
|
||||
[status-im.ui.components.permissions :as permissions]
|
||||
[status-im.ui.components.react :as react]
|
||||
status-im.ui.screens.privacy-and-security-settings.events
|
||||
[status-im.utils.dimensions :as dimensions]
|
||||
[status-im.utils.fx :as fx]
|
||||
status-im.utils.logging.core
|
||||
[status-im.utils.universal-links.core :as universal-links]
|
||||
[status-im.utils.utils :as utils]
|
||||
status-im.visibility-status-updates.core
|
||||
status-im.waku.core
|
||||
status-im.wallet.accounts.core
|
||||
status-im.wallet.choose-recipient.core
|
||||
[status-im.wallet.core :as wallet]
|
||||
status-im.wallet.custom-tokens.core
|
||||
status-im.wallet-connect.core
|
||||
status-im.wallet-connect-legacy.core
|
||||
status-im.network.net-info
|
||||
status-im.visibility-status-popover.core
|
||||
status-im2.contexts.shell.events
|
||||
[status-im.multiaccounts.model :as multiaccounts.model]))
|
||||
(:require
|
||||
clojure.set
|
||||
[re-frame.core :as re-frame]
|
||||
status-im.add-new.core
|
||||
[status-im.async-storage.core :as async-storage]
|
||||
status-im.backup.core
|
||||
status-im.bootnodes.core
|
||||
status-im.browser.core
|
||||
status-im.browser.permissions
|
||||
[status-im.chat.models :as chat]
|
||||
status-im.chat.models.images
|
||||
status-im.chat.models.input
|
||||
status-im.chat.models.loading
|
||||
status-im.chat.models.transport
|
||||
[status-im.constants :as constants]
|
||||
status-im.contact.block
|
||||
status-im.contact.chat
|
||||
status-im.contact.core
|
||||
status-im.currency.core
|
||||
[status-im.ethereum.json-rpc :as json-rpc]
|
||||
status-im.ethereum.subscriptions
|
||||
status-im.fleet.core
|
||||
status-im.http.core
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
[status-im.keycard.core :as keycard]
|
||||
status-im.log-level.core
|
||||
status-im.mailserver.constants
|
||||
[status-im.mailserver.core :as mailserver]
|
||||
[status-im.multiaccounts.biometric.core :as biometric]
|
||||
status-im.multiaccounts.login.core
|
||||
status-im.multiaccounts.logout.core
|
||||
[status-im.multiaccounts.model :as multiaccounts.model]
|
||||
status-im.multiaccounts.update.core
|
||||
[status-im.native-module.core :as status]
|
||||
status-im.network.net-info
|
||||
status-im.pairing.core
|
||||
status-im.profile.core
|
||||
status-im.search.core
|
||||
status-im.signals.core
|
||||
status-im.stickers.core
|
||||
status-im.transport.core
|
||||
[status-im.ui.components.permissions :as permissions]
|
||||
[status-im.ui.components.react :as react]
|
||||
status-im.ui.screens.privacy-and-security-settings.events
|
||||
[status-im.utils.dimensions :as dimensions]
|
||||
status-im.utils.logging.core
|
||||
[status-im.utils.universal-links.core :as universal-links]
|
||||
[status-im.utils.utils :as utils]
|
||||
status-im.visibility-status-popover.core
|
||||
status-im.visibility-status-updates.core
|
||||
status-im.waku.core
|
||||
status-im.wallet-connect-legacy.core
|
||||
status-im.wallet-connect.core
|
||||
status-im.wallet.accounts.core
|
||||
status-im.wallet.choose-recipient.core
|
||||
[status-im.wallet.core :as wallet]
|
||||
status-im.wallet.custom-tokens.core
|
||||
status-im2.contexts.activity-center.events
|
||||
status-im2.contexts.shell.events
|
||||
[status-im2.navigation.events :as navigation]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(re-frame/reg-fx
|
||||
:dismiss-keyboard
|
||||
@@ -95,53 +96,58 @@
|
||||
(fn []
|
||||
(dimensions/add-event-listener)))
|
||||
|
||||
(fx/defn dismiss-keyboard
|
||||
(rf/defn dismiss-keyboard
|
||||
{:events [:dismiss-keyboard]}
|
||||
[_]
|
||||
{:dismiss-keyboard nil})
|
||||
|
||||
(fx/defn identicon-generated
|
||||
(rf/defn identicon-generated
|
||||
{:events [:identicon-generated]}
|
||||
[{:keys [db]} path identicon]
|
||||
{:db (assoc-in db path identicon)})
|
||||
|
||||
(fx/defn gfycat-generated
|
||||
(rf/defn gfycat-generated
|
||||
{:events [:gfycat-generated]}
|
||||
[{:keys [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]}
|
||||
[cofx _]
|
||||
(when (multiaccounts.model/logged-in? cofx)
|
||||
{:multiaccounts.ui/switch-theme (get-in cofx [:db :multiaccount :appearance])
|
||||
:dispatch [:reload-new-ui]}))
|
||||
:dispatch [:reload-new-ui]}))
|
||||
|
||||
(def authentication-options
|
||||
{: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
|
||||
(if (= bioauth-code "USER_FALLBACK")
|
||||
(re-frame/dispatch [:multiaccounts.logout.ui/logout-confirmed])
|
||||
(utils/show-confirmation {:title (i18n/label :t/biometric-auth-confirm-title)
|
||||
:content (or bioauth-message (i18n/label :t/biometric-auth-confirm-message))
|
||||
:confirm-button-text (i18n/label :t/biometric-auth-confirm-try-again)
|
||||
:cancel-button-text (i18n/label :t/biometric-auth-confirm-logout)
|
||||
:on-accept #(biometric/authenticate nil on-biometric-auth-result authentication-options)
|
||||
:on-cancel #(re-frame/dispatch [:multiaccounts.logout.ui/logout-confirmed])}))))
|
||||
(utils/show-confirmation
|
||||
{:title (i18n/label :t/biometric-auth-confirm-title)
|
||||
:content (or bioauth-message (i18n/label :t/biometric-auth-confirm-message))
|
||||
:confirm-button-text (i18n/label :t/biometric-auth-confirm-try-again)
|
||||
:cancel-button-text (i18n/label :t/biometric-auth-confirm-logout)
|
||||
: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)
|
||||
signed-up? (get-in db [:multiaccount :signed-up?])
|
||||
biometric-auth? (= (:auth-method db) "biometric")
|
||||
requires-bio-auth (and
|
||||
signed-up?
|
||||
biometric-auth?
|
||||
(some? app-in-background-since)
|
||||
(>= (- now app-in-background-since)
|
||||
constants/ms-in-bg-for-require-bioauth))]
|
||||
(fx/merge cofx
|
||||
signed-up? (get-in db [:multiaccount :signed-up?])
|
||||
biometric-auth? (= (:auth-method db) "biometric")
|
||||
requires-bio-auth (and
|
||||
signed-up?
|
||||
biometric-auth?
|
||||
(some? app-in-background-since)
|
||||
(>= (- now app-in-background-since)
|
||||
constants/ms-in-bg-for-require-bioauth))]
|
||||
(rf/merge cofx
|
||||
{:db (dissoc db :app-in-background-since)}
|
||||
(mailserver/process-next-messages-request)
|
||||
(wallet/restart-wallet-service-after-background app-in-background-since)
|
||||
@@ -151,17 +157,17 @@
|
||||
#(when requires-bio-auth
|
||||
(biometric/authenticate % on-biometric-auth-result authentication-options)))))
|
||||
|
||||
(fx/defn on-going-in-background
|
||||
(rf/defn on-going-in-background
|
||||
[{:keys [db now]}]
|
||||
{:db (assoc db :app-in-background-since now)
|
||||
:dispatch-n [[:audio-recorder/on-background] [:audio-message/on-background]]})
|
||||
|
||||
(fx/defn app-state-change
|
||||
(rf/defn app-state-change
|
||||
{:events [:app-state-change]}
|
||||
[{:keys [db] :as cofx} state]
|
||||
(let [app-coming-from-background? (= state "active")
|
||||
app-going-in-background? (= state "background")]
|
||||
(fx/merge cofx
|
||||
app-going-in-background? (= state "background")]
|
||||
(rf/merge cofx
|
||||
{::app-state-change-fx state
|
||||
:db (assoc db :app-state state)}
|
||||
#(when app-coming-from-background?
|
||||
@@ -169,31 +175,31 @@
|
||||
#(when app-going-in-background?
|
||||
(on-going-in-background %)))))
|
||||
|
||||
(fx/defn request-permissions
|
||||
(rf/defn request-permissions
|
||||
{:events [:request-permissions]}
|
||||
[_ options]
|
||||
{:request-permissions-fx options})
|
||||
|
||||
(fx/defn update-window-dimensions
|
||||
(rf/defn update-window-dimensions
|
||||
{:events [:update-window-dimensions]}
|
||||
[{:keys [db]} dimensions]
|
||||
{:db (assoc db :dimensions/window (dimensions/window dimensions))})
|
||||
|
||||
(fx/defn init-timeline-chat
|
||||
(rf/defn init-timeline-chat
|
||||
{:events [:init-timeline-chat]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(when-not (get-in db [:pagination-info constants/timeline-chat-id :messages-initialized?])
|
||||
(chat/preload-chat-data cofx constants/timeline-chat-id)))
|
||||
|
||||
(fx/defn on-will-focus
|
||||
(rf/defn on-will-focus
|
||||
{:events [:screens/on-will-focus]}
|
||||
[{:keys [db] :as cofx} view-id]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(cond
|
||||
(= :chat view-id)
|
||||
{::async-storage/set! {:chat-id (get-in cofx [:db :current-chat-id])
|
||||
: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)
|
||||
{}
|
||||
@@ -204,47 +210,49 @@
|
||||
:else
|
||||
{::async-storage/set! {:chat-id nil
|
||||
:key-uid nil}
|
||||
:db (assoc db :screens/was-focused-once? true)})
|
||||
:db (assoc db :screens/was-focused-once? true)})
|
||||
#(case view-id
|
||||
:keycard-settings (keycard/settings-screen-did-load %)
|
||||
:reset-card (keycard/reset-card-screen-did-load %)
|
||||
:enter-pin-settings (keycard/enter-pin-screen-did-load %)
|
||||
:keycard-login-pin (keycard/login-pin-screen-did-load %)
|
||||
:add-new-account-pin (keycard/enter-pin-screen-did-load %)
|
||||
:keycard-settings (keycard/settings-screen-did-load %)
|
||||
:reset-card (keycard/reset-card-screen-did-load %)
|
||||
:enter-pin-settings (keycard/enter-pin-screen-did-load %)
|
||||
:keycard-login-pin (keycard/login-pin-screen-did-load %)
|
||||
:add-new-account-pin (keycard/enter-pin-screen-did-load %)
|
||||
:keycard-authentication-method (keycard/authentication-method-screen-did-load %)
|
||||
:multiaccounts (keycard/multiaccounts-screen-did-load %)
|
||||
:wallet (wallet/wallet-will-focus %)
|
||||
:multiaccounts (keycard/multiaccounts-screen-did-load %)
|
||||
:wallet (wallet/wallet-will-focus %)
|
||||
nil)))
|
||||
|
||||
;;TODO :replace by named events
|
||||
(fx/defn set-event
|
||||
(rf/defn set-event
|
||||
{:events [:set]}
|
||||
[{:keys [db]} k v]
|
||||
{:db (assoc db k v)})
|
||||
|
||||
(fx/defn set-view-id
|
||||
(rf/defn set-view-id
|
||||
{:events [:set-view-id]}
|
||||
[{:keys [db]} view-id]
|
||||
{:db (assoc db :view-id view-id)})
|
||||
|
||||
;;TODO :replace by named events
|
||||
(fx/defn set-once-event
|
||||
(rf/defn set-once-event
|
||||
{:events [:set-once]}
|
||||
[{:keys [db]} k v]
|
||||
(when-not (get db k)
|
||||
{:db (assoc db k v)}))
|
||||
|
||||
;;TODO :replace by named events
|
||||
(fx/defn set-in-event
|
||||
(rf/defn set-in-event
|
||||
{:events [:set-in]}
|
||||
[{:keys [db]} path v]
|
||||
{:db (assoc-in db path v)})
|
||||
|
||||
(defn on-ramp<-rpc [on-ramp]
|
||||
(clojure.set/rename-keys on-ramp {:logoUrl :logo-url
|
||||
:siteUrl :site-url}))
|
||||
(defn on-ramp<-rpc
|
||||
[on-ramp]
|
||||
(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]}
|
||||
[{:keys [db]} on-ramps]
|
||||
{:db (assoc
|
||||
@@ -252,7 +260,7 @@
|
||||
:buy-crypto/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]}
|
||||
[_]
|
||||
{::json-rpc/call [{:method "wallet_getCryptoOnRamps"
|
||||
@@ -260,10 +268,10 @@
|
||||
:on-success (fn [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]}
|
||||
[cofx]
|
||||
(fx/merge
|
||||
(rf/merge
|
||||
cofx
|
||||
(navigation/open-modal :buy-crypto nil)
|
||||
(wallet/keep-watching-history)))
|
||||
@@ -275,38 +283,45 @@
|
||||
: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?
|
||||
(hash id)
|
||||
(hash (str public-key id))))
|
||||
|
||||
(fx/defn close-information-box
|
||||
(rf/defn close-information-box
|
||||
{:events [:close-information-box]}
|
||||
[{:keys [db]} id global?]
|
||||
(let [public-key (get-in db [:multiaccount :public-key])
|
||||
hash (information-box-id-hash id public-key global?)]
|
||||
{::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]}
|
||||
[{:keys [db]} hashes states]
|
||||
{:db (assoc db :information-box-states (reduce
|
||||
(fn [acc [id hash]]
|
||||
(assoc acc id (get states hash)))
|
||||
{} hashes))})
|
||||
{:db (assoc db
|
||||
:information-box-states
|
||||
(reduce
|
||||
(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]}
|
||||
[{:keys [db]}]
|
||||
(let [public-key (get-in db [:multiaccount :public-key])
|
||||
{:keys [keys hashes]} (reduce (fn [acc {:keys [id global?]}]
|
||||
(let [hash (information-box-id-hash
|
||||
id public-key global?)]
|
||||
id
|
||||
public-key
|
||||
global?)]
|
||||
(-> acc
|
||||
(assoc-in [:hashes id] hash)
|
||||
(update :keys #(conj % hash)))))
|
||||
{} closable-information-boxes)]
|
||||
{}
|
||||
closable-information-boxes)]
|
||||
{::async-storage/get {:keys keys
|
||||
:cb #(re-frame/dispatch
|
||||
[:information-box-states-loaded hashes %])}}))
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
[status-im.multiaccounts.update.core :as multiaccounts.update]
|
||||
[status-im.node.core :as node]
|
||||
[status-im.utils.config :as config]
|
||||
[status-im.utils.fx :as fx]))
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(defn current-fleet-sub
|
||||
[multiaccount]
|
||||
@@ -14,10 +14,10 @@
|
||||
|
||||
(defn format-mailserver
|
||||
[mailserver address]
|
||||
{:id mailserver
|
||||
:name (name mailserver)
|
||||
{:id mailserver
|
||||
:name (name mailserver)
|
||||
:password constants/mailserver-password
|
||||
:address address})
|
||||
:address address})
|
||||
|
||||
(defn format-mailservers
|
||||
[mailservers]
|
||||
@@ -26,13 +26,14 @@
|
||||
{}
|
||||
mailservers))
|
||||
|
||||
(defn default-mailservers [db]
|
||||
(defn default-mailservers
|
||||
[db]
|
||||
(reduce (fn [acc [fleet node-by-type]]
|
||||
(assoc acc fleet (format-mailservers (:mail node-by-type))))
|
||||
{}
|
||||
(node/fleets db)))
|
||||
|
||||
(fx/defn show-save-confirmation
|
||||
(rf/defn show-save-confirmation
|
||||
{:events [:fleet.ui/fleet-selected]}
|
||||
[_ fleet]
|
||||
{:ui/show-confirmation
|
||||
@@ -44,7 +45,8 @@
|
||||
#(re-frame/dispatch [:fleet.ui/save-fleet-confirmed (keyword fleet)])
|
||||
:on-cancel nil}})
|
||||
|
||||
(defn nodes->fleet [nodes]
|
||||
(defn nodes->fleet
|
||||
[nodes]
|
||||
(letfn [(format-nodes [nodes]
|
||||
(reduce (fn [acc n]
|
||||
(assoc acc
|
||||
@@ -52,28 +54,30 @@
|
||||
n))
|
||||
{}
|
||||
nodes))]
|
||||
{:boot (format-nodes nodes)
|
||||
:mail (format-nodes nodes)
|
||||
{:boot (format-nodes nodes)
|
||||
:mail (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
|
||||
(assoc-in [:custom-fleets fleet] (nodes->fleet nodes))
|
||||
(assoc-in [:mailserver/mailservers fleet] (format-mailservers
|
||||
(reduce
|
||||
(fn [acc e]
|
||||
(assoc acc
|
||||
(keyword e)
|
||||
e))
|
||||
{}
|
||||
nodes))))})
|
||||
(assoc-in [:mailserver/mailservers fleet]
|
||||
(format-mailservers
|
||||
(reduce
|
||||
(fn [acc e]
|
||||
(assoc acc
|
||||
(keyword e)
|
||||
e))
|
||||
{}
|
||||
nodes))))})
|
||||
|
||||
(fx/defn save
|
||||
(rf/defn save
|
||||
{:events [:fleet.ui/save-fleet-confirmed]}
|
||||
[{:keys [db now] :as cofx} fleet]
|
||||
(let [old-fleet (get-in db [:multiaccount :fleet])]
|
||||
(when (not= fleet old-fleet)
|
||||
(fx/merge
|
||||
(rf/merge
|
||||
cofx
|
||||
(multiaccounts.update/multiaccount-update :fleet fleet {})
|
||||
(node/prepare-new-config
|
||||
|
||||
@@ -4,35 +4,36 @@
|
||||
[clojure.string :as string]
|
||||
[re-frame.core :as re-frame]
|
||||
[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.ethereum.json-rpc :as json-rpc]
|
||||
[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]}
|
||||
[cofx chat-id]
|
||||
(when (get-in cofx [:db :chats chat-id])
|
||||
(models.chat/navigate-to-chat cofx chat-id)))
|
||||
|
||||
(fx/defn handle-chat-removed
|
||||
(rf/defn handle-chat-removed
|
||||
{:events [:chat-removed]}
|
||||
[cofx response]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{:db (dissoc (:db cofx) :current-chat-id)
|
||||
:dispatch-n [[:sanitize-messages-and-process-response response]
|
||||
[:pop-to-root-tab :chat-stack]]}
|
||||
(activity-center/notifications-fetch-unread-count)))
|
||||
|
||||
(fx/defn handle-chat-update
|
||||
(rf/defn handle-chat-update
|
||||
{:events [:chat-updated]}
|
||||
[_ response do-not-navigate?]
|
||||
{: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"
|
||||
{:events [:group-chats.ui/remove-member-pressed]}
|
||||
[_ chat-id member do-not-navigate?]
|
||||
@@ -41,7 +42,7 @@
|
||||
:js-response true
|
||||
:on-success #(re-frame/dispatch [:chat-updated % do-not-navigate?])}]})
|
||||
|
||||
(fx/defn join-chat
|
||||
(rf/defn join-chat
|
||||
{:events [:group-chats.ui/join-pressed]}
|
||||
[_ chat-id]
|
||||
{::json-rpc/call [{:method "wakuext_confirmJoiningGroup"
|
||||
@@ -49,7 +50,7 @@
|
||||
:js-response true
|
||||
:on-success #(re-frame/dispatch [:chat-updated %])}]})
|
||||
|
||||
(fx/defn create
|
||||
(rf/defn create
|
||||
{:events [:group-chats.ui/create-pressed]
|
||||
:interceptors [(re-frame/inject-cofx :random-guid-generator)]}
|
||||
[{:keys [db] :as cofx} group-name]
|
||||
@@ -59,7 +60,7 @@
|
||||
:js-response true
|
||||
: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]}]
|
||||
(if (get-in cofx [:db :chats chat-id])
|
||||
{:dispatch [:chat.ui/navigate-to-chat chat-id]}
|
||||
@@ -68,7 +69,7 @@
|
||||
:js-response true
|
||||
:on-success #(re-frame/dispatch [:chat-updated %])}]}))
|
||||
|
||||
(fx/defn make-admin
|
||||
(rf/defn make-admin
|
||||
{:events [:group-chats.ui/make-admin-pressed]}
|
||||
[_ chat-id member]
|
||||
{::json-rpc/call [{:method "wakuext_addAdminsToGroupChat"
|
||||
@@ -76,7 +77,7 @@
|
||||
:js-response true
|
||||
:on-success #(re-frame/dispatch [:chat-updated %])}]})
|
||||
|
||||
(fx/defn add-members
|
||||
(rf/defn add-members
|
||||
"Add members to a group chat"
|
||||
{:events [:group-chats.ui/add-members-pressed]}
|
||||
[{{:keys [current-chat-id selected-participants]} :db :as cofx}]
|
||||
@@ -85,7 +86,7 @@
|
||||
:js-response true
|
||||
: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"
|
||||
{:events [:group-chats.ui/add-members-from-invitation]}
|
||||
[{{:keys [current-chat-id] :as db} :db :as cofx} id participant]
|
||||
@@ -95,7 +96,7 @@
|
||||
:js-response true
|
||||
:on-success #(re-frame/dispatch [:chat-updated %])}]})
|
||||
|
||||
(fx/defn leave
|
||||
(rf/defn leave
|
||||
"Leave chat"
|
||||
{:events [:group-chats.ui/leave-chat-confirmed]}
|
||||
[{:keys [db] :as cofx} chat-id]
|
||||
@@ -104,21 +105,22 @@
|
||||
:js-response true
|
||||
:on-success #(re-frame/dispatch [:chat-removed %])}]})
|
||||
|
||||
(fx/defn remove
|
||||
(rf/defn remove
|
||||
"Remove chat"
|
||||
{:events [:group-chats.ui/remove-chat-confirmed]}
|
||||
[cofx chat-id]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(models.chat/deactivate-chat chat-id)
|
||||
(navigation/pop-to-root-tab :chat-stack)))
|
||||
|
||||
(def not-blank?
|
||||
(complement string/blank?))
|
||||
|
||||
(defn- valid-name? [name]
|
||||
(defn- valid-name?
|
||||
[name]
|
||||
(spec/valid? not-blank? name))
|
||||
|
||||
(fx/defn name-changed
|
||||
(rf/defn name-changed
|
||||
"Save chat from edited profile"
|
||||
{:events [:group-chats.ui/name-changed]}
|
||||
[{:keys [db] :as cofx} chat-id new-name]
|
||||
@@ -129,29 +131,29 @@
|
||||
:js-response true
|
||||
:on-success #(re-frame/dispatch [:chat-updated %])}]}))
|
||||
|
||||
(fx/defn membership-retry
|
||||
(rf/defn membership-retry
|
||||
{:events [:group-chats.ui/membership-retry]}
|
||||
[{{:keys [current-chat-id] :as db} :db}]
|
||||
{: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]}
|
||||
[{{:keys [current-chat-id] :as db} :db} 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"
|
||||
{:events [:send-group-chat-membership-request]}
|
||||
[{{:keys [current-chat-id chats] :as db} :db :as cofx}]
|
||||
(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)
|
||||
::json-rpc/call [{:method "wakuext_sendGroupChatInvitationRequest"
|
||||
:params [nil current-chat-id invitation-admin message]
|
||||
:js-response true
|
||||
: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"
|
||||
{:events [:send-group-chat-membership-rejection]}
|
||||
[cofx invitation-id]
|
||||
@@ -160,14 +162,17 @@
|
||||
:js-response true
|
||||
:on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %])}]})
|
||||
|
||||
(fx/defn handle-invitations
|
||||
(rf/defn handle-invitations
|
||||
[{db :db} invitations]
|
||||
{:db (update db :group-chat/invitations #(reduce (fn [acc {:keys [id] :as inv}]
|
||||
(assoc acc id inv))
|
||||
%
|
||||
invitations))})
|
||||
{:db (update db
|
||||
:group-chat/invitations
|
||||
#(reduce (fn [acc {:keys [id] :as inv}]
|
||||
(assoc acc id inv))
|
||||
%
|
||||
invitations))})
|
||||
|
||||
(defn member-removed? [{:keys [membership-update-events]} pk]
|
||||
(defn member-removed?
|
||||
[{:keys [membership-update-events]} pk]
|
||||
(->> membership-update-events
|
||||
(filter #(contains? (set (:members %)) pk))
|
||||
(sort-by :clockValue >)
|
||||
@@ -175,41 +180,41 @@
|
||||
:type
|
||||
(= constants/invitation-state-removed)))
|
||||
|
||||
(fx/defn deselect-contact
|
||||
(rf/defn deselect-contact
|
||||
{:events [:deselect-contact]}
|
||||
[{:keys [db]} id]
|
||||
{:db (update db :group/selected-contacts disj id)})
|
||||
|
||||
(fx/defn select-contact
|
||||
(rf/defn select-contact
|
||||
{:events [:select-contact]}
|
||||
[{:keys [db]} id]
|
||||
{:db (update db :group/selected-contacts conj id)})
|
||||
|
||||
(fx/defn deselect-participant
|
||||
(rf/defn deselect-participant
|
||||
{:events [:deselect-participant]}
|
||||
[{:keys [db]} id]
|
||||
{:db (update db :selected-participants disj id)})
|
||||
|
||||
(fx/defn select-participant
|
||||
(rf/defn select-participant
|
||||
{:events [:select-participant]}
|
||||
[{:keys [db]} 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]}
|
||||
[{db :db}]
|
||||
{:db (assoc db :selected-participants #{})})
|
||||
|
||||
(fx/defn show-group-chat-profile
|
||||
(rf/defn show-group-chat-profile
|
||||
{:events [:show-group-chat-profile]}
|
||||
[{:keys [db] :as cofx} chat-id]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{:db (-> db
|
||||
(assoc :new-chat-name (get-in db [:chats chat-id :name]))
|
||||
(assoc :current-chat-id chat-id))}
|
||||
(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]}
|
||||
[{:keys [db]} chat-id]
|
||||
(let [chat-name (get-in db [:chats chat-id :name])]
|
||||
|
||||
@@ -1,69 +1,72 @@
|
||||
(ns status-im.keycard.backup-key
|
||||
(:require [status-im.utils.fx :as fx]
|
||||
[re-frame.core :as re-frame]
|
||||
[status-im.utils.utils :as utils]
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
(:require [re-frame.core :as re-frame]
|
||||
[status-im.ethereum.mnemonic :as mnemonic]
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
[status-im.keycard.common :as common]
|
||||
[status-im.multiaccounts.recover.core :as multiaccounts.recover]
|
||||
[status-im2.navigation.events :as navigation]
|
||||
[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]}
|
||||
[{:keys [db] :as cofx} backup-type]
|
||||
(log/debug "[keycard] start backup")
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{:db (-> db
|
||||
(assoc-in [:keycard :creating-backup?] backup-type))}
|
||||
(when (:multiaccount db)
|
||||
(navigation/change-tab :profile))
|
||||
(navigation/navigate-to-cofx :seed-phrase nil)))
|
||||
|
||||
(fx/defn recovery-card-pressed
|
||||
(rf/defn recovery-card-pressed
|
||||
{:events [:keycard-settings.ui/recovery-card-pressed]}
|
||||
[{:keys [db] :as cofx} show-warning]
|
||||
(fx/merge cofx
|
||||
{:db (-> db
|
||||
;setting pin-retry-counter is a workaround for the way the PIN view decides if it should accept PUK or PIN
|
||||
(assoc-in [:keycard :application-info :pin-retry-counter] 3)
|
||||
(assoc-in [:keycard :factory-reset-card?] true)
|
||||
(dissoc :popover/popover))
|
||||
(rf/merge cofx
|
||||
{:db (-> db
|
||||
;setting pin-retry-counter is a workaround for the way the PIN view
|
||||
;decides if it should accept PUK or PIN
|
||||
(assoc-in [:keycard :application-info :pin-retry-counter] 3)
|
||||
(assoc-in [:keycard :factory-reset-card?] true)
|
||||
(dissoc :popover/popover))
|
||||
:hide-popover nil}
|
||||
(signing.core/discard)
|
||||
(if show-warning
|
||||
(utils/show-confirmation {:title (i18n/label :t/keycard-recover-title)
|
||||
:content (i18n/label :t/keycard-recover-text)
|
||||
:confirm-button-text (i18n/label :t/yes)
|
||||
:cancel-button-text (i18n/label :t/no)
|
||||
:on-accept #(re-frame/dispatch [:keycard-settings.ui/backup-card-pressed :recovery-card])
|
||||
:on-cancel #()})
|
||||
(utils/show-confirmation
|
||||
{:title (i18n/label :t/keycard-recover-title)
|
||||
:content (i18n/label :t/keycard-recover-text)
|
||||
:confirm-button-text (i18n/label :t/yes)
|
||||
:cancel-button-text (i18n/label :t/no)
|
||||
:on-accept #(re-frame/dispatch [:keycard-settings.ui/backup-card-pressed
|
||||
:recovery-card])
|
||||
:on-cancel #()})
|
||||
(backup-card-pressed :recovery-card))))
|
||||
|
||||
(fx/defn start-keycard-backup
|
||||
(rf/defn start-keycard-backup
|
||||
{:events [::start-keycard-backup]}
|
||||
[{:keys [db] :as cofx}]
|
||||
{::multiaccounts.recover/import-multiaccount {:passphrase (-> db
|
||||
:multiaccounts/key-storage
|
||||
:seed-phrase
|
||||
mnemonic/sanitize-passphrase)
|
||||
:password nil
|
||||
{::multiaccounts.recover/import-multiaccount {:passphrase (-> db
|
||||
:multiaccounts/key-storage
|
||||
:seed-phrase
|
||||
mnemonic/sanitize-passphrase)
|
||||
:password nil
|
||||
:success-event ::create-backup-card}})
|
||||
|
||||
(fx/defn create-backup-card
|
||||
(rf/defn create-backup-card
|
||||
{:events [::create-backup-card]}
|
||||
[{:keys [db] :as cofx} root-data derived-data]
|
||||
(fx/merge cofx
|
||||
{:db (-> db
|
||||
(update :intro-wizard
|
||||
assoc
|
||||
:root-key root-data
|
||||
:derived derived-data
|
||||
:recovering? true
|
||||
:selected-storage-type :advanced)
|
||||
(assoc-in [:keycard :flow] :recovery)
|
||||
(update :multiaccounts/key-storage dissoc :seed-phrase))
|
||||
(rf/merge cofx
|
||||
{:db (-> db
|
||||
(update :intro-wizard
|
||||
assoc
|
||||
:root-key root-data
|
||||
:derived derived-data
|
||||
:recovering? true
|
||||
:selected-storage-type :advanced)
|
||||
(assoc-in [:keycard :flow] :recovery)
|
||||
(update :multiaccounts/key-storage dissoc :seed-phrase))
|
||||
:dismiss-keyboard nil}
|
||||
(common/listen-to-hardware-back-button)
|
||||
(navigation/navigate-to-cofx :keycard-onboarding-intro nil)))
|
||||
|
||||
@@ -1,71 +1,74 @@
|
||||
(ns status-im.keycard.change-pin
|
||||
(: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.onboarding :as onboarding]
|
||||
[status-im2.navigation.events :as navigation]
|
||||
[taoensso.timbre :as log]
|
||||
[utils.re-frame :as rf]
|
||||
[utils.security.core :as security]))
|
||||
|
||||
(fx/defn change-credentials-pressed
|
||||
(rf/defn change-credentials-pressed
|
||||
{:events [:keycard-settings.ui/change-credentials-pressed]}
|
||||
[{:keys [db] :as cofx} changing]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{:db
|
||||
(assoc-in db [:keycard :pin] {:enter-step :current
|
||||
:current []
|
||||
:puk []
|
||||
:original []
|
||||
:confirmation []
|
||||
:puk-original []
|
||||
:puk-confirmation []
|
||||
:status nil
|
||||
:error-label nil
|
||||
:on-verified (case changing
|
||||
:pin :keycard/proceed-to-change-pin
|
||||
:puk :keycard/proceed-to-change-puk
|
||||
:pairing :keycard/proceed-to-change-pairing)})}
|
||||
(assoc-in db
|
||||
[:keycard :pin]
|
||||
{:enter-step :current
|
||||
:current []
|
||||
:puk []
|
||||
:original []
|
||||
:confirmation []
|
||||
:puk-original []
|
||||
:puk-confirmation []
|
||||
:status nil
|
||||
:error-label nil
|
||||
:on-verified (case changing
|
||||
:pin :keycard/proceed-to-change-pin
|
||||
:puk :keycard/proceed-to-change-puk
|
||||
:pairing :keycard/proceed-to-change-pairing)})}
|
||||
(common/navigate-to-enter-pin-screen)))
|
||||
|
||||
(fx/defn proceed-to-change-pin
|
||||
(rf/defn proceed-to-change-pin
|
||||
{:events [:keycard/proceed-to-change-pin]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{:db (-> db
|
||||
(assoc-in [:keycard :pin :enter-step] :original)
|
||||
(assoc-in [:keycard :pin :status] 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]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{:db (-> db
|
||||
(assoc-in [:keycard :pin :enter-step] :puk-original)
|
||||
(assoc-in [:keycard :pin :status] 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]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(navigation/navigate-replace cofx :change-pairing-code nil))
|
||||
|
||||
(fx/defn discard-pin-change
|
||||
(rf/defn discard-pin-change
|
||||
{:events [::on-cancel]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(common/clear-pin)
|
||||
(common/hide-connection-sheet)
|
||||
(if (get-in db [:keycard :pin :puk-restore?])
|
||||
(navigation/navigate-to-cofx :multiaccounts nil)
|
||||
(navigation/set-stack-root :profile-stack [:my-profile :keycard-settings]))))
|
||||
|
||||
(fx/defn change-pin
|
||||
(rf/defn change-pin
|
||||
{:events [:keycard/change-pin]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(let [setup-step (get-in db [:keycard :setup-step])]
|
||||
(log/debug "[keycard] change-pin"
|
||||
"setup-step" setup-step)
|
||||
"setup-step"
|
||||
setup-step)
|
||||
(if (= setup-step :pin)
|
||||
(onboarding/load-preparing-screen cofx)
|
||||
(common/show-connection-sheet
|
||||
@@ -78,15 +81,15 @@
|
||||
(get-in db [:keycard :pin :original]))
|
||||
current-pin (common/vector->string
|
||||
(get-in db [:keycard :pin :current]))]
|
||||
(fx/merge
|
||||
(rf/merge
|
||||
cofx
|
||||
{:db (assoc-in db [:keycard :pin :status] :verifying)
|
||||
{:db (assoc-in db [:keycard :pin :status] :verifying)
|
||||
|
||||
:keycard/change-pin
|
||||
{:new-pin new-pin
|
||||
:current-pin current-pin}})))}))))
|
||||
|
||||
(fx/defn change-puk
|
||||
(rf/defn change-puk
|
||||
{:events [:keycard/change-puk]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(common/show-connection-sheet
|
||||
@@ -99,13 +102,13 @@
|
||||
(get-in db [:keycard :pin :puk-original]))
|
||||
pin (common/vector->string
|
||||
(get-in db [:keycard :pin :current]))]
|
||||
(fx/merge
|
||||
(rf/merge
|
||||
cofx
|
||||
{:db (assoc-in db [:keycard :pin :status] :verifying)
|
||||
:keycard/change-puk {:puk puk
|
||||
:pin pin}})))}))
|
||||
|
||||
(fx/defn change-pairing
|
||||
(rf/defn change-pairing
|
||||
{:events [:keycard/change-pairing]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(common/show-connection-sheet
|
||||
@@ -117,30 +120,32 @@
|
||||
(let [pairing (get-in db [:keycard :pin :pairing-code])
|
||||
pin (common/vector->string
|
||||
(get-in db [:keycard :pin :current]))]
|
||||
(fx/merge
|
||||
(rf/merge
|
||||
cofx
|
||||
{:db (assoc-in db [:keycard :pin :status] :verifying)
|
||||
:keycard/change-pairing {:pairing pairing
|
||||
:pin pin}})))}))
|
||||
|
||||
(fx/defn change-pairing-code
|
||||
(rf/defn change-pairing-code
|
||||
{:events [:keycard/change-pairing-code]}
|
||||
[{:keys [db] :as cofx} pairing]
|
||||
(fx/merge
|
||||
(rf/merge
|
||||
cofx
|
||||
{:db (assoc-in db [:keycard :pin :pairing-code] (security/unmask pairing))}
|
||||
(change-pairing)))
|
||||
|
||||
(fx/defn on-change-pin-success
|
||||
(rf/defn on-change-pin-success
|
||||
{:events [:keycard.callback/on-change-pin-success]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(let [pin (get-in db [:keycard :pin :original])
|
||||
puk-restore? (get-in db [:keycard :pin :puk-restore?])]
|
||||
(fx/merge cofx
|
||||
{:db (assoc-in db [:keycard :pin] {:status nil
|
||||
:login pin
|
||||
:confirmation []
|
||||
:error-label nil})
|
||||
(rf/merge cofx
|
||||
{:db (assoc-in db
|
||||
[:keycard :pin]
|
||||
{:status nil
|
||||
:login pin
|
||||
:confirmation []
|
||||
:error-label nil})
|
||||
:utils/show-popup {:title ""
|
||||
:content (i18n/label :t/pin-changed)}}
|
||||
(common/hide-connection-sheet)
|
||||
@@ -150,55 +155,61 @@
|
||||
(when (:multiaccounts/login db)
|
||||
(common/get-keys-from-keycard)))))
|
||||
|
||||
(fx/defn on-change-puk-success
|
||||
(rf/defn on-change-puk-success
|
||||
{:events [:keycard.callback/on-change-puk-success]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(fx/merge cofx
|
||||
{:db (assoc-in db [:keycard :pin] {:status nil
|
||||
:puk-original []
|
||||
:puk-confirmation []
|
||||
:error-label nil})
|
||||
(rf/merge cofx
|
||||
{:db (assoc-in db
|
||||
[:keycard :pin]
|
||||
{:status nil
|
||||
:puk-original []
|
||||
:puk-confirmation []
|
||||
:error-label nil})
|
||||
:utils/show-popup {:title ""
|
||||
:content (i18n/label :t/puk-changed)}}
|
||||
(common/hide-connection-sheet)
|
||||
(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]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(fx/merge cofx
|
||||
{:db (assoc-in db [:keycard :pin] {:status nil
|
||||
:pairing-code nil
|
||||
:error-label nil})
|
||||
(rf/merge cofx
|
||||
{:db (assoc-in db
|
||||
[:keycard :pin]
|
||||
{:status nil
|
||||
:pairing-code nil
|
||||
:error-label nil})
|
||||
:utils/show-popup {:title ""
|
||||
:content (i18n/label :t/pairing-changed)}}
|
||||
(common/hide-connection-sheet)
|
||||
(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]}
|
||||
[{:keys [db] :as cofx} error]
|
||||
(log/debug "[keycard] change pin error" error)
|
||||
(let [tag-was-lost? (common/tag-lost? (:error error))
|
||||
pin-retries (common/pin-retries (:error error))]
|
||||
(fx/merge cofx
|
||||
(if tag-was-lost?
|
||||
(fx/merge cofx
|
||||
{:db (assoc-in db [:keycard :pin :status] nil)}
|
||||
(common/set-on-card-connected :keycard/change-pin))
|
||||
(if-not (nil? pin-retries)
|
||||
(fx/merge cofx
|
||||
{:db (-> db
|
||||
(assoc-in [:keycard :application-info :pin-retry-counter] pin-retries)
|
||||
(update-in [:keycard :pin] assoc
|
||||
:status :error
|
||||
:enter-step :current
|
||||
:puk []
|
||||
:current []
|
||||
:original []
|
||||
:confirmation []
|
||||
:sign []
|
||||
:error-label :t/pin-mismatch))}
|
||||
(when (zero? pin-retries) (common/frozen-keycard-popup))
|
||||
(navigation/navigate-to-cofx :enter-pin-settings nil))
|
||||
(common/show-wrong-keycard-alert))))))
|
||||
pin-retries (common/pin-retries (:error error))]
|
||||
(rf/merge
|
||||
cofx
|
||||
(if tag-was-lost?
|
||||
(rf/merge cofx
|
||||
{:db (assoc-in db [:keycard :pin :status] nil)}
|
||||
(common/set-on-card-connected :keycard/change-pin))
|
||||
(if-not (nil? pin-retries)
|
||||
(rf/merge cofx
|
||||
{:db (-> db
|
||||
(assoc-in [:keycard :application-info :pin-retry-counter] pin-retries)
|
||||
(update-in [:keycard :pin]
|
||||
assoc
|
||||
:status :error
|
||||
:enter-step :current
|
||||
:puk []
|
||||
:current []
|
||||
:original []
|
||||
:confirmation []
|
||||
:sign []
|
||||
:error-label :t/pin-mismatch))}
|
||||
(when (zero? pin-retries) (common/frozen-keycard-popup))
|
||||
(navigation/navigate-to-cofx :enter-pin-settings nil))
|
||||
(common/show-wrong-keycard-alert))))))
|
||||
|
||||
+147
-129
@@ -1,28 +1,30 @@
|
||||
(ns status-im.keycard.common
|
||||
(:require [clojure.string :as string]
|
||||
[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.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.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 pin-mismatch-error #"Unexpected error SW, 0x63C(\d+)|wrongPIN\(retryCounter: (\d+)\)")
|
||||
|
||||
(defn pin-retries [error]
|
||||
(when-let [matched-error (re-matches pin-mismatch-error error)] (js/parseInt (second (filter some? matched-error)))))
|
||||
(defn pin-retries
|
||||
[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]
|
||||
{:dispatch [event]})
|
||||
|
||||
@@ -56,7 +58,8 @@
|
||||
(zero? free-pairing-slots))
|
||||
:no-pairing-slots))
|
||||
|
||||
(defn tag-lost? [error]
|
||||
(defn tag-lost?
|
||||
[error]
|
||||
(or
|
||||
(= error "Tag was lost.")
|
||||
(= error "NFCError:100")
|
||||
@@ -94,24 +97,24 @@
|
||||
(fn [supported?]
|
||||
(nfc/set-nfc-supported? supported?)))
|
||||
|
||||
(fx/defn listen-to-hardware-back-button
|
||||
(rf/defn listen-to-hardware-back-button
|
||||
[{:keys [db]}]
|
||||
(when-not (get-in db [:keycard :back-button-listener])
|
||||
{: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]}]
|
||||
(when-let [listener (get-in db [:keycard :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]
|
||||
(log/debug "[keycard] set-on-card-connected" on-connect)
|
||||
{:db (-> db
|
||||
(assoc-in [:keycard :on-card-connected] on-connect)
|
||||
(assoc-in [:keycard :last-on-card-connected] nil))})
|
||||
|
||||
(fx/defn stash-on-card-connected
|
||||
(rf/defn stash-on-card-connected
|
||||
[{:keys [db]}]
|
||||
(let [on-connect (get-in db [:keycard :on-card-connected])]
|
||||
(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 :on-card-connected] nil))}))
|
||||
|
||||
(fx/defn restore-on-card-connected
|
||||
(rf/defn restore-on-card-connected
|
||||
[{:keys [db]}]
|
||||
(let [on-connect (or
|
||||
(get-in db [:keycard :on-card-connected])
|
||||
@@ -129,21 +132,21 @@
|
||||
(assoc-in [:keycard :on-card-connected] on-connect)
|
||||
(assoc-in [:keycard :last-on-card-connect] nil))}))
|
||||
|
||||
(fx/defn clear-on-card-connected
|
||||
(rf/defn clear-on-card-connected
|
||||
[{:keys [db]}]
|
||||
(log/debug "[keycard] clear-on-card-connected")
|
||||
{:db (-> db
|
||||
(assoc-in [:keycard :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]
|
||||
(log/debug "[keycard] set-on-card-read" on-connect)
|
||||
{:db (-> db
|
||||
(assoc-in [:keycard :on-card-read] on-connect)
|
||||
(assoc-in [:keycard :last-on-card-read] nil))})
|
||||
|
||||
(fx/defn stash-on-card-read
|
||||
(rf/defn stash-on-card-read
|
||||
[{:keys [db]}]
|
||||
(let [on-connect (get-in db [:keycard :on-card-read])]
|
||||
(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 :on-card-read] nil))}))
|
||||
|
||||
(fx/defn restore-on-card-read
|
||||
(rf/defn restore-on-card-read
|
||||
[{:keys [db]}]
|
||||
(let [on-connect (or
|
||||
(get-in db [:keycard :on-card-read])
|
||||
@@ -161,14 +164,15 @@
|
||||
(assoc-in [:keycard :on-card-read] on-connect)
|
||||
(assoc-in [:keycard :last-on-card-connect] nil))}))
|
||||
|
||||
(fx/defn clear-on-card-read
|
||||
(rf/defn clear-on-card-read
|
||||
[{:keys [db]}]
|
||||
(log/debug "[keycard] clear-on-card-read")
|
||||
{:db (-> db
|
||||
(assoc-in [:keycard :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 []
|
||||
[keycard-sheet/connect-keycard
|
||||
{:on-cancel #(re-frame/dispatch on-cancel)
|
||||
@@ -177,17 +181,19 @@
|
||||
:on-connect ::on-card-connected
|
||||
:on-disconnect ::on-card-disconnected}]))
|
||||
|
||||
(fx/defn show-connection-sheet-component
|
||||
[{:keys [db] :as cofx} {:keys [on-card-connected on-card-read handler]
|
||||
{:keys [on-cancel]
|
||||
:or {on-cancel [::cancel-sheet-confirm]}}
|
||||
:sheet-options}]
|
||||
(rf/defn show-connection-sheet-component
|
||||
[{:keys [db] :as cofx}
|
||||
{:keys [on-card-connected on-card-read handler]
|
||||
{:keys [on-cancel]
|
||||
:or {on-cancel [::cancel-sheet-confirm]}}
|
||||
:sheet-options}]
|
||||
(assert (keyword? on-card-connected))
|
||||
(assert (fn? handler))
|
||||
(let [connected? (get-in db [:keycard :card-connected?])]
|
||||
(log/debug "[keycard] show-sheet-with-connection-check"
|
||||
"card-connected?" connected?)
|
||||
(fx/merge
|
||||
"card-connected?"
|
||||
connected?)
|
||||
(rf/merge
|
||||
cofx
|
||||
{:dismiss-keyboard true}
|
||||
(bottom-sheet/show-bottom-sheet
|
||||
@@ -205,7 +211,7 @@
|
||||
(when connected?
|
||||
handler))))
|
||||
|
||||
(fx/defn show-connection-sheet
|
||||
(rf/defn show-connection-sheet
|
||||
[{:keys [db] :as cofx} args]
|
||||
(let [nfc-running? (get-in db [:keycard :nfc-running?])]
|
||||
(log/debug "show connection; already running?" nfc-running?)
|
||||
@@ -213,59 +219,59 @@
|
||||
(show-connection-sheet-component cofx 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]}
|
||||
[cofx args]
|
||||
(log/debug "on-nfc-ready-for-sheet")
|
||||
(show-connection-sheet-component cofx args))
|
||||
|
||||
(fx/defn hide-connection-sheet-component
|
||||
(rf/defn hide-connection-sheet-component
|
||||
[{:keys [db] :as cofx}]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{:db (assoc-in db [:keycard :card-read-in-progress?] false)}
|
||||
(restore-on-card-connected)
|
||||
(restore-on-card-read)
|
||||
(bottom-sheet/hide-bottom-sheet)))
|
||||
|
||||
(fx/defn hide-connection-sheet
|
||||
(rf/defn hide-connection-sheet
|
||||
[cofx]
|
||||
(log/debug "hide-connection-sheet")
|
||||
{: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]}
|
||||
[cofx]
|
||||
(log/debug "on-nfc-ready-to-close-sheet")
|
||||
(hide-connection-sheet-component cofx))
|
||||
|
||||
(fx/defn clear-pin
|
||||
(rf/defn clear-pin
|
||||
[{:keys [db] :as cofx}]
|
||||
(fx/merge
|
||||
(rf/merge
|
||||
cofx
|
||||
{:db (assoc-in db
|
||||
[:keycard :pin]
|
||||
{:status nil
|
||||
:login (get-in db [:keycard :pin :original])
|
||||
:export-key []
|
||||
:sign []
|
||||
:puk []
|
||||
:current []
|
||||
:original []
|
||||
:confirmation []
|
||||
:error-label nil
|
||||
:on-verified (get-in db [:keycard :pin :on-verified])
|
||||
:on-verified-failure (get-in db [:keycard :pin :on-verified])})}))
|
||||
[:keycard :pin]
|
||||
{:status nil
|
||||
:login (get-in db [:keycard :pin :original])
|
||||
:export-key []
|
||||
:sign []
|
||||
:puk []
|
||||
:current []
|
||||
:original []
|
||||
:confirmation []
|
||||
:error-label nil
|
||||
:on-verified (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
|
||||
:keycard/back-button-pressed]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(when-not (get-in db [:keycard :card-connected?])
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(hide-connection-sheet)
|
||||
(clear-pin))))
|
||||
|
||||
(fx/defn cancel-sheet
|
||||
(rf/defn cancel-sheet
|
||||
{:events [::cancel-sheet]}
|
||||
[_]
|
||||
{:ui/show-confirmation {:title (i18n/label :t/keycard-cancel-setup-title)
|
||||
@@ -275,7 +281,7 @@
|
||||
:on-accept #(re-frame/dispatch [::cancel-sheet-confirm])
|
||||
: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.
|
||||
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
|
||||
@@ -284,120 +290,126 @@
|
||||
[{:keys [db]} 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")
|
||||
{:utils/show-popup {:title (i18n/label :t/wrong-card)
|
||||
:content (i18n/label :t/wrong-card-text)}})
|
||||
|
||||
(fx/defn unauthorized-operation
|
||||
(rf/defn unauthorized-operation
|
||||
[cofx]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{:utils/show-popup {:title ""
|
||||
:content (i18n/label :t/keycard-unauthorized-operation)}}
|
||||
(clear-on-card-connected)
|
||||
(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]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{:db (assoc-in db [:keycard :pin :current] [])}
|
||||
(navigation/navigate-to-cofx :enter-pin-settings nil)))
|
||||
|
||||
(defn- tag-lost-exception? [code error]
|
||||
(defn- tag-lost-exception?
|
||||
[code error]
|
||||
(or
|
||||
(= code "android.nfc.TagLostException")
|
||||
(= error "Tag was lost.")
|
||||
(= 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)
|
||||
{:db (assoc-in db [:keycard :setup-step] :error)}))
|
||||
|
||||
(fx/defn get-keys-from-keycard
|
||||
(rf/defn get-keys-from-keycard
|
||||
[{:keys [db]}]
|
||||
(let [key-uid (get-in db [:multiaccounts/login :key-uid])
|
||||
pin (string/join (get-in db [:keycard :pin :login]))]
|
||||
(log/debug "[keycard] get-keys-from-keycard"
|
||||
", not empty pin:" (boolean (seq pin)))
|
||||
", not empty pin:"
|
||||
(boolean (seq pin)))
|
||||
(when (seq pin)
|
||||
{:db (assoc-in db [:keycard :pin :status] :verifying)
|
||||
:keycard/get-keys {:pin pin}})))
|
||||
|
||||
(fx/defn on-get-keys-success
|
||||
(rf/defn on-get-keys-success
|
||||
{:events [:keycard.callback/on-get-keys-success]}
|
||||
[{:keys [db] :as cofx} data]
|
||||
(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])
|
||||
key-uid (get-in db [:keycard :application-info :key-uid])
|
||||
multiaccount-data (types/clj->json {:name name
|
||||
:key-uid key-uid
|
||||
:identicon identicon})
|
||||
save-keys? (get-in db [:multiaccounts/login :save-password?])]
|
||||
(fx/merge cofx
|
||||
key-uid (get-in db [:keycard :application-info :key-uid])
|
||||
multiaccount-data (types/clj->json {:name name
|
||||
:key-uid key-uid
|
||||
:identicon identicon})
|
||||
save-keys? (get-in db [:multiaccounts/login :save-password?])]
|
||||
(rf/merge cofx
|
||||
{:db
|
||||
(-> db
|
||||
(assoc-in [:keycard :pin :status] nil)
|
||||
(assoc-in [:keycard :pin :login] [])
|
||||
(update-in [:keycard :application-info] assoc
|
||||
(update-in [:keycard :application-info]
|
||||
assoc
|
||||
:puk-retry-counter 5
|
||||
:pin-retry-counter 3)
|
||||
(assoc-in [:keycard :multiaccount]
|
||||
(update account-data :whisper-public-key ethereum/normalized-hex))
|
||||
(assoc-in [:keycard :flow] nil)
|
||||
(update :multiaccounts/login assoc
|
||||
:password encryption-public-key
|
||||
:key-uid key-uid
|
||||
:identicon identicon
|
||||
:name name))
|
||||
:password encryption-public-key
|
||||
:key-uid key-uid
|
||||
:identicon identicon
|
||||
:name name))
|
||||
|
||||
:keycard/login-with-keycard {:multiaccount-data multiaccount-data
|
||||
:password encryption-public-key
|
||||
:chat-key whisper-private-key
|
||||
:key-uid key-uid}}
|
||||
:keycard/login-with-keycard {:multiaccount-data multiaccount-data
|
||||
:password encryption-public-key
|
||||
:chat-key whisper-private-key
|
||||
:key-uid key-uid}}
|
||||
(when save-keys?
|
||||
(keychain/save-keycard-keys key-uid encryption-public-key whisper-private-key))
|
||||
(clear-on-card-connected)
|
||||
(clear-on-card-read)
|
||||
(hide-connection-sheet))))
|
||||
|
||||
(fx/defn blocked-or-frozen-keycard-popup
|
||||
(rf/defn blocked-or-frozen-keycard-popup
|
||||
[{:keys [db] :as cofx} card-status]
|
||||
(fx/merge
|
||||
(rf/merge
|
||||
cofx
|
||||
{:db (assoc-in db [:keycard :pin :status] card-status)}
|
||||
(hide-connection-sheet)
|
||||
; do not try to display the popover if it is already open or
|
||||
; we are in the login interface (which has a different handling)
|
||||
; do not try to display the popover if it is already open or
|
||||
; we are in the login interface (which has a different handling)
|
||||
(when-not (or (:multiaccounts/login db) (:popover/popover db))
|
||||
(popover/show-popover {:view card-status}))))
|
||||
|
||||
(fx/defn blocked-keycard-popup
|
||||
(rf/defn blocked-keycard-popup
|
||||
[cofx]
|
||||
(blocked-or-frozen-keycard-popup cofx :blocked-card))
|
||||
|
||||
(fx/defn frozen-keycard-popup
|
||||
(rf/defn frozen-keycard-popup
|
||||
[cofx]
|
||||
(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]}
|
||||
[{:keys [db] :as cofx} error]
|
||||
(log/debug "[keycard] get keys error: " error)
|
||||
(let [tag-was-lost? (tag-lost? (:error error))
|
||||
key-uid (get-in db [:keycard :application-info :key-uid])
|
||||
flow (get-in db [:keycard :flow])
|
||||
pin-retries-count (pin-retries (:error error))]
|
||||
(let [tag-was-lost? (tag-lost? (:error error))
|
||||
key-uid (get-in db [:keycard :application-info :key-uid])
|
||||
flow (get-in db [:keycard :flow])
|
||||
pin-retries-count (pin-retries (:error error))]
|
||||
(if tag-was-lost?
|
||||
{:db (assoc-in db [:keycard :pin :status] nil)}
|
||||
(if-not (nil? pin-retries-count)
|
||||
(fx/merge
|
||||
(rf/merge
|
||||
cofx
|
||||
{:db (-> db
|
||||
(assoc-in [:keycard :application-info :pin-retry-counter] pin-retries-count)
|
||||
(update-in [:keycard :pin] assoc
|
||||
(update-in [:keycard :pin]
|
||||
assoc
|
||||
:status :error
|
||||
:login []
|
||||
:import-multiaccount []
|
||||
@@ -406,42 +418,45 @@
|
||||
(when (zero? pin-retries-count) (frozen-keycard-popup)))
|
||||
(show-wrong-keycard-alert)))))
|
||||
|
||||
(fx/defn factory-reset
|
||||
(rf/defn factory-reset
|
||||
{:events [:keycard/factory-reset]}
|
||||
[{:keys [db]} on-card-read]
|
||||
(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}})
|
||||
|
||||
;; Get application info
|
||||
|
||||
(fx/defn update-pairings
|
||||
(rf/defn update-pairings
|
||||
[{:keys [db]} instance-uid pairing]
|
||||
(let [paired-on (utils.datetime/timestamp)
|
||||
pairings (-> (get-in db [:keycard :pairings])
|
||||
(assoc instance-uid {:pairing pairing :paired-on paired-on}))]
|
||||
(let [paired-on (utils.datetime/timestamp)
|
||||
pairings (-> (get-in db [:keycard :pairings])
|
||||
(assoc instance-uid {:pairing pairing :paired-on paired-on}))]
|
||||
{: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]}
|
||||
[{:keys [db]} on-card-read]
|
||||
(log/debug "[keycard] get-application-info")
|
||||
{: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]}
|
||||
[{:keys [db] :as cofx} info on-success]
|
||||
(let [{:keys [pin-retry-counter puk-retry-counter instance-uid new-pairing]} info
|
||||
view-id (:view-id db)
|
||||
{:keys [on-card-read]} (:keycard db)
|
||||
on-success' (or on-success on-card-read)
|
||||
enter-step (get-in db [:keycard :pin :enter-step])]
|
||||
view-id (:view-id db)
|
||||
{:keys [on-card-read]} (:keycard db)
|
||||
on-success' (or on-success
|
||||
on-card-read)
|
||||
enter-step (get-in db
|
||||
[:keycard :pin
|
||||
:enter-step])]
|
||||
(log/debug "[keycard] on-get-application-info-success"
|
||||
"on-success" on-success'
|
||||
"on-success" on-success'
|
||||
"pin-retry-counter" pin-retry-counter
|
||||
"puk-retry-counter" puk-retry-counter)
|
||||
(fx/merge
|
||||
(rf/merge
|
||||
cofx
|
||||
{:db (-> db
|
||||
(assoc-in [:keycard :pin :enter-step] enter-step)
|
||||
@@ -461,7 +476,7 @@
|
||||
(when 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]}
|
||||
[{:keys [db] :as cofx} error]
|
||||
(let [on-card-read (get-in db [:keycard :on-card-read])
|
||||
@@ -475,10 +490,10 @@
|
||||
last-on-card-connected)
|
||||
(when-not tag-was-lost?
|
||||
(if login?
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(clear-on-card-read)
|
||||
(navigation/navigate-to-cofx :not-keycard nil))
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{:db (assoc-in db [:keycard :application-info-error] error)}
|
||||
|
||||
(when (contains?
|
||||
@@ -489,7 +504,7 @@
|
||||
(when on-card-read
|
||||
(dispatch-event on-card-read)))))))
|
||||
|
||||
(fx/defn on-card-connected
|
||||
(rf/defn on-card-connected
|
||||
{:events [::on-card-connected]}
|
||||
[{:keys [db] :as cofx} _]
|
||||
(let [instance-uid (get-in db [:keycard :application-info :instance-uid])
|
||||
@@ -500,9 +515,9 @@
|
||||
should-read-instance-uid? :keycard/get-application-info
|
||||
:else (get-in db [:keycard :on-card-read]))]
|
||||
(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
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{:db (-> db
|
||||
(assoc-in [:keycard :card-read-in-progress?] (boolean on-card-read)))}
|
||||
(when on-card-connected
|
||||
@@ -512,20 +527,21 @@
|
||||
(nil? on-card-connected))
|
||||
(get-application-info on-card-read))))))
|
||||
|
||||
(fx/defn on-card-disconnected
|
||||
(rf/defn on-card-disconnected
|
||||
{:events [::on-card-disconnected]}
|
||||
[{:keys [db] :as cofx} _]
|
||||
(log/debug "[keycard] card disconnected")
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{:db (-> db
|
||||
(assoc-in [:keycard :card-read-in-progress?] false))}
|
||||
(restore-on-card-connected)
|
||||
(restore-on-card-read)))
|
||||
|
||||
(defn keycard-multiaccount? [db]
|
||||
(defn keycard-multiaccount?
|
||||
[db]
|
||||
(boolean (get-in db [:multiaccount :keycard-pairing])))
|
||||
|
||||
(fx/defn verify-pin
|
||||
(rf/defn verify-pin
|
||||
{:events [:keycard/verify-pin]}
|
||||
[{:keys [db] :as cofx} {:keys [pin-step on-card-connected on-failure on-success]}]
|
||||
(let [on-success (or on-success
|
||||
@@ -534,23 +550,25 @@
|
||||
(get-in db [:keycard :pin :on-verified-failure]))
|
||||
pin-step (or pin-step
|
||||
(get-in db [:keycard :pin :step]))]
|
||||
(fx/merge
|
||||
(rf/merge
|
||||
cofx
|
||||
{:db (update-in db [:keycard :pin] assoc
|
||||
:step pin-step
|
||||
:on-verified on-success
|
||||
{:db (update-in db
|
||||
[:keycard :pin]
|
||||
assoc
|
||||
:step pin-step
|
||||
:on-verified on-success
|
||||
:on-verified-failure on-failure)}
|
||||
(show-connection-sheet
|
||||
{:on-card-connected (or on-card-connected :keycard/verify-pin)
|
||||
:handler
|
||||
(fn [{:keys [db] :as cofx}]
|
||||
(let [pin (vector->string (get-in db [:keycard :pin pin-step]))]
|
||||
(fx/merge
|
||||
(let [pin (vector->string (get-in db [:keycard :pin pin-step]))]
|
||||
(rf/merge
|
||||
cofx
|
||||
{:db (assoc-in db [:keycard :pin :status] :verifying)
|
||||
{:db (assoc-in db [:keycard :pin :status] :verifying)
|
||||
:keycard/verify-pin {:pin pin}})))}))))
|
||||
|
||||
(fx/defn navigete-to-keycard-settings
|
||||
(rf/defn navigete-to-keycard-settings
|
||||
{:events [::navigate-to-keycard-settings]}
|
||||
[cofx]
|
||||
(navigation/set-stack-root :profile-stack [:my-profile :keycard-settings]))
|
||||
|
||||
+186
-143
@@ -1,70 +1,74 @@
|
||||
(ns status-im.keycard.core
|
||||
(: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.common :as common]
|
||||
status-im.keycard.delete-key
|
||||
status-im.keycard.export-key
|
||||
status-im.keycard.unpair
|
||||
status-im.keycard.backup-key
|
||||
[status-im.keycard.login :as login]
|
||||
[status-im.keycard.mnemonic :as mnemonic]
|
||||
[status-im.keycard.onboarding :as onboarding]
|
||||
[status-im.keycard.recovery :as recovery]
|
||||
[status-im.keycard.sign :as sign]
|
||||
status-im.keycard.unpair
|
||||
[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.update.core :as multiaccounts.update]
|
||||
[status-im2.navigation.events :as navigation]
|
||||
[status-im.utils.datetime :as utils.datetime]
|
||||
[status-im.utils.fx :as fx]
|
||||
[taoensso.timbre :as log]))
|
||||
[status-im2.navigation.events :as navigation]
|
||||
[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}]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{:db (assoc-in db [:keycard :setup-step] 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 ""
|
||||
:confirm-button-text :t/okay}}))
|
||||
|
||||
(fx/defn load-pin-screen
|
||||
(rf/defn load-pin-screen
|
||||
[{:keys [db] :as cofx}]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{:db (-> db
|
||||
(assoc-in [:keycard :setup-step] :pin)
|
||||
(assoc-in [:keycard :pin] {:enter-step :original
|
||||
:original []
|
||||
:confirmation []}))}
|
||||
(assoc-in [:keycard :pin]
|
||||
{:enter-step :original
|
||||
:original []
|
||||
:confirmation []}))}
|
||||
(navigation/navigate-to-cofx :keycard-onboarding-pin nil)))
|
||||
|
||||
(fx/defn load-recovery-pin-screen
|
||||
(rf/defn load-recovery-pin-screen
|
||||
[{:keys [db] :as cofx}]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{:db (-> db
|
||||
(assoc-in [:keycard :pin] {:enter-step :import-multiaccount
|
||||
:import-multiaccount []
|
||||
:current []}))}
|
||||
(assoc-in [:keycard :pin]
|
||||
{:enter-step :import-multiaccount
|
||||
:import-multiaccount []
|
||||
:current []}))}
|
||||
(common/listen-to-hardware-back-button)
|
||||
(navigation/navigate-replace :keycard-recovery-pin nil)))
|
||||
|
||||
(fx/defn load-pairing
|
||||
(rf/defn load-pairing
|
||||
[{:keys [db]}]
|
||||
(let [instance-uid (get-in db [:keycard :application-info :instance-uid])
|
||||
pairing-data (or (get-in db [:keycard :pairings (keyword instance-uid)])
|
||||
(get-in db [:keycard :pairings instance-uid]))]
|
||||
{: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?]
|
||||
(log/debug "[keycard] proceed-setup-with-initialized-card"
|
||||
"instance-uid" instance-uid)
|
||||
"instance-uid"
|
||||
instance-uid)
|
||||
(if (= flow :import)
|
||||
(navigation/navigate-to-cofx cofx :keycard-recovery-no-key nil)
|
||||
(if paired?
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(common/listen-to-hardware-back-button)
|
||||
(when (= flow :create)
|
||||
(mnemonic/set-mnemonic))
|
||||
@@ -74,25 +78,25 @@
|
||||
(onboarding/load-pairing-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]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{:db (-> db
|
||||
(assoc-in [:keycard :pin :on-verified] nil)
|
||||
(assoc-in [:keycard :setup-step] nil))}
|
||||
(common/clear-on-card-connected)
|
||||
(navigation/navigate-to-cofx :keycard-settings nil)))
|
||||
|
||||
(fx/defn password-option-pressed
|
||||
(rf/defn password-option-pressed
|
||||
{:eevents [:keycard.ui/password-option-pressed]}
|
||||
[{:keys [db]}]
|
||||
(when (= (get-in db [:keycard :flow]) :create)
|
||||
#())) ;;TODO with v1 flow
|
||||
|
||||
(fx/defn settings-screen-did-load
|
||||
(rf/defn settings-screen-did-load
|
||||
[{:keys [db] :as cofx}]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{:db (-> db
|
||||
(assoc-in [:keycard :pin :on-verified] nil)
|
||||
(assoc-in [:keycard :setup-step] nil))}
|
||||
@@ -111,7 +115,7 @@
|
||||
[{:keys [db]}]
|
||||
(let [enter-step (get-in db [:keycard :pin :enter-step])]
|
||||
{:db (-> db
|
||||
(assoc-in [:keycard :pin enter-step] [])
|
||||
(assoc-in [:keycard :pin enter-step] [])
|
||||
(dissoc :intro-wizard :recovered-account?))}))
|
||||
|
||||
(defn multiaccounts-screen-did-load
|
||||
@@ -122,44 +126,51 @@
|
||||
[{:keys [db]}]
|
||||
{: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]}
|
||||
[_ supported?]
|
||||
{:keycard/set-nfc-supported supported?})
|
||||
|
||||
;; 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]}
|
||||
[{:keys [db]} nfc-enabled?]
|
||||
(log/debug "[keycard] check-nfc-enabled-success"
|
||||
"nfc-enabled?" nfc-enabled?)
|
||||
"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))
|
||||
|
||||
(defn- proceed-to-change-puk-confirmation [fx]
|
||||
(defn- proceed-to-change-puk-confirmation
|
||||
[fx]
|
||||
(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
|
||||
(update-in [:db :keycard :pin] dissoc :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))
|
||||
|
||||
(fx/defn on-unblock-pin-success
|
||||
(rf/defn on-unblock-pin-success
|
||||
{:events [:keycard.callback/on-unblock-pin-success]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(let [reset-pin (get-in db [:keycard :pin :reset])]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{:db
|
||||
(-> db
|
||||
(update-in [:keycard :application-info] assoc
|
||||
(update-in [:keycard :application-info]
|
||||
assoc
|
||||
:puk-retry-counter 5
|
||||
:pin-retry-counter 3)
|
||||
(update-in [:keycard :pin] assoc
|
||||
(update-in [:keycard :pin]
|
||||
assoc
|
||||
:status :after-unblocking
|
||||
:enter-step :login
|
||||
:login reset-pin
|
||||
@@ -171,18 +182,19 @@
|
||||
(common/clear-on-card-connected)
|
||||
(common/clear-on-card-read))))
|
||||
|
||||
(fx/defn on-unblock-pin-error
|
||||
(rf/defn on-unblock-pin-error
|
||||
{:events [:keycard.callback/on-unblock-pin-error]}
|
||||
[{:keys [db] :as cofx} 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)
|
||||
(when-not tag-was-lost?
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{:db
|
||||
(-> db
|
||||
(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)
|
||||
:error-label :t/puk-mismatch
|
||||
:enter-step :puk
|
||||
@@ -190,19 +202,25 @@
|
||||
|
||||
(common/hide-connection-sheet)))))
|
||||
|
||||
(fx/defn clear-on-verify-handlers
|
||||
(rf/defn clear-on-verify-handlers
|
||||
[{:keys [db]}]
|
||||
{:db (update-in db [:keycard :pin]
|
||||
dissoc :on-verified-failure :on-verified)})
|
||||
{:db (update-in db
|
||||
[: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]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(let [on-verified (get-in db [:keycard :pin :on-verified])]
|
||||
(log/debug "[hardwaller] success pin verification. on-verified" on-verified)
|
||||
(fx/merge cofx
|
||||
{:db (update-in db [:keycard :pin] merge {:status nil
|
||||
:error-label nil})}
|
||||
(rf/merge cofx
|
||||
{:db (update-in db
|
||||
[:keycard :pin]
|
||||
merge
|
||||
{:status nil
|
||||
:error-label nil})}
|
||||
(common/clear-on-card-connected)
|
||||
(common/clear-on-card-read)
|
||||
;; 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
|
||||
:wallet.accounts/generate-new-keycard-account
|
||||
:keycard/remove-key-with-unpair
|
||||
:keycard/unpair-and-delete} on-verified)
|
||||
:keycard/unpair-and-delete}
|
||||
on-verified)
|
||||
(common/hide-connection-sheet))
|
||||
(when-not (contains? #{:keycard/unpair
|
||||
:keycard/generate-and-load-key
|
||||
:keycard/remove-key-with-unpair
|
||||
: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))
|
||||
(when on-verified
|
||||
(common/dispatch-event on-verified))
|
||||
(clear-on-verify-handlers))))
|
||||
|
||||
(fx/defn on-verify-pin-error
|
||||
(rf/defn on-verify-pin-error
|
||||
{:events [:keycard.callback/on-verify-pin-error]}
|
||||
[{:keys [db] :as cofx} error]
|
||||
(let [tag-was-lost? (common/tag-lost? (:error error))
|
||||
setup? (boolean (get-in db [:keycard :setup-step]))
|
||||
on-verified-failure (get-in db [:keycard :pin :on-verified-failure])
|
||||
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)
|
||||
(when-not tag-was-lost?
|
||||
(if-not (nil? pin-retries)
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{:db (-> db
|
||||
(assoc-in [:keycard :application-info :pin-retry-counter] pin-retries)
|
||||
(update-in [:keycard :pin] assoc
|
||||
(update-in [:keycard :pin]
|
||||
assoc
|
||||
:status :error
|
||||
:enter-step :current
|
||||
:puk []
|
||||
@@ -251,20 +272,21 @@
|
||||
(not on-verified-failure))
|
||||
(when exporting?
|
||||
(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 on-verified-failure
|
||||
(fn [_] {:utils/dispatch-later
|
||||
[{:dispatch [on-verified-failure]
|
||||
:ms 200}]}))
|
||||
(fn [_]
|
||||
{:utils/dispatch-later
|
||||
[{:dispatch [on-verified-failure]
|
||||
:ms 200}]}))
|
||||
#_(clear-on-verify-handlers))
|
||||
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(common/hide-connection-sheet)
|
||||
(common/show-wrong-keycard-alert)
|
||||
(clear-on-verify-handlers))))))
|
||||
|
||||
(fx/defn unblock-pin
|
||||
(rf/defn unblock-pin
|
||||
{:events [:keycard/unblock-pin]}
|
||||
[cofx]
|
||||
(common/show-connection-sheet
|
||||
@@ -275,7 +297,7 @@
|
||||
(let [puk (common/vector->string (get-in db [:keycard :pin :puk]))
|
||||
pin (common/vector->string (get-in db [:keycard :pin :reset]))
|
||||
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
|
||||
{:puk puk
|
||||
:new-pin pin}}))}))
|
||||
@@ -283,22 +305,24 @@
|
||||
(def pin-code-length 6)
|
||||
(def puk-code-length 12)
|
||||
|
||||
(fx/defn handle-pin-input
|
||||
(rf/defn handle-pin-input
|
||||
[{:keys [db]} enter-step]
|
||||
(let [numbers-entered (count (get-in db [:keycard :pin enter-step]))]
|
||||
(when (or (= numbers-entered pin-code-length)
|
||||
(= numbers-entered puk-code-length))
|
||||
{:dispatch [:keycard/process-pin-input]})))
|
||||
|
||||
(fx/defn update-pin
|
||||
(rf/defn update-pin
|
||||
{:events [:keycard.ui/pin-numpad-button-pressed]}
|
||||
[{:keys [db] :as cofx} number enter-step]
|
||||
(log/debug "update-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))
|
||||
(< numbers-entered puk-code-length)
|
||||
(< numbers-entered pin-code-length))]
|
||||
(fx/merge cofx
|
||||
need-update? (if (or (= enter-step :puk)
|
||||
(= enter-step :puk-original)
|
||||
(= enter-step :puk-confirmation))
|
||||
(< numbers-entered puk-code-length)
|
||||
(< numbers-entered pin-code-length))]
|
||||
(rf/merge cofx
|
||||
{:db (cond-> (-> db
|
||||
(assoc-in [:keycard :pin :enter-step] enter-step)
|
||||
(assoc-in [:keycard :pin :status] nil))
|
||||
@@ -306,26 +330,38 @@
|
||||
(when need-update?
|
||||
(handle-pin-input enter-step)))))
|
||||
|
||||
(defn- pin-enter-error [fx error-label]
|
||||
(update-in fx [:db :keycard :pin] merge {:status :error
|
||||
:error-label error-label
|
||||
:enter-step :original
|
||||
:original []
|
||||
:confirmation []}))
|
||||
(defn- pin-enter-error
|
||||
[fx error-label]
|
||||
(update-in fx
|
||||
[:db :keycard :pin]
|
||||
merge
|
||||
{:status :error
|
||||
:error-label error-label
|
||||
:enter-step :original
|
||||
:original []
|
||||
:confirmation []}))
|
||||
|
||||
(defn- puk-enter-error [fx error-label]
|
||||
(update-in fx [:db :keycard :pin] merge {:status :error
|
||||
:error-label error-label
|
||||
:enter-step :puk-original
|
||||
:puk-original []
|
||||
:puk-confirmation []}))
|
||||
(defn- puk-enter-error
|
||||
[fx error-label]
|
||||
(update-in fx
|
||||
[:db :keycard :pin]
|
||||
merge
|
||||
{:status :error
|
||||
:error-label error-label
|
||||
:enter-step :puk-original
|
||||
:puk-original []
|
||||
:puk-confirmation []}))
|
||||
|
||||
(defn- pin-reset-error [fx error-label]
|
||||
(update-in fx [:db :keycard :pin] merge {:status :error
|
||||
:error-label error-label
|
||||
:enter-step :reset
|
||||
:reset []
|
||||
:reset-confirmation []}))
|
||||
(defn- pin-reset-error
|
||||
[fx error-label]
|
||||
(update-in fx
|
||||
[:db :keycard :pin]
|
||||
merge
|
||||
{:status :error
|
||||
:error-label error-label
|
||||
:enter-step :reset
|
||||
:reset []
|
||||
:reset-confirmation []}))
|
||||
|
||||
; PIN enter steps:
|
||||
; login - PIN is used to login
|
||||
@@ -333,14 +369,14 @@
|
||||
; current - current PIN to perform actions which require PIN auth
|
||||
; original - new PIN when user changes it or creates new one
|
||||
; confirmation - confirmation for new PIN
|
||||
(fx/defn process-pin-input
|
||||
(rf/defn process-pin-input
|
||||
{:events [:keycard/process-pin-input]}
|
||||
[{:keys [db]}]
|
||||
(let [enter-step (get-in db [:keycard :pin :enter-step])
|
||||
pin (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])
|
||||
numbers-entered (count pin)]
|
||||
(log/debug "[keycard] process-pin-input"
|
||||
"enter-step" enter-step
|
||||
"enter-step" enter-step
|
||||
"numbers-entered" numbers-entered)
|
||||
(cond-> {:db (assoc-in db [:keycard :pin :status] nil)}
|
||||
|
||||
@@ -417,15 +453,19 @@
|
||||
(get-in db [:keycard :pin :reset-confirmation])))
|
||||
(pin-reset-error :t/pin-mismatch))))
|
||||
|
||||
(fx/defn set-multiaccount-pairing
|
||||
(rf/defn set-multiaccount-pairing
|
||||
[cofx _ pairing paired-on]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(multiaccounts.update/multiaccount-update
|
||||
:keycard-pairing pairing {})
|
||||
:keycard-pairing
|
||||
pairing
|
||||
{})
|
||||
(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]}
|
||||
[{:keys [db]} 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
|
||||
;; unfinished setup.
|
||||
|
||||
(fx/defn on-pair-success
|
||||
(rf/defn on-pair-success
|
||||
{:events [:keycard.callback/on-pair-success]}
|
||||
[{:keys [db] :as cofx} pairing]
|
||||
(let [setup-step (get-in db [:keycard :setup-step])
|
||||
@@ -448,19 +488,20 @@
|
||||
paired-on (utils.datetime/timestamp)
|
||||
pairings (-> (get-in db [:keycard :pairings])
|
||||
(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)
|
||||
:begin
|
||||
:card-ready)]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{:keycard/persist-pairings pairings
|
||||
:db (-> db
|
||||
(assoc-in [:keycard :pairings] pairings)
|
||||
(assoc-in [:keycard :application-info :paired?] true)
|
||||
(assoc-in [:keycard :setup-step] next-step)
|
||||
(assoc-in [:keycard :secrets :pairing] pairing)
|
||||
(assoc-in [:keycard :secrets :paired-on] paired-on))}
|
||||
(when-not (and (= flow :recovery) (= next-step :card-ready)) (common/hide-connection-sheet))
|
||||
:db (-> db
|
||||
(assoc-in [:keycard :pairings] pairings)
|
||||
(assoc-in [:keycard :application-info :paired?] true)
|
||||
(assoc-in [:keycard :setup-step] next-step)
|
||||
(assoc-in [:keycard :secrets :pairing] pairing)
|
||||
(assoc-in [:keycard :secrets :paired-on] paired-on))}
|
||||
(when-not (and (= flow :recovery) (= next-step :card-ready))
|
||||
(common/hide-connection-sheet))
|
||||
(when multiaccount
|
||||
(set-multiaccount-pairing multiaccount pairing paired-on))
|
||||
(when (= flow :login)
|
||||
@@ -474,7 +515,7 @@
|
||||
(when (= flow :create)
|
||||
(mnemonic/set-mnemonic)))))
|
||||
|
||||
(fx/defn on-pair-error
|
||||
(rf/defn on-pair-error
|
||||
{:events [:keycard.callback/on-pair-error]}
|
||||
[{:keys [db] :as cofx} {:keys [error code]}]
|
||||
(log/debug "[keycard] pair error: " error)
|
||||
@@ -483,7 +524,7 @@
|
||||
flow (get-in db [:keycard :flow])]
|
||||
(log/debug "[keycard] on-pair-error" setup-step "flow:" flow)
|
||||
(when-not tag-was-lost?
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{:db (assoc-in db [:keycard :setup-error] (i18n/label :t/invalid-pairing-password))}
|
||||
(common/set-on-card-connected (if (= setup-step :pairing)
|
||||
:keycard/load-pairing-screen
|
||||
@@ -494,17 +535,19 @@
|
||||
(when (not= setup-step :enter-pair-code)
|
||||
(common/process-error code error))))))
|
||||
|
||||
(fx/defn set-setup-step
|
||||
(rf/defn set-setup-step
|
||||
[{:keys [db]} card-state]
|
||||
{:db (assoc-in db [:keycard :setup-step]
|
||||
(case card-state
|
||||
:not-paired :pair
|
||||
:no-pairing-slots :no-slots
|
||||
:init :card-ready
|
||||
:multiaccount :import-multiaccount
|
||||
:begin))})
|
||||
{:db (assoc-in db
|
||||
[:keycard :setup-step]
|
||||
(case card-state
|
||||
:not-paired :pair
|
||||
:no-pairing-slots :no-slots
|
||||
:init :card-ready
|
||||
: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)
|
||||
:content (i18n/label :t/keycard-applet-install-instructions)
|
||||
:cancel-button-text ""
|
||||
@@ -512,17 +555,17 @@
|
||||
|
||||
;; 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.
|
||||
(fx/defn check-card-state
|
||||
(rf/defn check-card-state
|
||||
{:events [:keycard/check-card-state]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(let [app-info (get-in db [:keycard :application-info])
|
||||
flow (get-in db [:keycard :flow])
|
||||
(let [app-info (get-in db [:keycard :application-info])
|
||||
flow (get-in db [:keycard :flow])
|
||||
{: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"
|
||||
"card-state" card-state
|
||||
"flow" flow)
|
||||
(fx/merge cofx
|
||||
"flow" flow)
|
||||
(rf/merge cofx
|
||||
{:db (assoc-in db [:keycard :card-state] card-state)}
|
||||
(set-setup-step card-state)
|
||||
|
||||
@@ -538,7 +581,7 @@
|
||||
(if (= flow :import)
|
||||
(navigation/navigate-to-cofx :keycard-recovery-no-key nil)
|
||||
(fn [cofx]
|
||||
(fx/merge
|
||||
(rf/merge
|
||||
cofx
|
||||
(common/clear-on-card-read)
|
||||
(load-pin-screen)))))
|
||||
@@ -560,72 +603,72 @@
|
||||
(#{:create :recovery} flow))
|
||||
(show-keycard-has-multiaccount-alert)))))
|
||||
|
||||
(fx/defn on-card-connected
|
||||
(rf/defn on-card-connected
|
||||
{:events [:keycard.callback/on-card-connected]}
|
||||
[{:keys [db]} _]
|
||||
(log/debug "[keycard] card globally connected")
|
||||
{:db (assoc-in db [:keycard :card-connected?] true)})
|
||||
|
||||
(fx/defn on-card-disconnected
|
||||
(rf/defn on-card-disconnected
|
||||
{:events [:keycard.callback/on-card-disconnected]}
|
||||
[{:keys [db]} _]
|
||||
(log/debug "[keycard] card disconnected")
|
||||
{: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]}
|
||||
[{:keys [db]} _]
|
||||
(log/debug "[keycard] nfc user cancelled")
|
||||
{:dispatch [:signing.ui/cancel-is-pressed]})
|
||||
|
||||
(fx/defn on-nfc-timeout
|
||||
(rf/defn on-nfc-timeout
|
||||
{:events [:keycard.callback/on-nfc-timeout]}
|
||||
[{:keys [db]} _]
|
||||
(log/debug "[keycard] nfc timeout")
|
||||
{:db (-> db
|
||||
(assoc-in [:keycard :nfc-running?] false)
|
||||
(assoc-in [:keycard :card-connected?] false))
|
||||
{:db (-> db
|
||||
(assoc-in [:keycard :nfc-running?] false)
|
||||
(assoc-in [:keycard :card-connected?] false))
|
||||
: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]}
|
||||
[{:keys [db]} 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]}
|
||||
[{:keys [db]}]
|
||||
{: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]}
|
||||
[{:keys [db]} step]
|
||||
(when-not (empty? (get-in db [:keycard :pin step]))
|
||||
{:db (update-in db [:keycard :pin step] pop)}))
|
||||
|
||||
(fx/defn start-nfc
|
||||
(rf/defn start-nfc
|
||||
{:events [:keycard.ui/start-nfc]}
|
||||
[cofx]
|
||||
{:keycard/start-nfc nil})
|
||||
|
||||
(fx/defn stop-nfc
|
||||
(rf/defn stop-nfc
|
||||
{:events [:keycard.ui/stop-nfc]}
|
||||
[cofx]
|
||||
{:keycard/stop-nfc nil
|
||||
{:keycard/stop-nfc nil
|
||||
:keycard.callback/on-card-disconnected nil})
|
||||
|
||||
(fx/defn start-nfc-success
|
||||
(rf/defn start-nfc-success
|
||||
{:events [:keycard.callback/start-nfc-success]}
|
||||
[{:keys [db]} _]
|
||||
(log/debug "[keycard] nfc started success")
|
||||
{:db (assoc-in db [:keycard :nfc-running?] true)})
|
||||
|
||||
(fx/defn start-nfc-failure
|
||||
(rf/defn start-nfc-failure
|
||||
{:events [:keycard.callback/start-nfc-failure]}
|
||||
[{:keys [db]} _]
|
||||
(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]}
|
||||
[{:keys [db]} _]
|
||||
(log/debug "[keycard] nfc stopped success")
|
||||
@@ -634,7 +677,7 @@
|
||||
(assoc-in [:keycard :nfc-running?] false)
|
||||
(assoc-in [:keycard :card-connected?] false))})
|
||||
|
||||
(fx/defn stop-nfc-failure
|
||||
(rf/defn stop-nfc-failure
|
||||
{:events [:keycard.callback/stop-nfc-failure]}
|
||||
[{:keys [db]} _]
|
||||
(log/debug "[keycard] nfc failed stopping")) ;; leave current value on :nfc-running
|
||||
|
||||
@@ -1,44 +1,46 @@
|
||||
(ns status-im.keycard.delete-key
|
||||
(:require [status-im2.navigation.events :as navigation]
|
||||
[status-im.utils.fx :as fx]
|
||||
[status-im.keycard.common :as common]))
|
||||
(:require [status-im.keycard.common :as common]
|
||||
[status-im2.navigation.events :as navigation]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(fx/defn reset-card-pressed
|
||||
(rf/defn reset-card-pressed
|
||||
{:events [:keycard-settings.ui/reset-card-pressed]}
|
||||
[cofx]
|
||||
(navigation/navigate-to-cofx cofx :reset-card nil))
|
||||
|
||||
(fx/defn delete-card
|
||||
(rf/defn delete-card
|
||||
[{: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])]
|
||||
(if (and key-uid
|
||||
(= key-uid multiaccount-key-uid))
|
||||
{:keycard/delete nil}
|
||||
(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]}
|
||||
[cofx]
|
||||
(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]}
|
||||
[{:keys [db]}]
|
||||
{:db (assoc-in db [:keycard :reset-card :disabled?] true)
|
||||
: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]}
|
||||
[{:keys [db] :as cofx} keep-keys-on-keycard?]
|
||||
(fx/merge cofx
|
||||
{:db (assoc-in db [:keycard :pin] {:enter-step :current
|
||||
:current []
|
||||
:puk []
|
||||
:status nil
|
||||
:error-label nil
|
||||
:on-verified (if keep-keys-on-keycard?
|
||||
:keycard/unpair-and-delete
|
||||
:keycard/remove-key-with-unpair)})}
|
||||
(rf/merge cofx
|
||||
{:db (assoc-in db
|
||||
[:keycard :pin]
|
||||
{:enter-step :current
|
||||
:current []
|
||||
:puk []
|
||||
:status nil
|
||||
:error-label nil
|
||||
: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/navigate-to-enter-pin-screen)))
|
||||
|
||||
@@ -1,47 +1,49 @@
|
||||
(ns status-im.keycard.export-key
|
||||
(:require [status-im.utils.fx :as fx]
|
||||
[taoensso.timbre :as log]
|
||||
(:require [status-im.keycard.common :as common]
|
||||
[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]}
|
||||
[{:keys [db] :as cofx} error]
|
||||
(log/debug "[keycard] export key error" error)
|
||||
(let [tag-was-lost? (common/tag-lost? (:error error))
|
||||
pin-retries (common/pin-retries (:error error))]
|
||||
(cond tag-was-lost?
|
||||
(fx/merge cofx
|
||||
{:db (assoc-in db [:keycard :pin :status] nil)}
|
||||
(common/set-on-card-connected :wallet.accounts/generate-new-keycard-account))
|
||||
pin-retries (common/pin-retries (:error error))]
|
||||
(cond
|
||||
tag-was-lost?
|
||||
(rf/merge cofx
|
||||
{:db (assoc-in db [:keycard :pin :status] nil)}
|
||||
(common/set-on-card-connected :wallet.accounts/generate-new-keycard-account))
|
||||
|
||||
(not (nil? pin-retries))
|
||||
(fx/merge cofx
|
||||
{:db (-> db
|
||||
(assoc-in [:keycard :application-info :pin-retry-counter] pin-retries)
|
||||
(update-in [:keycard :pin] assoc
|
||||
:status :error
|
||||
:enter-step :export-key
|
||||
:puk []
|
||||
:current []
|
||||
:original []
|
||||
:confirmation []
|
||||
:sign []
|
||||
:export-key []
|
||||
:error-label :t/pin-mismatch))}
|
||||
(common/hide-connection-sheet)
|
||||
(when (zero? pin-retries) (common/frozen-keycard-popup)))
|
||||
:else
|
||||
(fx/merge cofx
|
||||
(common/show-wrong-keycard-alert)
|
||||
(common/clear-pin)
|
||||
(common/hide-connection-sheet)))))
|
||||
(not (nil? pin-retries))
|
||||
(rf/merge cofx
|
||||
{:db (-> db
|
||||
(assoc-in [:keycard :application-info :pin-retry-counter] pin-retries)
|
||||
(update-in [:keycard :pin]
|
||||
assoc
|
||||
:status :error
|
||||
:enter-step :export-key
|
||||
:puk []
|
||||
:current []
|
||||
:original []
|
||||
:confirmation []
|
||||
:sign []
|
||||
:export-key []
|
||||
:error-label :t/pin-mismatch))}
|
||||
(common/hide-connection-sheet)
|
||||
(when (zero? pin-retries) (common/frozen-keycard-popup)))
|
||||
:else
|
||||
(rf/merge cofx
|
||||
(common/show-wrong-keycard-alert)
|
||||
(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]}
|
||||
[{:keys [db] :as cofx} pubkey]
|
||||
(let [callback-fn (get-in db [:keycard :on-export-success])]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{:dispatch (callback-fn pubkey)}
|
||||
(wallet/hide-pin-sheet)
|
||||
(common/clear-pin)
|
||||
|
||||
@@ -1,70 +1,73 @@
|
||||
(ns status-im.keycard.login
|
||||
(:require [status-im.ethereum.core :as ethereum]
|
||||
[status-im2.navigation.events :as navigation]
|
||||
[status-im.utils.fx :as fx]
|
||||
[status-im.utils.types :as types]
|
||||
[taoensso.timbre :as log]
|
||||
(:require [status-im.bottom-sheet.core :as bottom-sheet]
|
||||
[status-im.ethereum.core :as ethereum]
|
||||
[status-im.keycard.common :as common]
|
||||
[status-im.keycard.recovery :as recovery]
|
||||
[status-im.keycard.onboarding :as onboarding]
|
||||
status-im.keycard.fx
|
||||
[status-im.bottom-sheet.core :as bottom-sheet]
|
||||
[status-im.signing.core :as signing.core]))
|
||||
[status-im.keycard.onboarding :as onboarding]
|
||||
[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
|
||||
:keycard.login.pin.ui/cancel-pressed]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{:db db}
|
||||
(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]}
|
||||
[cofx]
|
||||
(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]}
|
||||
[cofx]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(bottom-sheet/hide-bottom-sheet)
|
||||
(onboarding/start-onboarding-flow)))
|
||||
|
||||
(fx/defn login-add-key-pressed
|
||||
(rf/defn login-add-key-pressed
|
||||
{:events [:keycard.login.ui/add-key-pressed]}
|
||||
[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]}
|
||||
[{:keys [db] :as cofx} value]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{: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]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(log/debug "[keycard] load-pair-card-pressed")
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{:db (assoc-in db [:keycard :flow] :login)}
|
||||
(navigation/navigate-to-cofx :keycard-recovery-pair nil)))
|
||||
|
||||
(fx/defn reset-pin
|
||||
(rf/defn reset-pin
|
||||
{:events [::reset-pin]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(fx/merge
|
||||
(rf/merge
|
||||
cofx
|
||||
(signing.core/discard)
|
||||
(fn [{:keys [db]}]
|
||||
{:db (-> db
|
||||
(dissoc :popover/popover)
|
||||
(update-in [:keycard :pin] dissoc
|
||||
:reset :puk)
|
||||
(update-in [:keycard :pin] assoc
|
||||
:enter-step :reset
|
||||
:error nil
|
||||
:status nil))
|
||||
{:db (-> db
|
||||
(dissoc :popover/popover)
|
||||
(update-in [:keycard :pin]
|
||||
dissoc
|
||||
:reset
|
||||
:puk)
|
||||
(update-in [:keycard :pin]
|
||||
assoc
|
||||
:enter-step :reset
|
||||
:error nil
|
||||
:status nil))
|
||||
:hide-popover nil})
|
||||
(when (:multiaccount db)
|
||||
(navigation/change-tab :profile))
|
||||
@@ -73,50 +76,51 @@
|
||||
(navigation/navigate-replace :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]}
|
||||
[{:keys [db]}]
|
||||
{:db (-> db
|
||||
(dissoc :popover/popover)
|
||||
(update :keycard dissoc :setup-step))
|
||||
{:db (-> db
|
||||
(dissoc :popover/popover)
|
||||
(update :keycard dissoc :setup-step))
|
||||
:hide-popover nil})
|
||||
|
||||
(fx/defn login-with-keycard
|
||||
(rf/defn login-with-keycard
|
||||
{:events [:keycard/login-with-keycard]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(let [{:keys [:pin-retry-counter :puk-retry-counter]
|
||||
:as application-info}
|
||||
:as application-info}
|
||||
(get-in db [:keycard :application-info])
|
||||
|
||||
key-uid (get-in db [:keycard :application-info :key-uid])
|
||||
paired? (get-in db [:keycard :application-info :paired?])
|
||||
multiaccount (get-in db [:multiaccounts/multiaccounts (get-in db [:multiaccounts/login :key-uid])])
|
||||
multiaccount-key-uid (get multiaccount :key-uid)
|
||||
key-uid (get-in db [:keycard :application-info :key-uid])
|
||||
paired? (get-in db [:keycard :application-info :paired?])
|
||||
multiaccount (get-in db
|
||||
[:multiaccounts/multiaccounts (get-in db [:multiaccounts/login :key-uid])])
|
||||
multiaccount-key-uid (get multiaccount :key-uid)
|
||||
multiaccount-mismatch? (or (nil? multiaccount)
|
||||
(not= multiaccount-key-uid key-uid))]
|
||||
(log/debug "[keycard] login-with-keycard"
|
||||
"empty application info" (empty? application-info)
|
||||
"no key-uid" (empty? key-uid)
|
||||
"no key-uid" (empty? key-uid)
|
||||
"multiaccount-mismatch?" multiaccount-mismatch?
|
||||
"no pairing" paired?)
|
||||
"no pairing" paired?)
|
||||
(cond
|
||||
(empty? application-info)
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(common/hide-connection-sheet)
|
||||
(navigation/navigate-to-cofx :not-keycard nil))
|
||||
|
||||
(empty? key-uid)
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(common/hide-connection-sheet)
|
||||
(navigation/navigate-to-cofx :keycard-blank nil))
|
||||
|
||||
multiaccount-mismatch?
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(common/hide-connection-sheet)
|
||||
(navigation/navigate-to-cofx :keycard-wrong nil))
|
||||
|
||||
(not paired?)
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(common/hide-connection-sheet)
|
||||
(navigation/navigate-to-cofx :keycard-unpaired nil))
|
||||
|
||||
@@ -128,7 +132,7 @@
|
||||
:else
|
||||
(common/get-keys-from-keycard cofx))))
|
||||
|
||||
(fx/defn proceed-to-login
|
||||
(rf/defn proceed-to-login
|
||||
{:events [::login-after-reset]}
|
||||
[cofx]
|
||||
(log/debug "[keycard] proceed-to-login")
|
||||
@@ -139,12 +143,14 @@
|
||||
:on-card-read :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]}
|
||||
[{:keys [db] :as cofx} key-uid [encryption-public-key whisper-private-key :as creds]]
|
||||
(if (nil? creds)
|
||||
(navigation/set-stack-root cofx :multiaccounts-stack [:multiaccounts
|
||||
:keycard-login-pin])
|
||||
(navigation/set-stack-root cofx
|
||||
:multiaccounts-stack
|
||||
[:multiaccounts
|
||||
:keycard-login-pin])
|
||||
(let [{:keys [identicon name]} (get-in db [:multiaccounts/multiaccounts key-uid])
|
||||
multiaccount-data (types/clj->json {:name name
|
||||
:key-uid key-uid
|
||||
@@ -160,18 +166,18 @@
|
||||
(update account-data :whisper-public-key ethereum/normalized-hex))
|
||||
(assoc-in [:keycard :flow] nil)
|
||||
(update :multiaccounts/login assoc
|
||||
:password encryption-public-key
|
||||
:key-uid key-uid
|
||||
:identicon identicon
|
||||
:name name
|
||||
:save-password? true))
|
||||
:password encryption-public-key
|
||||
:key-uid key-uid
|
||||
:identicon identicon
|
||||
:name name
|
||||
:save-password? true))
|
||||
:keycard/login-with-keycard
|
||||
{:multiaccount-data multiaccount-data
|
||||
:key-uid key-uid
|
||||
:password encryption-public-key
|
||||
:chat-key whisper-private-key}})))
|
||||
|
||||
(fx/defn on-login-success
|
||||
(rf/defn on-login-success
|
||||
{:events [:keycard.login.callback/login-success]}
|
||||
[_ result]
|
||||
(log/debug "loginWithKeycard success: " result))
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
(ns status-im.keycard.mnemonic
|
||||
(:require [status-im2.navigation.events :as navigation]
|
||||
[status-im.utils.fx :as fx]
|
||||
(:require [status-im.keycard.common :as common]
|
||||
status-im.keycard.fx
|
||||
[status-im2.navigation.events :as navigation]
|
||||
[taoensso.timbre :as log]
|
||||
[status-im.keycard.common :as common]
|
||||
status-im.keycard.fx))
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(fx/defn set-mnemonic
|
||||
(rf/defn set-mnemonic
|
||||
[{:keys [db] :as cofx}]
|
||||
(log/debug "[keycard] set-mnemonic")
|
||||
(let [selected-id (get-in db [:intro-wizard :selected-id])
|
||||
mnemonic (reduce
|
||||
(fn [_ {:keys [id mnemonic]}]
|
||||
(when (= selected-id id)
|
||||
(reduced mnemonic)))
|
||||
nil
|
||||
(get-in db [:intro-wizard :multiaccounts]))]
|
||||
(fx/merge
|
||||
mnemonic (reduce
|
||||
(fn [_ {:keys [id mnemonic]}]
|
||||
(when (= selected-id id)
|
||||
(reduced mnemonic)))
|
||||
nil
|
||||
(get-in db [:intro-wizard :multiaccounts]))]
|
||||
(rf/merge
|
||||
cofx
|
||||
{:db (-> db
|
||||
(assoc-in [:keycard :setup-step] :recovery-phrase)
|
||||
@@ -24,11 +24,11 @@
|
||||
(common/hide-connection-sheet)
|
||||
(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
|
||||
:keycard/load-loading-keys-screen]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(fx/merge
|
||||
(rf/merge
|
||||
cofx
|
||||
{:db (assoc-in db [:keycard :setup-step] :loading-keys)}
|
||||
(common/show-connection-sheet
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
(ns status-im.keycard.onboarding
|
||||
(:require [clojure.string :as string]
|
||||
[re-frame.core :as re-frame]
|
||||
[status-im.constants :as constants]
|
||||
[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.mnemonic :as mnemonic]
|
||||
[taoensso.timbre :as log]
|
||||
status-im.keycard.fx
|
||||
[status-im.keycard.mnemonic :as mnemonic]
|
||||
[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 [db]}]
|
||||
{:db (-> db
|
||||
@@ -21,7 +21,7 @@
|
||||
(assoc-in [:keycard :pin :original] [])
|
||||
(assoc-in [:keycard :pin :confirmation] []))})
|
||||
|
||||
(fx/defn start-installation
|
||||
(rf/defn start-installation
|
||||
[{:keys [db] :as cofx}]
|
||||
(let [card-state (get-in db [:keycard :card-state])
|
||||
pin (common/vector->string (get-in db [:keycard :pin :original]))]
|
||||
@@ -33,12 +33,12 @@
|
||||
|
||||
(do
|
||||
(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)
|
||||
:content (i18n/label :t/something-went-wrong)}}
|
||||
(navigation/navigate-to-cofx :keycard-authentication-method nil))))))
|
||||
|
||||
(fx/defn load-preparing-screen
|
||||
(rf/defn load-preparing-screen
|
||||
{:events [:keycard/load-preparing-screen]}
|
||||
[cofx]
|
||||
(common/show-connection-sheet
|
||||
@@ -47,11 +47,11 @@
|
||||
:on-card-connected :keycard/load-preparing-screen
|
||||
:handler start-installation}))
|
||||
|
||||
(fx/defn load-pairing-screen
|
||||
(rf/defn load-pairing-screen
|
||||
{:events [:keycard/load-pairing-screen
|
||||
:keycard.onboarding.puk-code.ui/confirm-pressed]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(fx/merge
|
||||
(rf/merge
|
||||
cofx
|
||||
{:db (assoc-in db [:keycard :setup-step] :pairing)}
|
||||
(common/show-connection-sheet
|
||||
@@ -59,21 +59,22 @@
|
||||
:on-card-connected :keycard/load-pairing-screen
|
||||
: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]}
|
||||
[_]
|
||||
{:ui/show-confirmation {:title (i18n/label :t/secret-keys-confirmation-title)
|
||||
:content (i18n/label :t/secret-keys-confirmation-text)
|
||||
:confirm-button-text (i18n/label :t/yes)
|
||||
: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 #()}})
|
||||
|
||||
(fx/defn load-finishing-screen
|
||||
(rf/defn load-finishing-screen
|
||||
{:events [:keycard.onboarding.recovery-phrase-confirm-word2.ui/next-pressed
|
||||
:keycard/load-finishing-screen]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(fx/merge
|
||||
(rf/merge
|
||||
cofx
|
||||
{:db (assoc-in db [:keycard :setup-step] :loading-keys)}
|
||||
(common/show-connection-sheet
|
||||
@@ -81,29 +82,30 @@
|
||||
:on-card-connected :keycard/load-finishing-screen
|
||||
: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]}
|
||||
[_]
|
||||
(.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
|
||||
:keycard.ui/recovery-phrase-next-button-pressed]}
|
||||
[_]
|
||||
{:ui/show-confirmation {:title (i18n/label :t/keycard-recovery-phrase-confirmation-title)
|
||||
:content (i18n/label :t/keycard-recovery-phrase-confirmation-text)
|
||||
{:ui/show-confirmation {:title (i18n/label :t/keycard-recovery-phrase-confirmation-title)
|
||||
:content (i18n/label :t/keycard-recovery-phrase-confirmation-text)
|
||||
:confirm-button-text (i18n/label :t/yes)
|
||||
:cancel-button-text (i18n/label :t/cancel)
|
||||
:on-accept #(re-frame/dispatch [:keycard.onboarding.recovery-phrase.ui/confirm-pressed])
|
||||
:on-cancel #()}})
|
||||
:cancel-button-text (i18n/label :t/cancel)
|
||||
:on-accept #(re-frame/dispatch
|
||||
[: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}]
|
||||
(let [mnemonic (get-in db [:keycard :secrets :mnemonic])
|
||||
[word1 word2] (shuffle (map-indexed vector (clojure.string/split mnemonic #" ")))
|
||||
word1 (zipmap [:idx :word] word1)
|
||||
word2 (zipmap [:idx :word] word2)]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{:db (-> db
|
||||
(assoc-in [:keycard :setup-step] :recovery-phrase-confirm-word1)
|
||||
(assoc-in [:keycard :recovery-phrase :step] :word1)
|
||||
@@ -113,14 +115,14 @@
|
||||
(assoc-in [:keycard :recovery-phrase :word2] word2))}
|
||||
(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]}
|
||||
[cofx]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(recovery-phrase-start-confirmation)
|
||||
(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]}]
|
||||
{:db (-> db
|
||||
(assoc-in [:keycard :recovery-phrase :step] :word2)
|
||||
@@ -128,50 +130,52 @@
|
||||
(assoc-in [:keycard :recovery-phrase :input-word] nil)
|
||||
(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}]
|
||||
(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])))]
|
||||
(if (empty? pin)
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{:db (-> db
|
||||
(assoc-in [:keycard :pin] {:enter-step :current
|
||||
:on-verified :keycard/generate-and-load-key
|
||||
:current []})
|
||||
(assoc-in [:keycard :pin]
|
||||
{:enter-step :current
|
||||
:on-verified :keycard/generate-and-load-key
|
||||
:current []})
|
||||
(assoc-in [:keycard :setup-step] :loading-keys))}
|
||||
(navigation/navigate-to-cofx :keycard-onboarding-pin nil))
|
||||
(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
|
||||
:keycard.onboarding.recovery-phrase-confirm-word.ui/input-submitted]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(let [step (get-in db [:keycard :recovery-phrase :step])
|
||||
input-word (get-in db [:keycard :recovery-phrase :input-word])
|
||||
(let [step (get-in db [:keycard :recovery-phrase :step])
|
||||
input-word (get-in db [:keycard :recovery-phrase :input-word])
|
||||
{:keys [word]} (get-in db [:keycard :recovery-phrase step])]
|
||||
(if (= word input-word)
|
||||
(if (= (:view-id db) :keycard-onboarding-recovery-phrase-confirm-word1)
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(recovery-phrase-next-word)
|
||||
(navigation/navigate-replace :keycard-onboarding-recovery-phrase-confirm-word2 nil))
|
||||
(proceed-with-generating-key cofx))
|
||||
{: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]}
|
||||
[{:keys [db]} 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]}
|
||||
[{:keys [db]} 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]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(let [flow (get-in db [:keycard :flow])]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{:keycard/check-nfc-enabled nil}
|
||||
(if (= flow :import)
|
||||
(navigation/navigate-to-cofx :keycard-recovery-intro nil)
|
||||
@@ -179,33 +183,36 @@
|
||||
(common/listen-to-hardware-back-button)
|
||||
(navigation/navigate-to-cofx :keycard-onboarding-intro nil))))))
|
||||
|
||||
(fx/defn start-onboarding-flow
|
||||
(rf/defn start-onboarding-flow
|
||||
{:events [:keycard/start-onboarding-flow]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(fx/merge cofx
|
||||
{:db (assoc-in db [:keycard :flow] :create)
|
||||
(rf/merge cofx
|
||||
{:db (assoc-in db [:keycard :flow] :create)
|
||||
:keycard/check-nfc-enabled nil}
|
||||
(common/listen-to-hardware-back-button)
|
||||
(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]}
|
||||
[_]
|
||||
{:keycard/open-nfc-settings nil})
|
||||
|
||||
(defn- show-recover-confirmation []
|
||||
(defn- show-recover-confirmation
|
||||
[]
|
||||
{:ui/show-confirmation {:title (i18n/label :t/are-you-sure?)
|
||||
:content (i18n/label :t/are-you-sure-description)
|
||||
:confirm-button-text (clojure.string/upper-case (i18n/label :t/yes))
|
||||
:cancel-button-text (i18n/label :t/see-it-again)
|
||||
:on-accept #(re-frame/dispatch [:keycard.ui/recovery-phrase-confirm-pressed])
|
||||
:on-cancel #(re-frame/dispatch [:keycard.ui/recovery-phrase-cancel-pressed])}})
|
||||
:on-accept #(re-frame/dispatch
|
||||
[: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]}
|
||||
[{:keys [db]}]
|
||||
(let [step (get-in db [:keycard :recovery-phrase :step])
|
||||
input-word (get-in db [:keycard :recovery-phrase :input-word])
|
||||
(let [step (get-in db [:keycard :recovery-phrase :step])
|
||||
input-word (get-in db [:keycard :recovery-phrase :input-word])
|
||||
{:keys [word]} (get-in db [:keycard :recovery-phrase step])]
|
||||
(if (= word input-word)
|
||||
(if (= step :word1)
|
||||
@@ -213,21 +220,21 @@
|
||||
(show-recover-confirmation))
|
||||
{: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}]
|
||||
(if (= (get-in db [:keycard :flow]) :create)
|
||||
(recovery-phrase-start-confirmation cofx)
|
||||
(let [mnemonic (get-in db [:multiaccounts/recover :passphrase])]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{:db (assoc-in db [:keycard :secrets :mnemonic] mnemonic)}
|
||||
(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
|
||||
:keycard.callback/on-init-card-success]}
|
||||
[{:keys [db] :as cofx} secrets]
|
||||
(let [secrets' (js->clj secrets :keywordize-keys true)]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{:db (-> db
|
||||
(assoc-in [:keycard :card-state] :init)
|
||||
(assoc-in [:keycard :setup-step] :secret-keys)
|
||||
@@ -237,17 +244,17 @@
|
||||
:on-card-read :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
|
||||
:keycard.callback/on-init-card-error]}
|
||||
[{:keys [db] :as cofx} {:keys [code 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)}
|
||||
(common/set-on-card-connected :keycard/load-preparing-screen)
|
||||
(common/process-error code error)))
|
||||
|
||||
(fx/defn generate-and-load-key
|
||||
(rf/defn generate-and-load-key
|
||||
{:events [:keycard/generate-and-load-key]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(let [{:keys [pin]}
|
||||
@@ -256,36 +263,41 @@
|
||||
{:keys [selected-id multiaccounts]}
|
||||
(:intro-wizard db)
|
||||
|
||||
multiaccount (or (->> multiaccounts
|
||||
(filter #(= (:id %) selected-id))
|
||||
first)
|
||||
(assoc (get-in db [:intro-wizard :root-key])
|
||||
:derived
|
||||
(get-in db [:intro-wizard :derived])))
|
||||
recovery-mnemonic (get-in db [:intro-wizard :passphrase])
|
||||
mnemonic (or (:mnemonic multiaccount)
|
||||
recovery-mnemonic)
|
||||
pin' (or pin (common/vector->string (get-in db [:keycard :pin :current])))]
|
||||
(fx/merge cofx
|
||||
multiaccount (or (->> multiaccounts
|
||||
(filter #(= (:id %) selected-id))
|
||||
first)
|
||||
(assoc (get-in db [:intro-wizard :root-key])
|
||||
:derived
|
||||
(get-in db [:intro-wizard :derived])))
|
||||
recovery-mnemonic (get-in db [:intro-wizard :passphrase])
|
||||
mnemonic (or (:mnemonic multiaccount)
|
||||
recovery-mnemonic)
|
||||
pin' (or pin
|
||||
(common/vector->string (get-in db
|
||||
[:keycard :pin
|
||||
:current])))]
|
||||
(rf/merge cofx
|
||||
{:keycard/generate-and-load-key
|
||||
{:mnemonic mnemonic
|
||||
:pin pin'
|
||||
:key-uid (:key-uid multiaccount)
|
||||
: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]}
|
||||
[{:keys [db] :as cofx} 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]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(fx/merge
|
||||
(rf/merge
|
||||
cofx
|
||||
{:db (-> db
|
||||
(update :keycard
|
||||
dissoc :secrets :card-state :multiaccount-wallet-address
|
||||
dissoc
|
||||
:secrets
|
||||
:card-state :multiaccount-wallet-address
|
||||
:multiaccount-whisper-public-key :application-info)
|
||||
(assoc-in [:keycard :setup-step] :begin)
|
||||
(assoc-in [:keycard :pin :on-verified] nil))}
|
||||
@@ -301,7 +313,7 @@
|
||||
:on-card-read :keycard/check-card-state
|
||||
:handler (common/get-application-info :keycard/check-card-state)}))))
|
||||
|
||||
(fx/defn factory-reset
|
||||
(rf/defn factory-reset
|
||||
{:events [::factory-reset]}
|
||||
[cofx]
|
||||
(common/show-connection-sheet
|
||||
@@ -310,14 +322,14 @@
|
||||
:on-card-read :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]}
|
||||
[{:keys [db] :as cofx}]
|
||||
{:db (update db :keycard dissoc :factory-reset-card?)})
|
||||
|
||||
(fx/defn cancel-pressed
|
||||
(rf/defn cancel-pressed
|
||||
{:events [::cancel-pressed]}
|
||||
[cofx]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(navigation/navigate-back)
|
||||
(common/cancel-sheet-confirm)))
|
||||
|
||||
+156
-136
@@ -1,30 +1,31 @@
|
||||
(ns status-im.keycard.recovery
|
||||
(:require [status-im2.navigation.events :as navigation]
|
||||
[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]
|
||||
(:require [clojure.string :as string]
|
||||
[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]
|
||||
[taoensso.timbre :as log]
|
||||
[status-im.keycard.common :as common]
|
||||
status-im.keycard.fx
|
||||
[status-im.constants :as constants]
|
||||
[status-im.ethereum.eip55 :as eip55]
|
||||
[status-im.ethereum.core :as ethereum]
|
||||
[status-im.bottom-sheet.core :as bottom-sheet]
|
||||
[status-im.multiaccounts.create.core :as multiaccounts.create]
|
||||
[status-im.multiaccounts.model :as multiaccounts.model]
|
||||
[status-im.native-module.core :as status]
|
||||
[status-im.popover.core :as popover]
|
||||
[status-im.utils.types :as types]
|
||||
[utils.security.core :as security]
|
||||
[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.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}})
|
||||
|
||||
(fx/defn pair
|
||||
(rf/defn pair
|
||||
{:events [:keycard/pair]}
|
||||
[cofx]
|
||||
(let [{:keys [password]} (get-in cofx [:db :keycard :secrets])]
|
||||
@@ -33,41 +34,41 @@
|
||||
{:on-card-connected :keycard/pair
|
||||
:handler (pair* password)})))
|
||||
|
||||
(fx/defn pair-code-next-button-pressed
|
||||
(rf/defn pair-code-next-button-pressed
|
||||
{:events [:keycard.onboarding.pair.ui/input-submitted
|
||||
:keycard.ui/pair-code-next-button-pressed
|
||||
:keycard.onboarding.pair.ui/next-pressed]}
|
||||
[{: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))]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(if pairing
|
||||
{:db (-> db
|
||||
(assoc-in [:keycard :setup-step] :import-multiaccount)
|
||||
(assoc-in [:keycard :secrets :paired-on] paired-on))}
|
||||
(pair)))))
|
||||
|
||||
(fx/defn load-pair-screen
|
||||
(rf/defn load-pair-screen
|
||||
[{:keys [db] :as cofx}]
|
||||
(log/debug "[keycard] load-pair-screen")
|
||||
(fx/merge cofx
|
||||
{:db (-> db
|
||||
(assoc-in [:keycard :setup-step] :pair))
|
||||
(rf/merge cofx
|
||||
{:db (-> db
|
||||
(assoc-in [:keycard :setup-step] :pair))
|
||||
:dispatch [:bottom-sheet/hide]}
|
||||
(common/listen-to-hardware-back-button)
|
||||
(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]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{:db (assoc-in db [:keycard :flow] :recovery)}
|
||||
(navigation/navigate-to-cofx :keycard-recovery-enter-mnemonic nil)))
|
||||
|
||||
(fx/defn start-import-flow
|
||||
(rf/defn start-import-flow
|
||||
{:events [::recover-with-keycard-pressed]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{:db
|
||||
(-> db
|
||||
(assoc-in [:keycard :flow] :import)
|
||||
@@ -76,35 +77,37 @@
|
||||
(bottom-sheet/hide-bottom-sheet)
|
||||
(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]}
|
||||
[_]
|
||||
{:dispatch [:bottom-sheet/show-sheet :recover-sheet]})
|
||||
|
||||
(fx/defn recovery-keycard-selected
|
||||
(rf/defn recovery-keycard-selected
|
||||
{:events [:recovery.ui/keycard-option-pressed]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(fx/merge cofx
|
||||
{:db (assoc-in db [:keycard :flow] :recovery)
|
||||
(rf/merge cofx
|
||||
{:db (assoc-in db [:keycard :flow] :recovery)
|
||||
:keycard/check-nfc-enabled nil}
|
||||
(common/listen-to-hardware-back-button)
|
||||
(navigation/navigate-to-cofx :keycard-onboarding-intro nil)))
|
||||
|
||||
(fx/defn cancel-pressed
|
||||
(rf/defn cancel-pressed
|
||||
{:events [::cancel-pressed]}
|
||||
[cofx]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(common/cancel-sheet-confirm)
|
||||
(navigation/navigate-back)))
|
||||
|
||||
(fx/defn begin-setup-pressed
|
||||
(rf/defn begin-setup-pressed
|
||||
{:events [:keycard.recovery.intro.ui/begin-recovery-pressed]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(fx/merge
|
||||
(rf/merge
|
||||
cofx
|
||||
{:db (-> db
|
||||
(update :keycard
|
||||
dissoc :secrets :card-state :multiaccount-wallet-address
|
||||
dissoc
|
||||
:secrets
|
||||
:card-state :multiaccount-wallet-address
|
||||
:multiaccount-whisper-public-key
|
||||
:application-info)
|
||||
(assoc-in [:keycard :setup-step] :begin)
|
||||
@@ -115,21 +118,24 @@
|
||||
:sheet-options {:on-cancel [::cancel-pressed]}
|
||||
: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]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(fx/merge cofx
|
||||
{:db (update db :keycard dissoc
|
||||
(rf/merge cofx
|
||||
{:db (update db
|
||||
:keycard dissoc
|
||||
:multiaccount-wallet-address
|
||||
:multiaccount-whisper-public-key)}
|
||||
(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]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(let [accs (get db :multiaccounts/multiaccounts)]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{:db (-> db
|
||||
(update :keycard dissoc :flow)
|
||||
(dissoc :restored-account?))}
|
||||
@@ -138,16 +144,16 @@
|
||||
(navigation/navigate-to-cofx :get-your-keys nil)
|
||||
(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]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(fx/merge cofx
|
||||
{:db (assoc-in db [:keycard :flow] :create)
|
||||
(rf/merge cofx
|
||||
{:db (assoc-in db [:keycard :flow] :create)
|
||||
:keycard/check-nfc-enabled nil}
|
||||
(intro-wizard)))
|
||||
|
||||
(fx/defn create-keycard-multiaccount
|
||||
{:events [::create-keycard-multiaccount]
|
||||
(rf/defn create-keycard-multiaccount
|
||||
{:events [::create-keycard-multiaccount]
|
||||
:interceptors [(re-frame/inject-cofx :random-guid-generator)
|
||||
(re-frame/inject-cofx ::multiaccounts.create/get-signing-phrase)]}
|
||||
[{:keys [db] :as cofx}]
|
||||
@@ -166,13 +172,14 @@
|
||||
encryption-public-key
|
||||
instance-uid
|
||||
key-uid
|
||||
recovered]} multiaccount
|
||||
{:keys [pairing paired-on]} secrets
|
||||
recovered]}
|
||||
multiaccount
|
||||
{:keys [pairing paired-on]} secrets
|
||||
{:keys [name identicon]}
|
||||
(if (nil? name)
|
||||
;; name might have been generated during recovery via passphrase
|
||||
(get-in db [:intro-wizard :derived constants/path-whisper-keyword])
|
||||
{:name name
|
||||
{:name name
|
||||
:identicon identicon})]
|
||||
;; if a name is still `nil` we have to generate it before multiaccount's
|
||||
;; creation otherwise spec validation will fail
|
||||
@@ -180,7 +187,7 @@
|
||||
{:keycard/generate-name-and-photo
|
||||
{:public-key whisper-public-key
|
||||
:on-success ::on-name-and-photo-generated}}
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{:db (-> db
|
||||
(assoc-in [:keycard :setup-step] nil)
|
||||
(dissoc :intro-wizard))}
|
||||
@@ -193,7 +200,7 @@
|
||||
{:public-key whisper-public-key
|
||||
:address (eip55/address->checksum whisper-address)
|
||||
:name name
|
||||
:identicon identicon}
|
||||
:identicon identicon}
|
||||
constants/path-default-wallet-keyword
|
||||
{:public-key wallet-public-key
|
||||
:address (eip55/address->checksum wallet-address)}}
|
||||
@@ -207,24 +214,29 @@
|
||||
encryption-public-key
|
||||
{})))))
|
||||
|
||||
(fx/defn return-to-keycard-login
|
||||
(rf/defn return-to-keycard-login
|
||||
[{:keys [db] :as cofx}]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{:db (-> db
|
||||
(update-in [:keycard :pin] assoc :enter-step :login
|
||||
:status nil
|
||||
:login [])
|
||||
(update-in [:keycard :pin]
|
||||
assoc
|
||||
:enter-step :login
|
||||
:status nil
|
||||
:login [])
|
||||
(update :keycard dissoc :application-info))}
|
||||
(navigation/set-stack-root :multiaccounts-stack [:multiaccounts
|
||||
:keycard-login-pin])))
|
||||
(navigation/set-stack-root :multiaccounts-stack
|
||||
[:multiaccounts
|
||||
:keycard-login-pin])))
|
||||
|
||||
(fx/defn on-backup-success
|
||||
(rf/defn on-backup-success
|
||||
[{:keys [db] :as cofx} backup-type]
|
||||
(fx/merge cofx
|
||||
{:utils/show-popup {:title (i18n/label (if (= backup-type :recovery-card)
|
||||
:t/keycard-access-reset :t/keycard-backup-success-title))
|
||||
:content (i18n/label (if (= backup-type :recovery-card)
|
||||
:t/keycard-can-use-with-new-passcode :t/keycard-backup-success-body))}}
|
||||
(rf/merge cofx
|
||||
{:utils/show-popup {:title (i18n/label (if (= backup-type :recovery-card)
|
||||
:t/keycard-access-reset
|
||||
:t/keycard-backup-success-title))
|
||||
:content (i18n/label (if (= backup-type :recovery-card)
|
||||
:t/keycard-can-use-with-new-passcode
|
||||
:t/keycard-backup-success-body))}}
|
||||
(cond
|
||||
(multiaccounts.model/logged-in? cofx)
|
||||
(navigation/set-stack-root :profile-stack [:my-profile :keycard-settings])
|
||||
@@ -246,36 +258,40 @@
|
||||
#(let [{:keys [error]} (types/json->clj %)]
|
||||
(if (string/blank? error)
|
||||
(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}]
|
||||
(let [pairing (get-in db [:keycard :secrets :pairing])
|
||||
paired-on (get-in db [:keycard :secrets :paired-on])
|
||||
instance-uid (get-in db [:keycard :multiaccount :instance-uid])
|
||||
account (-> db
|
||||
:multiaccounts/login
|
||||
(assoc :keycard-pairing pairing)
|
||||
(assoc :save-password? false))
|
||||
key-uid (-> account :key-uid)
|
||||
settings {:keycard-instance-uid instance-uid
|
||||
:keycard-paired-on paired-on
|
||||
:keycard-pairing pairing}
|
||||
password (ethereum/sha3 (security/safe-unmask-data (get-in db [:keycard :migration-password])))
|
||||
(let [pairing (get-in db [:keycard :secrets :pairing])
|
||||
paired-on (get-in db [:keycard :secrets :paired-on])
|
||||
instance-uid (get-in db [:keycard :multiaccount :instance-uid])
|
||||
account (-> db
|
||||
:multiaccounts/login
|
||||
(assoc :keycard-pairing pairing)
|
||||
(assoc :save-password? false))
|
||||
key-uid (-> account :key-uid)
|
||||
settings {:keycard-instance-uid instance-uid
|
||||
:keycard-paired-on paired-on
|
||||
:keycard-pairing pairing}
|
||||
password (ethereum/sha3 (security/safe-unmask-data (get-in db
|
||||
[:keycard
|
||||
:migration-password])))
|
||||
encryption-pass (get-in db [:keycard :multiaccount :encryption-public-key])
|
||||
login-params {:key-uid key-uid
|
||||
:multiaccount-data (types/clj->json account)
|
||||
:password encryption-pass
|
||||
:chat-key (get-in db [:keycard :multiaccount :whisper-private-key])}]
|
||||
{:db (-> db
|
||||
(assoc-in [:multiaccounts/multiaccounts key-uid :keycard-pairing] pairing)
|
||||
(assoc :multiaccounts/login account)
|
||||
(assoc :auth-method keychain/auth-method-none)
|
||||
(update :keycard dissoc :flow :migration-password)
|
||||
(dissoc :recovered-account?))
|
||||
login-params {:key-uid key-uid
|
||||
:multiaccount-data (types/clj->json account)
|
||||
:password encryption-pass
|
||||
:chat-key (get-in db [:keycard :multiaccount :whisper-private-key])}]
|
||||
{:db (-> db
|
||||
(assoc-in [:multiaccounts/multiaccounts key-uid :keycard-pairing] pairing)
|
||||
(assoc :multiaccounts/login account)
|
||||
(assoc :auth-method keychain/auth-method-none)
|
||||
(update :keycard dissoc :flow :migration-password)
|
||||
(dissoc :recovered-account?))
|
||||
::finish-migration [account settings password encryption-pass login-params]}))
|
||||
|
||||
(fx/defn delete-multiaccount
|
||||
(rf/defn delete-multiaccount
|
||||
[{:keys [db]}]
|
||||
(let [key-uid (get-in db [:multiaccounts/login :key-uid])]
|
||||
{:keycard/delete-multiaccount-before-migration
|
||||
@@ -283,61 +299,63 @@
|
||||
:on-error #(re-frame/dispatch [::delete-multiaccount-error %])
|
||||
:on-success #(re-frame/dispatch [::create-keycard-multiaccount])}}))
|
||||
|
||||
(fx/defn handle-delete-multiaccount-error
|
||||
(rf/defn handle-delete-multiaccount-error
|
||||
{:events [::delete-multiaccount-error]}
|
||||
[cofx _]
|
||||
(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]
|
||||
:interceptors [(re-frame/inject-cofx :random-guid-generator)
|
||||
(re-frame/inject-cofx ::multiaccounts.create/get-signing-phrase)]}
|
||||
[{:keys [db random-guid-generator] :as cofx} data]
|
||||
(let [account-data (js->clj data :keywordize-keys true)
|
||||
backup? (get-in db [:keycard :creating-backup?])
|
||||
migration? (get-in db [:keycard :converting-account?])]
|
||||
(fx/merge cofx
|
||||
{:db (-> db
|
||||
(assoc-in [:keycard :multiaccount]
|
||||
(-> account-data
|
||||
(update :address ethereum/normalized-hex)
|
||||
(update :whisper-address ethereum/normalized-hex)
|
||||
(update :wallet-address ethereum/normalized-hex)
|
||||
(update :wallet-root-address ethereum/normalized-hex)
|
||||
(update :public-key ethereum/normalized-hex)
|
||||
(update :whisper-public-key ethereum/normalized-hex)
|
||||
(update :wallet-public-key ethereum/normalized-hex)
|
||||
(update :wallet-root-public-key ethereum/normalized-hex)
|
||||
(update :instance-uid #(get-in db [:keycard :multiaccount :instance-uid] %))))
|
||||
(assoc-in [:keycard :multiaccount-wallet-address] (:wallet-address account-data))
|
||||
(assoc-in [:keycard :multiaccount-whisper-public-key] (:whisper-public-key account-data))
|
||||
(assoc-in [:keycard :pin :status] nil)
|
||||
(assoc-in [:keycard :application-info :key-uid]
|
||||
(ethereum/normalized-hex (:key-uid account-data)))
|
||||
(update :keycard dissoc :recovery-phrase :creating-backup? :converting-account?)
|
||||
(update-in [:keycard :secrets] dissoc :pin :puk :password :mnemonic)
|
||||
(assoc :multiaccounts/new-installation-id (random-guid-generator)))}
|
||||
(common/remove-listener-to-hardware-back-button)
|
||||
(common/hide-connection-sheet)
|
||||
(cond backup? (on-backup-success backup?)
|
||||
migration? (migrate-account)
|
||||
backup? (get-in db [:keycard :creating-backup?])
|
||||
migration? (get-in db [:keycard :converting-account?])]
|
||||
(rf/merge
|
||||
cofx
|
||||
{:db (-> db
|
||||
(assoc-in
|
||||
[:keycard :multiaccount]
|
||||
(-> account-data
|
||||
(update :address ethereum/normalized-hex)
|
||||
(update :whisper-address ethereum/normalized-hex)
|
||||
(update :wallet-address ethereum/normalized-hex)
|
||||
(update :wallet-root-address ethereum/normalized-hex)
|
||||
(update :public-key ethereum/normalized-hex)
|
||||
(update :whisper-public-key ethereum/normalized-hex)
|
||||
(update :wallet-public-key ethereum/normalized-hex)
|
||||
(update :wallet-root-public-key ethereum/normalized-hex)
|
||||
(update :instance-uid #(get-in db [:keycard :multiaccount :instance-uid] %))))
|
||||
(assoc-in [:keycard :multiaccount-wallet-address] (:wallet-address account-data))
|
||||
(assoc-in [:keycard :multiaccount-whisper-public-key] (:whisper-public-key account-data))
|
||||
(assoc-in [:keycard :pin :status] nil)
|
||||
(assoc-in [:keycard :application-info :key-uid]
|
||||
(ethereum/normalized-hex (:key-uid account-data)))
|
||||
(update :keycard dissoc :recovery-phrase :creating-backup? :converting-account?)
|
||||
(update-in [:keycard :secrets] dissoc :pin :puk :password :mnemonic)
|
||||
(assoc :multiaccounts/new-installation-id (random-guid-generator)))}
|
||||
(common/remove-listener-to-hardware-back-button)
|
||||
(common/hide-connection-sheet)
|
||||
(cond backup? (on-backup-success backup?)
|
||||
migration? (migrate-account)
|
||||
|
||||
(get-in db [:keycard :delete-account?])
|
||||
(delete-multiaccount)
|
||||
(get-in db [:keycard :delete-account?])
|
||||
(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]}
|
||||
[{:keys [db] :as cofx} {:keys [error code]}]
|
||||
(log/debug "[keycard] generate and load key error: " error)
|
||||
(when-not (common/tag-lost? error)
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{:db (assoc-in db [:keycard :setup-error] error)}
|
||||
(common/set-on-card-connected :keycard/load-loading-keys-screen)
|
||||
(common/process-error code error))))
|
||||
|
||||
(fx/defn import-multiaccount
|
||||
(rf/defn import-multiaccount
|
||||
{:events [:keycard/import-multiaccount]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(let [{:keys [pairing]} (get-in db [:keycard :secrets])
|
||||
@@ -345,17 +363,18 @@
|
||||
key-uid (get-in db [:keycard :application-info :key-uid])
|
||||
pairing' (or pairing (common/get-pairing db key-uid))
|
||||
pin (common/vector->string (get-in db [:keycard :pin :import-multiaccount]))]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{:db (-> db
|
||||
(assoc-in [:keycard :multiaccount :instance-uid] instance-uid)
|
||||
(assoc-in [:keycard :pin :status] :verifying)
|
||||
(assoc-in [:keycard :secrets] {:pairing pairing'
|
||||
:paired-on (utils.datetime/timestamp)}))
|
||||
(assoc-in [:keycard :secrets]
|
||||
{:pairing pairing'
|
||||
:paired-on (utils.datetime/timestamp)}))
|
||||
:keycard/import-keys
|
||||
{:pin pin
|
||||
:on-success :keycard.callback/on-generate-and-load-key-success}})))
|
||||
{:pin pin
|
||||
: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]}
|
||||
[cofx]
|
||||
(common/show-connection-sheet
|
||||
@@ -363,17 +382,18 @@
|
||||
{:on-card-connected :keycard/load-recovering-key-screen
|
||||
:handler (common/dispatch-event :keycard/import-multiaccount)}))
|
||||
|
||||
(fx/defn on-name-and-photo-generated
|
||||
{:events [::on-name-and-photo-generated]
|
||||
(rf/defn on-name-and-photo-generated
|
||||
{:events [::on-name-and-photo-generated]
|
||||
:interceptors [(re-frame/inject-cofx :random-guid-generator)
|
||||
(re-frame/inject-cofx ::multiaccounts.create/get-signing-phrase)]}
|
||||
[{:keys [db] :as cofx} whisper-name identicon]
|
||||
(fx/merge
|
||||
(rf/merge
|
||||
cofx
|
||||
{:db (update-in db [:keycard :multiaccount]
|
||||
{:db (update-in db
|
||||
[:keycard :multiaccount]
|
||||
(fn [multiacc]
|
||||
(assoc multiacc
|
||||
:recovered (get db :recovered-account?)
|
||||
:name whisper-name
|
||||
:name whisper-name
|
||||
:identicon identicon)))}
|
||||
(create-keycard-multiaccount)))
|
||||
|
||||
@@ -1,46 +1,49 @@
|
||||
(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.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.types :as types]
|
||||
[taoensso.timbre :as log]
|
||||
[status-im.keycard.common :as common]
|
||||
[clojure.string :as string]))
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(fx/defn sign
|
||||
(rf/defn sign
|
||||
{:events [:keycard/sign]}
|
||||
[{:keys [db] :as cofx} hash on-success]
|
||||
(let [card-connected? (get-in db [:keycard :card-connected?])
|
||||
key-uid (get-in db [:multiaccount :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-match? (= key-uid keycard-key-uid)
|
||||
hash (or hash (get-in db [:keycard :hash]))
|
||||
data (get-in db [:keycard :data])
|
||||
typed? (get-in db [:keycard :typed?])
|
||||
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))
|
||||
path (reduce
|
||||
(fn [_ {:keys [address path]}]
|
||||
(when (ethereum/address= from address)
|
||||
(reduced path)))
|
||||
nil
|
||||
(:multiaccount/accounts db))]
|
||||
(let [card-connected? (get-in db [:keycard :card-connected?])
|
||||
key-uid (get-in db [:multiaccount :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-match? (= key-uid keycard-key-uid)
|
||||
hash (or hash (get-in db [:keycard :hash]))
|
||||
data (get-in db [:keycard :data])
|
||||
typed? (get-in db [:keycard :typed?])
|
||||
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))
|
||||
path (reduce
|
||||
(fn [_ {:keys [address path]}]
|
||||
(when (ethereum/address= from address)
|
||||
(reduced path)))
|
||||
nil
|
||||
(:multiaccount/accounts db))]
|
||||
(cond
|
||||
(not keycard-match?)
|
||||
(common/show-wrong-keycard-alert cofx)
|
||||
|
||||
(not card-connected?)
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{:db (assoc-in db [:signing/sign :keycard-step] :signing)}
|
||||
(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
|
||||
{:db (-> db
|
||||
(assoc-in [:keycard :card-read-in-progress?] true)
|
||||
(assoc-in [:keycard :pin :status] :verifying))
|
||||
(pos? keycard-pin-retries) ; if 0, get-application-info will have already closed the connection
|
||||
; sheet and opened the frozen card popup
|
||||
{:db (-> db
|
||||
(assoc-in [:keycard :card-read-in-progress?] true)
|
||||
(assoc-in [:keycard :pin :status] :verifying))
|
||||
:keycard/sign {:hash (ethereum/naked-address hash)
|
||||
:data data
|
||||
:typed? typed? ; this parameter is for e2e
|
||||
@@ -48,76 +51,80 @@
|
||||
:pin pin
|
||||
:path path}})))
|
||||
|
||||
(defn normalize-signature [signature]
|
||||
(defn normalize-signature
|
||||
[signature]
|
||||
(-> signature
|
||||
(string/replace-first #"00$", "1b")
|
||||
(string/replace-first #"01$", "1c")
|
||||
(string/replace-first #"00$" "1b")
|
||||
(string/replace-first #"01$" "1c")
|
||||
ethereum/normalized-hex))
|
||||
|
||||
(fx/defn sign-message
|
||||
(rf/defn sign-message
|
||||
{:events [:keycard/sign-message]}
|
||||
[cofx params result]
|
||||
(let [{:keys [result error]} (types/json->clj result)
|
||||
on-success #(re-frame/dispatch [:keycard/on-sign-message-success params
|
||||
(normalize-signature %)])
|
||||
hash (ethereum/naked-address result)]
|
||||
on-success #(re-frame/dispatch [:keycard/on-sign-message-success params
|
||||
(normalize-signature %)])
|
||||
hash (ethereum/naked-address result)]
|
||||
(sign cofx hash on-success)))
|
||||
|
||||
(fx/defn on-sign-message-success
|
||||
(rf/defn on-sign-message-success
|
||||
{:events [:keycard/on-sign-message-success]}
|
||||
[{:keys [db] :as cofx} {:keys [tx-hash message-id chat-id value contract]} signature]
|
||||
(fx/merge
|
||||
(rf/merge
|
||||
cofx
|
||||
{:dispatch
|
||||
(if message-id
|
||||
[:sign/send-accept-transaction-message message-id tx-hash signature]
|
||||
[:sign/send-transaction-message chat-id value contract tx-hash signature])
|
||||
:signing/show-transaction-result nil
|
||||
:db (-> db
|
||||
(assoc-in [:keycard :pin :sign] [])
|
||||
(assoc-in [:keycard :pin :status] nil))}
|
||||
:db (-> db
|
||||
(assoc-in [:keycard :pin :sign] [])
|
||||
(assoc-in [:keycard :pin :status] nil))}
|
||||
(common/clear-on-card-connected)
|
||||
(common/get-application-info nil)
|
||||
(common/hide-connection-sheet)))
|
||||
|
||||
(fx/defn sign-typed-data
|
||||
(rf/defn sign-typed-data
|
||||
{:events [:keycard/sign-typed-data]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(let [card-connected? (get-in db [:keycard :card-connected?])
|
||||
hash (get-in db [:keycard :hash])]
|
||||
hash (get-in db [:keycard :hash])]
|
||||
(if card-connected?
|
||||
{:db (-> db
|
||||
(assoc-in [:keycard :card-read-in-progress?] true)
|
||||
(assoc-in [:signing/sign :keycard-step] :signing))
|
||||
:keycard/sign-typed-data {:hash (ethereum/naked-address hash)}}
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(common/set-on-card-connected :keycard/sign-typed-data)
|
||||
{: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]}
|
||||
[{:keys [db]} {:keys [decimals symbol]}]
|
||||
{:db (-> db
|
||||
(assoc-in [:signing/sign :formatted-data :message :formatted-currency] symbol)
|
||||
(update-in [:signing/sign :formatted-data :message]
|
||||
#(assoc % :formatted-amount (.dividedBy ^js (money/bignumber (:amount %))
|
||||
(money/bignumber (money/from-decimal decimals))))))})
|
||||
#(assoc %
|
||||
: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]}
|
||||
[{:keys [db] :as cofx} result]
|
||||
(let [{:keys [result]} (types/json->clj result)
|
||||
message (get-in db [:signing/sign :formatted-data :message])
|
||||
(let [{:keys [result]} (types/json->clj result)
|
||||
message (get-in db [:signing/sign :formatted-data :message])
|
||||
currency-contract (:currency message)]
|
||||
(when currency-contract
|
||||
{::json-rpc/call [{:method "wallet_discoverToken"
|
||||
:params [(ethereum/chain-id db) currency-contract]
|
||||
:on-success #(re-frame/dispatch [:keycard/fetch-currency-token-on-success %])}]})
|
||||
(fx/merge cofx
|
||||
{::json-rpc/call [{:method "wallet_discoverToken"
|
||||
:params [(ethereum/chain-id db) currency-contract]
|
||||
:on-success #(re-frame/dispatch [:keycard/fetch-currency-token-on-success
|
||||
%])}]})
|
||||
(rf/merge cofx
|
||||
{:db (assoc-in db [:keycard :hash] result)}
|
||||
sign-typed-data)))
|
||||
|
||||
(fx/defn prepare-to-sign
|
||||
(rf/defn prepare-to-sign
|
||||
{:events [:keycard/prepare-to-sign]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(common/show-connection-sheet
|
||||
@@ -125,30 +132,31 @@
|
||||
{:on-card-connected :keycard/prepare-to-sign
|
||||
:handler (common/get-application-info :keycard/sign)}))
|
||||
|
||||
(fx/defn sign-message-completed
|
||||
(rf/defn sign-message-completed
|
||||
[_ signature]
|
||||
{:dispatch
|
||||
[:signing/sign-message-completed signature]})
|
||||
|
||||
(fx/defn send-transaction-with-signature
|
||||
(rf/defn 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]}
|
||||
[{:keys [db] :as cofx} signature]
|
||||
(log/debug "[keycard] sign success: " signature)
|
||||
(let [signature-json (types/clj->json {:result (normalize-signature signature)})
|
||||
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)]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{:db (-> db
|
||||
(assoc-in [:keycard :hash] nil)
|
||||
(assoc-in [:keycard :transaction] nil))}
|
||||
(when-not command?
|
||||
(fn [{:keys [db] :as cofx}]
|
||||
(fx/merge
|
||||
(rf/merge
|
||||
cofx
|
||||
{:db (-> db
|
||||
(assoc-in [:keycard :pin :sign] [])
|
||||
@@ -163,18 +171,19 @@
|
||||
:on-completed #(re-frame/dispatch [:signing/transaction-completed % tx-obj])})
|
||||
(sign-message-completed signature-json)))))
|
||||
|
||||
(fx/defn on-sign-error
|
||||
(rf/defn on-sign-error
|
||||
{:events [:keycard.callback/on-sign-error]}
|
||||
[{:keys [db] :as cofx} error]
|
||||
(log/debug "[keycard] sign 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?
|
||||
(if (not (nil? pin-retries))
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{:db (-> db
|
||||
(assoc-in [:keycard :application-info :pin-retry-counter] pin-retries)
|
||||
(update-in [:keycard :pin] assoc
|
||||
(update-in [:keycard :pin]
|
||||
assoc
|
||||
:status :error
|
||||
:sign []
|
||||
:error-label :t/pin-mismatch)
|
||||
@@ -182,6 +191,6 @@
|
||||
(common/hide-connection-sheet)
|
||||
(when (zero? pin-retries) (common/frozen-keycard-popup)))
|
||||
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(common/hide-connection-sheet)
|
||||
(common/show-wrong-keycard-alert))))))
|
||||
|
||||
@@ -1,42 +1,45 @@
|
||||
(ns status-im.keycard.unpair
|
||||
(:require [re-frame.core :as re-frame]
|
||||
[status-im.multiaccounts.update.core :as multiaccounts.update]
|
||||
[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.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]}
|
||||
[_]
|
||||
{:ui/show-confirmation {:title (i18n/label :t/unpair-card)
|
||||
:content (i18n/label :t/unpair-card-confirmation)
|
||||
:confirm-button-text (i18n/label :t/yes)
|
||||
: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 #()}})
|
||||
|
||||
(fx/defn unpair-card-confirmed
|
||||
(rf/defn unpair-card-confirmed
|
||||
{:events [:keycard-settings.ui/unpair-card-confirmed]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(fx/merge cofx
|
||||
{:db (assoc-in db [:keycard :pin] {:enter-step :current
|
||||
:current []
|
||||
:puk []
|
||||
:status nil
|
||||
:error-label nil
|
||||
:on-verified :keycard/unpair})}
|
||||
(rf/merge cofx
|
||||
{:db (assoc-in db
|
||||
[:keycard :pin]
|
||||
{:enter-step :current
|
||||
:current []
|
||||
:puk []
|
||||
:status nil
|
||||
:error-label nil
|
||||
:on-verified :keycard/unpair})}
|
||||
(common/navigate-to-enter-pin-screen)))
|
||||
|
||||
(fx/defn unpair
|
||||
(rf/defn unpair
|
||||
{:events [:keycard/unpair]}
|
||||
[{: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}}))
|
||||
|
||||
(fx/defn unpair-and-delete
|
||||
(rf/defn unpair-and-delete
|
||||
{:events [:keycard/unpair-and-delete]}
|
||||
[cofx]
|
||||
(common/show-connection-sheet
|
||||
@@ -44,55 +47,65 @@
|
||||
{:on-card-connected :keycard/unpair-and-delete
|
||||
:handler
|
||||
(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
|
||||
{:pin pin}}))}))
|
||||
|
||||
(fx/defn remove-pairing-from-multiaccount
|
||||
(rf/defn remove-pairing-from-multiaccount
|
||||
[cofx {:keys [remove-instance-uid?]}]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(multiaccounts.update/multiaccount-update
|
||||
:keycard-pairing nil {})
|
||||
:keycard-pairing
|
||||
nil
|
||||
{})
|
||||
(multiaccounts.update/multiaccount-update
|
||||
:keycard-paired-on nil {})
|
||||
:keycard-paired-on
|
||||
nil
|
||||
{})
|
||||
(when remove-instance-uid?
|
||||
(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]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(let [instance-uid (get-in db [:keycard :application-info :instance-uid])
|
||||
pairings (get-in db [:keycard :pairings])]
|
||||
(fx/merge cofx
|
||||
{:db (-> db
|
||||
(assoc-in [:keycard :secrets] nil)
|
||||
(update-in [:keycard :pairings] dissoc (keyword instance-uid))
|
||||
(assoc-in [:keycard :pin] {:status nil
|
||||
:error-label nil
|
||||
:on-verified nil}))
|
||||
:keycard/persist-pairings (dissoc pairings (keyword instance-uid))
|
||||
:utils/show-popup {:title ""
|
||||
:content (i18n/label :t/card-unpaired)}}
|
||||
(common/clear-on-card-connected)
|
||||
(remove-pairing-from-multiaccount nil)
|
||||
(navigation/navigate-to-cofx :keycard-settings nil))))
|
||||
(rf/merge
|
||||
cofx
|
||||
{:db (-> db
|
||||
(assoc-in [:keycard :secrets] nil)
|
||||
(update-in [:keycard :pairings] dissoc (keyword instance-uid))
|
||||
(assoc-in [:keycard :pin]
|
||||
{:status nil
|
||||
:error-label nil
|
||||
:on-verified nil}))
|
||||
:keycard/persist-pairings (dissoc pairings (keyword instance-uid))
|
||||
:utils/show-popup {:title ""
|
||||
:content (i18n/label :t/card-unpaired)}}
|
||||
(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]}
|
||||
[{:keys [db] :as cofx} error]
|
||||
(log/debug "[keycard] unpair error" error)
|
||||
(fx/merge cofx
|
||||
{:db (assoc-in db [:keycard :pin] {:status nil
|
||||
:error-label nil
|
||||
:on-verified nil})
|
||||
(rf/merge cofx
|
||||
{:db (assoc-in db
|
||||
[:keycard :pin]
|
||||
{:status nil
|
||||
:error-label nil
|
||||
:on-verified nil})
|
||||
:keycard/get-application-info nil
|
||||
:utils/show-popup {:title ""
|
||||
:content (i18n/label :t/something-went-wrong)}}
|
||||
:utils/show-popup {:title ""
|
||||
:content (i18n/label :t/something-went-wrong)}}
|
||||
(common/clear-on-card-connected)
|
||||
(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]}
|
||||
[cofx]
|
||||
(common/show-connection-sheet
|
||||
@@ -100,66 +113,75 @@
|
||||
{:on-card-connected :keycard/remove-key-with-unpair
|
||||
:handler
|
||||
(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
|
||||
{:pin pin}}))}))
|
||||
|
||||
(defn handle-account-removal [{:keys [db] :as cofx} keys-removed-from-card?]
|
||||
(let [key-uid (get-in db [:multiaccount :key-uid])
|
||||
(defn handle-account-removal
|
||||
[{: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])
|
||||
pairings (get-in db [:keycard :pairings])]
|
||||
(fx/merge cofx
|
||||
{:db (-> db
|
||||
(update :multiaccounts/multiaccounts dissoc key-uid)
|
||||
(assoc-in [:keycard :secrets] nil)
|
||||
(update-in [:keycard :pairings] dissoc (keyword instance-uid))
|
||||
(update-in [:keycard :pairings] dissoc instance-uid)
|
||||
(assoc-in [:keycard :whisper-public-key] nil)
|
||||
(assoc-in [:keycard :wallet-address] nil)
|
||||
(assoc-in [:keycard :application-info] nil)
|
||||
(assoc-in [:keycard :pin] {:status nil
|
||||
:error-label nil
|
||||
:on-verified nil}))
|
||||
:keycard/persist-pairings (dissoc pairings (keyword instance-uid))
|
||||
:utils/show-popup {:title (i18n/label (if keys-removed-from-card? :t/profile-deleted-title :t/database-reset-title))
|
||||
: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))))
|
||||
pairings (get-in db [:keycard :pairings])]
|
||||
(rf/merge
|
||||
cofx
|
||||
{:db (-> db
|
||||
(update :multiaccounts/multiaccounts dissoc key-uid)
|
||||
(assoc-in [:keycard :secrets] nil)
|
||||
(update-in [:keycard :pairings]
|
||||
dissoc
|
||||
(keyword instance-uid))
|
||||
(update-in [:keycard :pairings] dissoc instance-uid)
|
||||
(assoc-in [:keycard :whisper-public-key] nil)
|
||||
(assoc-in [:keycard :wallet-address] nil)
|
||||
(assoc-in [:keycard :application-info] nil)
|
||||
(assoc-in [:keycard :pin]
|
||||
{:status nil
|
||||
:error-label nil
|
||||
:on-verified nil}))
|
||||
:keycard/persist-pairings (dissoc pairings (keyword instance-uid))
|
||||
:utils/show-popup {:title (i18n/label (if keys-removed-from-card?
|
||||
:t/profile-deleted-title
|
||||
:t/database-reset-title))
|
||||
: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]}
|
||||
[cofx]
|
||||
(handle-account-removal cofx true))
|
||||
|
||||
(fx/defn on-remove-key-error
|
||||
(rf/defn on-remove-key-error
|
||||
{:events [:keycard.callback/on-remove-key-error]}
|
||||
[{:keys [db] :as cofx} error]
|
||||
(log/debug "[keycard] remove key error" error)
|
||||
(let [tag-was-lost? (common/tag-lost? (:error error))]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(if tag-was-lost?
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{:db (assoc-in db [:keycard :pin :status] nil)}
|
||||
(common/set-on-card-connected :keycard/remove-key-with-unpair))
|
||||
(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]}
|
||||
[cofx]
|
||||
(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]}
|
||||
[{:keys [db] :as cofx} error]
|
||||
(log/debug "[keycard] unpair and delete error" error)
|
||||
(let [tag-was-lost? (common/tag-lost? (:error error))]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(if tag-was-lost?
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{:db (assoc-in db [:keycard :pin :status] nil)}
|
||||
(common/set-on-card-connected :keycard/unpair-and-delete))
|
||||
(common/show-wrong-keycard-alert)))))
|
||||
|
||||
@@ -1,37 +1,37 @@
|
||||
(ns status-im.keycard.wallet
|
||||
(:require [status-im.ethereum.core :as ethereum]
|
||||
[status-im.utils.fx :as fx]
|
||||
[status-im.keycard.common :as common]
|
||||
(:require [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.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.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]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(fx/merge
|
||||
(rf/merge
|
||||
cofx
|
||||
{:db (-> db
|
||||
(assoc-in [:keycard :pin :enter-step] :export-key)
|
||||
(update-in [:keycard :pin] dissoc :export-key))
|
||||
{:db (-> db
|
||||
(assoc-in [:keycard :pin :enter-step] :export-key)
|
||||
(update-in [:keycard :pin] dissoc :export-key))
|
||||
:dismiss-keyboard nil}
|
||||
(bottom-sheet/show-bottom-sheet {:view {:content add-new.views/pin}})))
|
||||
|
||||
(fx/defn verify-pin-with-delay
|
||||
(rf/defn verify-pin-with-delay
|
||||
[cofx]
|
||||
{:utils/dispatch-later
|
||||
;; We need to give previous sheet some time to be fully hidden
|
||||
[{:ms 200
|
||||
[{:ms 200
|
||||
:dispatch [:wallet.accounts/verify-pin]}]})
|
||||
|
||||
(fx/defn hide-pin-sheet
|
||||
(rf/defn hide-pin-sheet
|
||||
{:events [:keycard/new-account-pin-sheet-hide]}
|
||||
[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]}
|
||||
[{:keys [db]}]
|
||||
(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]))]
|
||||
{:db
|
||||
(assoc-in
|
||||
db [:keycard :on-export-success]
|
||||
db
|
||||
[:keycard :on-export-success]
|
||||
#(vector :wallet.accounts/account-stored
|
||||
(let [public-key (utils.hex/normalize-hex %)]
|
||||
{;; Strip leading 04 prefix denoting uncompressed key format
|
||||
@@ -52,7 +53,7 @@
|
||||
|
||||
:keycard/export-key {:pin pin :path path}}))
|
||||
|
||||
(fx/defn verify-pin
|
||||
(rf/defn verify-pin
|
||||
{:events [:wallet.accounts/verify-pin]}
|
||||
[cofx]
|
||||
(common/verify-pin
|
||||
|
||||
@@ -1,23 +1,24 @@
|
||||
(ns status-im.log-level.core
|
||||
(:require [re-frame.core :as re-frame]
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
[status-im.multiaccounts.update.core :as multiaccounts.update]
|
||||
[status-im.node.core :as node]
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
[status-im.utils.fx :as fx]))
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(fx/defn save-log-level
|
||||
(rf/defn save-log-level
|
||||
{:events [:log-level.ui/change-log-level-confirmed]}
|
||||
[{:keys [db now] :as cofx} log-level]
|
||||
(let [old-log-level (get-in db [:multiaccount :log-level])]
|
||||
(when (not= old-log-level log-level)
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(multiaccounts.update/multiaccount-update
|
||||
:log-level log-level
|
||||
:log-level
|
||||
log-level
|
||||
{})
|
||||
(node/prepare-new-config
|
||||
{: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]}
|
||||
[_ {:keys [name value]}]
|
||||
{:ui/show-confirmation
|
||||
|
||||
+142
-119
@@ -1,5 +1,4 @@
|
||||
(ns ^{:doc "Mailserver events and API"}
|
||||
status-im.mailserver.core
|
||||
(ns ^{:doc "Mailserver events and API"} status-im.mailserver.core
|
||||
(:require [clojure.string :as string]
|
||||
[re-frame.core :as re-frame]
|
||||
[status-im.ethereum.json-rpc :as json-rpc]
|
||||
@@ -8,8 +7,8 @@
|
||||
[status-im.node.core :as node]
|
||||
[status-im.utils.mobile-sync :as mobile-network-utils]
|
||||
[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 ?
|
||||
;;
|
||||
@@ -24,22 +23,28 @@
|
||||
;; as soon as the mailserver becomes available
|
||||
|
||||
|
||||
(defn connected? [db id]
|
||||
(defn connected?
|
||||
[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]))
|
||||
|
||||
(defn fetch-current [db]
|
||||
(defn fetch-current
|
||||
[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)]))
|
||||
|
||||
(defn connection-error-dismissed [db]
|
||||
(defn connection-error-dismissed
|
||||
[db]
|
||||
(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)]
|
||||
(:id (some #(when (= address (:address %))
|
||||
%)
|
||||
@@ -48,19 +53,21 @@
|
||||
current-fleet
|
||||
vals)))))
|
||||
|
||||
(fx/defn disconnect-from-mailserver
|
||||
(rf/defn disconnect-from-mailserver
|
||||
{:events [::disconnect-from-mailserver]}
|
||||
[{:keys [db] :as cofx}]
|
||||
{:db (-> db
|
||||
(assoc :mailserver/state nil)
|
||||
(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?]))
|
||||
|
||||
(defonce showing-connection-error-popup? (atom false))
|
||||
|
||||
(defn cancel-connection-popup! []
|
||||
(defn cancel-connection-popup!
|
||||
[]
|
||||
(reset! showing-connection-error-popup? false)
|
||||
(re-frame/dispatch [:mailserver.ui/dismiss-connection-error true]))
|
||||
|
||||
@@ -68,7 +75,8 @@
|
||||
::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)
|
||||
{:ui/show-confirmation
|
||||
{:title (i18n/label :t/mailserver-error-title)
|
||||
@@ -88,27 +96,28 @@
|
||||
preferred-mailserver]))
|
||||
:style "default"}]}})
|
||||
|
||||
(fx/defn handle-successful-request
|
||||
(rf/defn handle-successful-request
|
||||
{:events [::request-success]}
|
||||
[{:keys [db] :as cofx} response-js]
|
||||
{:db (dissoc db :mailserver/current-request)
|
||||
:dispatch [:sanitize-messages-and-process-response response-js]})
|
||||
|
||||
(fx/defn handle-mailserver-not-working [{:keys [db] :as cofx}]
|
||||
(let [current-fleet (node/current-fleet-key db)
|
||||
error-dismissed? (connection-error-dismissed db)
|
||||
pinned-mailserver (get-in db [:multiaccount :pinned-mailservers current-fleet])]
|
||||
(rf/defn handle-mailserver-not-working
|
||||
[{:keys [db] :as cofx}]
|
||||
(let [current-fleet (node/current-fleet-key db)
|
||||
error-dismissed? (connection-error-dismissed db)
|
||||
pinned-mailserver (get-in db [:multiaccount :pinned-mailservers current-fleet])]
|
||||
(when (and pinned-mailserver
|
||||
(not error-dismissed?)
|
||||
(not @showing-connection-error-popup?))
|
||||
(show-connection-error! cofx current-fleet pinned-mailserver))))
|
||||
|
||||
(fx/defn handle-request-error
|
||||
(rf/defn handle-request-error
|
||||
{:events [::request-error]}
|
||||
[{:keys [db] :as cofx}]
|
||||
{:db (dissoc db :mailserver/current-request)})
|
||||
|
||||
(fx/defn process-next-messages-request
|
||||
(rf/defn process-next-messages-request
|
||||
{:events [::request-messages]}
|
||||
[{:keys [db now] :as cofx}]
|
||||
(when (and
|
||||
@@ -116,93 +125,99 @@
|
||||
(mobile-network-utils/syncing-allowed? cofx)
|
||||
(fetch-use-mailservers? cofx)
|
||||
(not (:mailserver/current-request db)))
|
||||
{:db (assoc db :mailserver/current-request true)
|
||||
::json-rpc/call [{:method "wakuext_requestAllHistoricMessagesWithRetries"
|
||||
:params []
|
||||
{:db (assoc db :mailserver/current-request true)
|
||||
::json-rpc/call [{:method "wakuext_requestAllHistoricMessagesWithRetries"
|
||||
:params []
|
||||
:js-response true
|
||||
:on-success #(do
|
||||
(log/info "fetched historical messages")
|
||||
(re-frame/dispatch [::request-success %]))
|
||||
:on-error #(do
|
||||
(log/error "failed retrieve historical messages" %)
|
||||
(re-frame/dispatch [::request-error]))}]}))
|
||||
:on-success #(do
|
||||
(log/info "fetched historical messages")
|
||||
(re-frame/dispatch [::request-success %]))
|
||||
:on-error #(do
|
||||
(log/error "failed retrieve historical messages" %)
|
||||
(re-frame/dispatch [::request-error]))}]}))
|
||||
|
||||
(fx/defn handle-mailserver-changed
|
||||
(rf/defn handle-mailserver-changed
|
||||
[{:keys [db] :as cofx} ms]
|
||||
(if (seq ms)
|
||||
{:db (assoc db :mailserver/state :connecting
|
||||
{:db (assoc db
|
||||
:mailserver/state :connecting
|
||||
:mailserver/current-id (keyword ms))}
|
||||
{:db (assoc db :mailserver/state nil)}))
|
||||
|
||||
(fx/defn handle-mailserver-available
|
||||
(rf/defn handle-mailserver-available
|
||||
[{:keys [db] :as cofx} ms]
|
||||
{::cancel-connection-popup []
|
||||
:db (assoc db :mailserver/state :connected
|
||||
:mailserver/current-id (keyword ms))})
|
||||
:db (assoc db
|
||||
:mailserver/state :connected
|
||||
:mailserver/current-id (keyword ms))})
|
||||
|
||||
(fx/defn connected-to-mailserver
|
||||
(rf/defn connected-to-mailserver
|
||||
[{:keys [db] :as cofx}]
|
||||
(let [{:keys [address]} (fetch-current db)]
|
||||
(fx/merge
|
||||
(let [{:keys [address]} (fetch-current db)]
|
||||
(rf/merge
|
||||
cofx
|
||||
{:mailserver/update-mailservers [[address] #(re-frame/dispatch [::request-messages])]})))
|
||||
|
||||
(fx/defn handle-request-success
|
||||
(rf/defn handle-request-success
|
||||
{:events [:mailserver.callback/request-success]}
|
||||
[{{:keys [chats] :as db} :db} {:keys [request-id topics]}]
|
||||
(when (:mailserver/current-request db)
|
||||
{:db (assoc-in db [:mailserver/current-request :request-id]
|
||||
request-id)}))
|
||||
{:db (assoc-in db
|
||||
[:mailserver/current-request :request-id]
|
||||
request-id)}))
|
||||
|
||||
(fx/defn toggle-use-mailservers
|
||||
(rf/defn toggle-use-mailservers
|
||||
[cofx value]
|
||||
{::json-rpc/call
|
||||
[{:method "wakuext_toggleUseMailservers"
|
||||
:params [value]
|
||||
[{:method "wakuext_toggleUseMailservers"
|
||||
:params [value]
|
||||
:on-success #(log/info "successfully toggled use-mailservers" 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]}
|
||||
[cofx use-mailservers?]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(multiaccounts.update/optimistic :use-mailservers? use-mailservers?)
|
||||
(toggle-use-mailservers use-mailservers?)
|
||||
(when use-mailservers?
|
||||
(disconnect-from-mailserver))))
|
||||
|
||||
(fx/defn retry-next-messages-request
|
||||
(rf/defn retry-next-messages-request
|
||||
{:events [:mailserver.ui/retry-request-pressed]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{:db (dissoc db :mailserver/request-error)}
|
||||
(process-next-messages-request)))
|
||||
|
||||
(fx/defn show-request-error-popup
|
||||
(rf/defn show-request-error-popup
|
||||
{:events [:mailserver.ui/request-error-pressed]}
|
||||
[{:keys [db]}]
|
||||
(let [mailserver-error (:mailserver/request-error db)]
|
||||
{:utils/show-confirmation
|
||||
{:title (i18n/label :t/mailserver-request-error-title)
|
||||
:content (i18n/label :t/mailserver-request-error-content
|
||||
{:error mailserver-error})
|
||||
:on-accept #(re-frame/dispatch [:mailserver.ui/retry-request-pressed])
|
||||
{:title (i18n/label :t/mailserver-request-error-title)
|
||||
:content (i18n/label :t/mailserver-request-error-content
|
||||
{:error mailserver-error})
|
||||
:on-accept #(re-frame/dispatch [:mailserver.ui/retry-request-pressed])
|
||||
:confirm-button-text (i18n/label :t/mailserver-request-retry)}}))
|
||||
|
||||
(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})")
|
||||
|
||||
(defn- extract-url-components [address]
|
||||
(defn- extract-url-components
|
||||
[address]
|
||||
(rest (re-matches #"enode://(.*)@(.*)" address)))
|
||||
|
||||
(defn valid-enode-url? [address]
|
||||
(defn valid-enode-url?
|
||||
[address]
|
||||
(re-matches enode-url-regex address))
|
||||
|
||||
(defn build-url [address]
|
||||
(defn build-url
|
||||
[address]
|
||||
(let [[initial host] (extract-url-components address)]
|
||||
(str "enode://" initial "@" host)))
|
||||
|
||||
(fx/defn set-input
|
||||
(rf/defn set-input
|
||||
{:events [:mailserver.ui/input-changed]}
|
||||
[{:keys [db]} input-key value]
|
||||
{:db (update
|
||||
@@ -216,26 +231,28 @@
|
||||
:name (string/blank? value)
|
||||
:url (not (valid-enode-url? value)))})})
|
||||
|
||||
(defn- address->mailserver [address]
|
||||
(defn- address->mailserver
|
||||
[address]
|
||||
(let [[enode url :as response] (extract-url-components address)]
|
||||
{:address (if (seq response)
|
||||
(str "enode://" enode "@" url)
|
||||
address)
|
||||
:custom true}))
|
||||
:custom true}))
|
||||
|
||||
(defn- build [id mailserver-name address]
|
||||
(defn- build
|
||||
[id mailserver-name address]
|
||||
(assoc (address->mailserver address)
|
||||
:id id
|
||||
:id id
|
||||
:name mailserver-name))
|
||||
|
||||
(def default? (comp not :custom fetch))
|
||||
|
||||
(fx/defn edit
|
||||
(rf/defn edit
|
||||
{:events [:mailserver.ui/custom-mailserver-selected]}
|
||||
[{:keys [db] :as cofx} id]
|
||||
(let [{:keys [id address name]} (fetch db id)
|
||||
url (when address (build-url address))]
|
||||
(fx/merge cofx
|
||||
url (when address (build-url address))]
|
||||
(rf/merge cofx
|
||||
(set-input :id id)
|
||||
(set-input :url (str url))
|
||||
(set-input :name (str name))
|
||||
@@ -247,14 +264,15 @@
|
||||
(assoc :fleet (name current-fleet))
|
||||
(update :id name)))
|
||||
|
||||
(fx/defn upsert
|
||||
{:events [:mailserver.ui/save-pressed]
|
||||
(rf/defn upsert
|
||||
{:events [:mailserver.ui/save-pressed]
|
||||
:interceptors [(re-frame/inject-cofx :random-id-generator)]}
|
||||
[{{: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
|
||||
current-fleet (node/current-fleet-key db)]
|
||||
current-fleet (node/current-fleet-key db)]
|
||||
(when (and (not (string/blank? (:value name)))
|
||||
(valid-enode-url? (:value url)))
|
||||
|
||||
@@ -263,14 +281,14 @@
|
||||
(keyword (string/replace (random-id-generator) "-" "")))
|
||||
(:value name)
|
||||
(:value url))
|
||||
current (connected? db (:id mailserver))]
|
||||
{:db (-> db
|
||||
(dissoc :mailserver.edit/mailserver)
|
||||
(assoc-in [:mailserver/mailservers current-fleet (:id mailserver)]
|
||||
mailserver))
|
||||
current (connected? db (:id mailserver))]
|
||||
{:db (-> db
|
||||
(dissoc :mailserver.edit/mailserver)
|
||||
(assoc-in [:mailserver/mailservers current-fleet (:id mailserver)]
|
||||
mailserver))
|
||||
::json-rpc/call
|
||||
[{:method "mailservers_addMailserver"
|
||||
:params [(mailserver->rpc mailserver current-fleet)]
|
||||
[{:method "mailservers_addMailserver"
|
||||
:params [(mailserver->rpc mailserver current-fleet)]
|
||||
:on-success (fn []
|
||||
;; we naively logout if the user is connected to
|
||||
;; the edited mailserver
|
||||
@@ -279,47 +297,49 @@
|
||||
[:multiaccounts.logout.ui/logout-confirmed]))
|
||||
(log/debug "saved mailserver" id "successfuly"))
|
||||
:on-failure #(log/error "failed to save mailserver" id %)}]
|
||||
:dispatch [:navigate-back]}))))
|
||||
:dispatch [:navigate-back]}))))
|
||||
|
||||
(defn can-delete?
|
||||
[db id]
|
||||
(not (or (default? db id)
|
||||
(connected? db id))))
|
||||
|
||||
(fx/defn delete
|
||||
(rf/defn delete
|
||||
{:events [:mailserver.ui/delete-confirmed]}
|
||||
[{:keys [db] :as cofx} id]
|
||||
(if (can-delete? db id)
|
||||
{:db (-> db
|
||||
(update-in
|
||||
[:mailserver/mailservers (node/current-fleet-key db)]
|
||||
dissoc id)
|
||||
(dissoc :mailserver.edit/mailserver))
|
||||
{:db (-> db
|
||||
(update-in
|
||||
[:mailserver/mailservers (node/current-fleet-key db)]
|
||||
dissoc
|
||||
id)
|
||||
(dissoc :mailserver.edit/mailserver))
|
||||
::json-rpc/call
|
||||
[{:method "mailservers_deleteMailserver"
|
||||
:params [(name id)]
|
||||
[{:method "mailservers_deleteMailserver"
|
||||
:params [(name id)]
|
||||
:on-success #(log/debug "deleted mailserver" id)
|
||||
:on-failure #(log/error "failed to delete mailserver" id %)}]
|
||||
: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]}
|
||||
[{:keys [db]} mailserver-id]
|
||||
(let [current-fleet (node/current-fleet-key db)]
|
||||
{:ui/show-confirmation
|
||||
{:title (i18n/label :t/close-app-title)
|
||||
{:title (i18n/label :t/close-app-title)
|
||||
:content
|
||||
(i18n/label :t/connect-mailserver-content
|
||||
{:name (get-in db [:mailserver/mailservers
|
||||
current-fleet mailserver-id :name])})
|
||||
{:name (get-in db
|
||||
[:mailserver/mailservers
|
||||
current-fleet mailserver-id :name])})
|
||||
:confirm-button-text (i18n/label :t/close-app-button)
|
||||
:on-accept
|
||||
#(re-frame/dispatch
|
||||
[: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]}
|
||||
[{:keys [db]} mailserver-id]
|
||||
{:ui/show-confirmation
|
||||
@@ -329,64 +349,67 @@
|
||||
:on-accept #(re-frame/dispatch
|
||||
[:mailserver.ui/delete-confirmed mailserver-id])}})
|
||||
|
||||
(fx/defn set-url-from-qr
|
||||
(rf/defn set-url-from-qr
|
||||
{:events [:mailserver.callback/qr-code-scanned]}
|
||||
[cofx 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]}
|
||||
[{:keys [db]} new-state]
|
||||
{:db (assoc db :mailserver/connection-error-dismissed new-state)})
|
||||
|
||||
(fx/defn pin-mailserver
|
||||
(rf/defn pin-mailserver
|
||||
{:events [:mailserver.ui/connect-confirmed]}
|
||||
[{:keys [db] :as cofx} current-fleet mailserver-id]
|
||||
(let [pinned-mailservers (-> db
|
||||
(get-in [:multiaccount :pinned-mailservers])
|
||||
(assoc current-fleet mailserver-id))]
|
||||
(fx/merge cofx
|
||||
{:db (assoc db :mailserver/current-id mailserver-id)
|
||||
::json-rpc/call [{:method "wakuext_setPinnedMailservers"
|
||||
:params [pinned-mailservers]
|
||||
(rf/merge cofx
|
||||
{:db (assoc db :mailserver/current-id mailserver-id)
|
||||
::json-rpc/call [{:method "wakuext_setPinnedMailservers"
|
||||
:params [pinned-mailservers]
|
||||
: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))))
|
||||
|
||||
(fx/defn unpin
|
||||
(rf/defn unpin
|
||||
{:events [:mailserver.ui/unpin-pressed]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(let [current-fleet (node/current-fleet-key db)
|
||||
pinned-mailservers (-> db
|
||||
(get-in [:multiaccount :pinned-mailservers])
|
||||
(dissoc current-fleet))]
|
||||
(fx/merge cofx
|
||||
{::json-rpc/call [{:method "wakuext_setPinnedMailservers"
|
||||
:params [pinned-mailservers]
|
||||
(let [current-fleet (node/current-fleet-key db)
|
||||
pinned-mailservers (-> db
|
||||
(get-in [:multiaccount :pinned-mailservers])
|
||||
(dissoc current-fleet))]
|
||||
(rf/merge cofx
|
||||
{::json-rpc/call [{:method "wakuext_setPinnedMailservers"
|
||||
:params [pinned-mailservers]
|
||||
: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
|
||||
:pinned-mailservers pinned-mailservers)
|
||||
:pinned-mailservers
|
||||
pinned-mailservers)
|
||||
(dismiss-connection-error false))))
|
||||
|
||||
(fx/defn pin
|
||||
(rf/defn pin
|
||||
{:events [:mailserver.ui/pin-pressed]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(let [current-fleet (node/current-fleet-key db)
|
||||
mailserver-id (:mailserver/current-id db)
|
||||
(let [current-fleet (node/current-fleet-key db)
|
||||
mailserver-id (:mailserver/current-id db)
|
||||
pinned-mailservers (get-in db [:multiaccount :pinned-mailservers])]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(multiaccounts.update/multiaccount-update
|
||||
:pinned-mailservers (assoc pinned-mailservers
|
||||
current-fleet
|
||||
mailserver-id)
|
||||
:pinned-mailservers
|
||||
(assoc pinned-mailservers
|
||||
current-fleet
|
||||
mailserver-id)
|
||||
{})
|
||||
(dismiss-connection-error false))))
|
||||
|
||||
(fx/defn mailserver-ui-add-pressed
|
||||
(rf/defn mailserver-ui-add-pressed
|
||||
{:events [:mailserver.ui/add-pressed]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{:db (dissoc db :mailserver.edit/mailserver)}
|
||||
(navigation/navigate-to-cofx :edit-mailserver nil)))
|
||||
|
||||
@@ -1,28 +1,29 @@
|
||||
(ns status-im.mobile-sync-settings.core
|
||||
(:require
|
||||
[status-im.multiaccounts.update.core :as multiaccounts.update]
|
||||
[status-im.utils.fx :as fx]
|
||||
[status-im.wallet.core :as wallet]
|
||||
[status-im.bottom-sheet.core :as bottom-sheet]
|
||||
[status-im.multiaccounts.model :as multiaccounts.model]
|
||||
[status-im2.navigation.events :as navigation]
|
||||
[status-im.mailserver.core :as mailserver]
|
||||
[status-im.utils.mobile-sync :as utils]
|
||||
[taoensso.timbre :as log]))
|
||||
(:require [status-im.bottom-sheet.core :as bottom-sheet]
|
||||
[status-im.mailserver.core :as mailserver]
|
||||
[status-im.multiaccounts.model :as multiaccounts.model]
|
||||
[status-im.multiaccounts.update.core :as multiaccounts.update]
|
||||
[status-im.utils.mobile-sync :as utils]
|
||||
[status-im.wallet.core :as wallet]
|
||||
[status-im2.navigation.events :as navigation]
|
||||
[taoensso.timbre :as log]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(fx/defn sheet-defaults
|
||||
(rf/defn sheet-defaults
|
||||
[{:keys [db]}]
|
||||
(let [remember-choice? (get-in db [:multiaccount :remember-syncing-choice?])]
|
||||
{:db (assoc db :mobile-network/remember-choice? (or (nil? remember-choice?)
|
||||
remember-choice?))}))
|
||||
{:db (assoc db
|
||||
: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}]
|
||||
(let [initialized? (get db :network-status/initialized?)
|
||||
logged-in? (multiaccounts.model/logged-in? cofx)
|
||||
(let [initialized? (get db :network-status/initialized?)
|
||||
logged-in? (multiaccounts.model/logged-in? cofx)
|
||||
{:keys [remember-syncing-choice?]} (:multiaccount db)]
|
||||
(apply
|
||||
fx/merge
|
||||
rf/merge
|
||||
cofx
|
||||
{:db (assoc db :network-status/initialized? true)}
|
||||
(cond
|
||||
@@ -51,67 +52,71 @@
|
||||
([sync?] (apply-settings sync? :default))
|
||||
([sync? remember?]
|
||||
(fn [{:keys [db] :as cofx}]
|
||||
(let [network (:network/type db)
|
||||
(let [network (:network/type db)
|
||||
remember-choice?
|
||||
(if (not= :default remember?)
|
||||
remember?
|
||||
(:mobile-network/remember-choice? db))
|
||||
cellular? (utils/cellular? network)]
|
||||
cellular? (utils/cellular? network)]
|
||||
(log/info "apply mobile network settings"
|
||||
"sunc?" sync?
|
||||
"sunc?" sync?
|
||||
"remember?" remember?
|
||||
"cellular?" cellular?)
|
||||
(fx/merge
|
||||
(rf/merge
|
||||
cofx
|
||||
(multiaccounts.update/multiaccount-update
|
||||
:syncing-on-mobile-network? (boolean sync?) {})
|
||||
:syncing-on-mobile-network?
|
||||
(boolean sync?)
|
||||
{})
|
||||
(multiaccounts.update/multiaccount-update
|
||||
:remember-syncing-choice? (boolean remember-choice?) {})
|
||||
:remember-syncing-choice?
|
||||
(boolean remember-choice?)
|
||||
{})
|
||||
(when (and cellular? sync?)
|
||||
(mailserver/process-next-messages-request))
|
||||
(wallet/restart-wallet-service nil))))))
|
||||
|
||||
(fx/defn mobile-network-continue-syncing
|
||||
(rf/defn mobile-network-continue-syncing
|
||||
{:events [:mobile-network/continue-syncing]}
|
||||
[cofx]
|
||||
((apply-settings true) cofx))
|
||||
|
||||
(fx/defn mobile-network-stop-syncing
|
||||
(rf/defn mobile-network-stop-syncing
|
||||
{:events [:mobile-network/stop-syncing]}
|
||||
[cofx]
|
||||
((apply-settings false) cofx))
|
||||
|
||||
(fx/defn mobile-network-set-syncing
|
||||
(rf/defn mobile-network-set-syncing
|
||||
{:events [:mobile-network/set-syncing]}
|
||||
[{:keys [db] :as cofx} syncing?]
|
||||
(let [{:keys [remember-syncing-choice?]} (:multiaccount db)]
|
||||
((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?]}
|
||||
[{:keys [db] :as cofx} ask?]
|
||||
(let [{:keys [syncing-on-mobile-network?]} (:multiaccount db)]
|
||||
((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]}
|
||||
[cofx]
|
||||
((apply-settings false false) cofx))
|
||||
|
||||
(fx/defn mobile-network-remember-choice?
|
||||
(rf/defn mobile-network-remember-choice?
|
||||
{:events [:mobile-network/remember-choice?]}
|
||||
[{:keys [db]} 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]}
|
||||
[cofx]
|
||||
(fx/merge
|
||||
(rf/merge
|
||||
cofx
|
||||
(bottom-sheet/hide-bottom-sheet)
|
||||
(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]}
|
||||
[cofx]
|
||||
(bottom-sheet/show-bottom-sheet
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
(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.native-module.core :as status]
|
||||
[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.platform :as platform]
|
||||
[taoensso.timbre :as log]
|
||||
["react-native-touch-id" :default touchid]))
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
;; currently, for android, react-native-touch-id
|
||||
;; is not returning supported biometric type
|
||||
@@ -28,11 +28,12 @@
|
||||
(def android-device-blacklisted?
|
||||
(cond
|
||||
(= (:brand deviceinfo) "bannedbrand") true
|
||||
:else false))
|
||||
:else false))
|
||||
|
||||
;; biometric auth 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
|
||||
{:unifiedErrors true}
|
||||
(when platform/ios?
|
||||
@@ -46,7 +47,8 @@
|
||||
:sensorErrorDescription (i18n/label :t/biometric-auth-android-sensor-error-desc)
|
||||
:cancelText (i18n/label :cancel)}))))
|
||||
|
||||
(defn get-label [supported-biometric-auth]
|
||||
(defn get-label
|
||||
[supported-biometric-auth]
|
||||
(case supported-biometric-auth
|
||||
:fingerprint (i18n/label :t/biometric-fingerprint)
|
||||
:FaceID (i18n/label :t/biometric-faceid)
|
||||
@@ -61,29 +63,33 @@
|
||||
(= touchid-error-code "USER_FALLBACK") nil
|
||||
;; add here more specific errors if needed
|
||||
;; 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
|
||||
{:bioauth-success true})
|
||||
|
||||
(defn- generate-error-result [touchid-error-obj]
|
||||
(defn- generate-error-result
|
||||
[touchid-error-obj]
|
||||
(let [code (aget touchid-error-obj "code")]
|
||||
{:bioauth-success false
|
||||
:bioauth-code code
|
||||
:bioauth-message (get-error-message code)}))
|
||||
|
||||
(defn- do-get-supported [callback]
|
||||
(defn- do-get-supported
|
||||
[callback]
|
||||
(-> (.isSupported touchid)
|
||||
(.then #(callback (or (keyword %) android-default-support)))
|
||||
(.catch #(callback nil))))
|
||||
|
||||
(defn get-supported [callback]
|
||||
(defn get-supported
|
||||
[callback]
|
||||
(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?
|
||||
(callback nil)
|
||||
(do-get-supported callback))
|
||||
:else (callback nil)))
|
||||
:else (callback nil)))
|
||||
|
||||
(defn authenticate-fx
|
||||
([cb]
|
||||
@@ -105,12 +111,12 @@
|
||||
(get-supported callback)
|
||||
(callback nil)))))))
|
||||
|
||||
(fx/defn set-supported-biometric-auth
|
||||
(rf/defn set-supported-biometric-auth
|
||||
{:events [:init.callback/get-supported-biometric-auth-success]}
|
||||
[{:keys [db]} supported-biometric-auth]
|
||||
{:db (assoc db :supported-biometric-auth supported-biometric-auth)})
|
||||
|
||||
(fx/defn authenticate
|
||||
(rf/defn authenticate
|
||||
[_ cb options]
|
||||
{:biometric-auth/authenticate [cb options]})
|
||||
|
||||
@@ -119,10 +125,11 @@
|
||||
(fn [[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])
|
||||
(get-in db [:multiaccounts/login :key-uid]))]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(keychain/save-auth-method
|
||||
key-uid
|
||||
(if biometric-auth?
|
||||
@@ -131,7 +138,7 @@
|
||||
#(when-not biometric-auth?
|
||||
{:keychain/clear-user-password key-uid}))))
|
||||
|
||||
(fx/defn biometric-auth-switched
|
||||
(rf/defn biometric-auth-switched
|
||||
{:events [:multiaccounts.ui/biometric-auth-switched]}
|
||||
[cofx biometric-auth?]
|
||||
(if biometric-auth?
|
||||
@@ -141,17 +148,17 @@
|
||||
{})
|
||||
(update-biometric cofx false)))
|
||||
|
||||
(fx/defn show-message
|
||||
(rf/defn show-message
|
||||
[cofx bioauth-message bioauth-code]
|
||||
(let [content (or (when (get #{"NOT_AVAILABLE" "NOT_ENROLLED"} bioauth-code)
|
||||
(i18n/label :t/grant-face-id-permissions))
|
||||
bioauth-message)]
|
||||
(when content
|
||||
{:utils/show-popup
|
||||
{:title (i18n/label :t/biometric-auth-login-error-title)
|
||||
{:title (i18n/label :t/biometric-auth-login-error-title)
|
||||
:content content}})))
|
||||
|
||||
(fx/defn biometric-init-done
|
||||
(rf/defn biometric-init-done
|
||||
{:events [:biometric-init-done]}
|
||||
[cofx {:keys [bioauth-success bioauth-message bioauth-code]}]
|
||||
(if bioauth-success
|
||||
@@ -161,7 +168,7 @@
|
||||
(popover/show-popover cofx {:view :enable-biometric}))
|
||||
(show-message cofx bioauth-message bioauth-code)))
|
||||
|
||||
(fx/defn biometric-auth
|
||||
(rf/defn biometric-auth
|
||||
{:events [:biometric-authenticate]}
|
||||
[cofx]
|
||||
(authenticate
|
||||
@@ -170,31 +177,31 @@
|
||||
{:reason (i18n/label :t/biometric-auth-reason-login)
|
||||
:ios-fallback-label (i18n/label :t/biometric-auth-login-ios-fallback-label)}))
|
||||
|
||||
(fx/defn enable
|
||||
(rf/defn enable
|
||||
{:events [:biometric/enable]}
|
||||
[cofx]
|
||||
(fx/merge
|
||||
(rf/merge
|
||||
cofx
|
||||
(popover/hide-popover)
|
||||
(authenticate #(re-frame/dispatch [:biometric/setup-done %]) {})))
|
||||
|
||||
(fx/defn disable
|
||||
(rf/defn disable
|
||||
{:events [:biometric/disable]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(fx/merge
|
||||
(rf/merge
|
||||
cofx
|
||||
{:db (-> db
|
||||
(assoc :auth-method keychain/auth-method-none)
|
||||
(assoc-in [:multiaccounts/login :save-password?] false))}
|
||||
(popover/hide-popover)))
|
||||
|
||||
(fx/defn setup-done
|
||||
(rf/defn setup-done
|
||||
{:events [:biometric/setup-done]}
|
||||
[{:keys [db] :as cofx} {:keys [bioauth-success bioauth-message bioauth-code]}]
|
||||
(log/debug "[biometric] setup-done"
|
||||
"bioauth-success" bioauth-success
|
||||
"bioauth-message" bioauth-message
|
||||
"bioauth-code" bioauth-code)
|
||||
"bioauth-code" bioauth-code)
|
||||
(if bioauth-success
|
||||
{:db (assoc db :auth-method keychain/auth-method-biometric-prepare)}
|
||||
(show-message cofx bioauth-message bioauth-code)))
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
(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.multiaccounts.update.core :as multiaccounts.update]
|
||||
[status-im.bottom-sheet.core :as bottom-sheet]
|
||||
[status-im.native-module.core :as native-module]
|
||||
[status-im.ethereum.json-rpc :as json-rpc]
|
||||
[status-im.utils.fx :as fx]
|
||||
[status-im.theme.core :as theme]
|
||||
[status-im.utils.gfycat.core :as gfycat]
|
||||
[status-im.utils.identicon :as identicon]
|
||||
[status-im.theme.core :as theme]
|
||||
[status-im.utils.utils :as utils]
|
||||
[quo.platform :as platform]
|
||||
[status-im2.common.theme.core :as utils.theme]
|
||||
[taoensso.timbre :as log]
|
||||
[clojure.string :as string]
|
||||
[status-im2.common.theme.core :as utils.theme]))
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
;; validate that the given mnemonic was generated from Status Dictionary
|
||||
(re-frame/reg-fx
|
||||
@@ -35,7 +35,7 @@
|
||||
(cond-> {:nickname nickname
|
||||
:display-name display-name
|
||||
: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))
|
||||
(assoc :ens-name (str "@" (or (stateofus/username ens-name) ens-name))))))
|
||||
|
||||
@@ -48,7 +48,8 @@
|
||||
(let [{:keys [nickname
|
||||
ens-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))]
|
||||
(->> [nickname ens-name display-name three-words-name short-public-key]
|
||||
(remove string/blank?)
|
||||
@@ -70,11 +71,13 @@
|
||||
(str "@" (or username ens-name)))
|
||||
(or alias (gfycat/generate-gfy public-key)))))
|
||||
|
||||
(defn contact-by-identity [contacts identity]
|
||||
(defn contact-by-identity
|
||||
[contacts identity]
|
||||
(or (get contacts 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)]
|
||||
(if me?
|
||||
[(or (:preferred-name current-multiaccount)
|
||||
@@ -110,53 +113,61 @@
|
||||
(fn [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]}
|
||||
[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]}
|
||||
[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]}
|
||||
[{:keys [db] :as cofx} value]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{::webview-debug-changed value}
|
||||
(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]}
|
||||
[{:keys [db] :as cofx} private?]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{::blank-preview-flag-changed private?}
|
||||
(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]}
|
||||
[cofx enabled?]
|
||||
(multiaccounts.update/multiaccount-update
|
||||
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]}
|
||||
[cofx value]
|
||||
(multiaccounts.update/multiaccount-update
|
||||
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]}]
|
||||
(let [private? (get-in db [:multiaccount :preview-privacy?])]
|
||||
{::blank-preview-flag-changed private?}))
|
||||
@@ -169,48 +180,49 @@
|
||||
:light)]
|
||||
(theme/change-theme theme))))
|
||||
|
||||
(fx/defn switch-appearance
|
||||
(rf/defn switch-appearance
|
||||
{:events [:multiaccounts.ui/appearance-switched]}
|
||||
[cofx theme]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{:multiaccounts.ui/switch-theme 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]}
|
||||
[cofx id]
|
||||
(fx/merge cofx
|
||||
{::json-rpc/call [{:method "wakuext_changeIdentityImageShowTo"
|
||||
:params [id]
|
||||
(rf/merge cofx
|
||||
{::json-rpc/call [{:method "wakuext_changeIdentityImageShowTo"
|
||||
:params [id]
|
||||
:on-success #(log/debug "picture settings changed successfully")}]}
|
||||
(multiaccounts.update/optimistic :profile-pictures-show-to id)))
|
||||
|
||||
(fx/defn switch-appearance-profile
|
||||
(rf/defn switch-appearance-profile
|
||||
{:events [:multiaccounts.ui/appearance-profile-switched]}
|
||||
[cofx id]
|
||||
(multiaccounts.update/multiaccount-update cofx :profile-pictures-visibility id {}))
|
||||
|
||||
(defn clean-path [path]
|
||||
(defn clean-path
|
||||
[path]
|
||||
(if path
|
||||
(string/replace-first path #"file://" "")
|
||||
(log/warn "[native-module] Empty path was provided")))
|
||||
|
||||
(fx/defn save-profile-picture
|
||||
(rf/defn save-profile-picture
|
||||
{:events [::save-profile-picture]}
|
||||
[cofx path ax ay bx by]
|
||||
(let [key-uid (get-in cofx [:db :multiaccount :key-uid])]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{::json-rpc/call [{:method "multiaccounts_storeIdentityImage"
|
||||
:params [key-uid (clean-path path) ax ay bx by]
|
||||
;; NOTE: In case of an error we can show a toast error
|
||||
:on-success #(re-frame/dispatch [::update-local-picture %])}]}
|
||||
(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]}
|
||||
[cofx url]
|
||||
(let [key-uid (get-in cofx [:db :multiaccount :key-uid])]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{::json-rpc/call [{:method "multiaccounts_storeIdentityImageFromURL"
|
||||
:params [key-uid url]
|
||||
:on-error #(log/error "::save-profile-picture-from-url error" %)
|
||||
@@ -218,32 +230,37 @@
|
||||
(bottom-sheet/hide-bottom-sheet))))
|
||||
|
||||
(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]}
|
||||
[cofx name]
|
||||
(let [key-uid (get-in cofx [:db :multiaccount :key-uid])]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{::json-rpc/call [{:method "multiaccounts_deleteIdentityImage"
|
||||
: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" %)}]}
|
||||
(multiaccounts.update/optimistic :images nil)
|
||||
(bottom-sheet/hide-bottom-sheet))))
|
||||
|
||||
(fx/defn get-profile-picture
|
||||
(rf/defn get-profile-picture
|
||||
[cofx]
|
||||
(let [key-uid (get-in cofx [:db :multiaccount :key-uid])]
|
||||
{::json-rpc/call [{:method "multiaccounts_getIdentityImages"
|
||||
:params [key-uid]
|
||||
:on-success #(re-frame/dispatch [::update-local-picture %])}]}))
|
||||
|
||||
(fx/defn store-profile-picture
|
||||
(rf/defn store-profile-picture
|
||||
{:events [::update-local-picture]}
|
||||
[cofx pics]
|
||||
(multiaccounts.update/optimistic cofx :images pics))
|
||||
|
||||
(comment
|
||||
;; 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))
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
(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.data-store.settings :as data-store.settings]
|
||||
[status-im.ethereum.core :as ethereum]
|
||||
@@ -7,26 +8,27 @@
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
[status-im.native-module.core :as status]
|
||||
[status-im.node.core :as node]
|
||||
[quo.design-system.colors :as colors]
|
||||
[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.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
|
||||
(map (fn [[path {:keys [publicKey] :as data}]]
|
||||
[path (cond-> (-> data
|
||||
(dissoc :publicKey)
|
||||
(assoc :public-key publicKey)))]))
|
||||
[path
|
||||
(cond-> (-> data
|
||||
(dissoc :publicKey)
|
||||
(assoc :public-key publicKey)))]))
|
||||
(into {})))
|
||||
|
||||
(defn normalize-multiaccount-data-keys
|
||||
[{:keys [publicKey keyUid derived] :as data}]
|
||||
(cond-> (-> data
|
||||
(dissoc :keyUid :publicKey)
|
||||
(assoc :key-uid keyUid
|
||||
(assoc :key-uid keyUid
|
||||
:public-key publicKey))
|
||||
derived
|
||||
(update :derived normalize-derived-data-keys)))
|
||||
@@ -49,7 +51,7 @@
|
||||
hashed-password
|
||||
callback)))
|
||||
|
||||
(fx/defn create-multiaccount
|
||||
(rf/defn create-multiaccount
|
||||
{:events [:create-multiaccount]}
|
||||
[{:keys [db]} key-code]
|
||||
(let [{:keys [selected-id]} (:intro-wizard db)]
|
||||
@@ -63,14 +65,14 @@
|
||||
(ethereum/sha3 (security/safe-unmask-data key-code))
|
||||
(fn [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
|
||||
public-key
|
||||
(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
|
||||
[constants/path-whisper-keyword]
|
||||
(merge derived-whisper {:name name :identicon identicon}))]
|
||||
[constants/path-whisper-keyword]
|
||||
(merge derived-whisper {:name name :identicon identicon}))]
|
||||
(re-frame/dispatch [::store-multiaccount-success key-code derived-data-extended]))))))]}))
|
||||
|
||||
(re-frame/reg-fx
|
||||
@@ -86,32 +88,36 @@
|
||||
(mapv normalize-multiaccount-data-keys
|
||||
(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]}
|
||||
[{:keys [db]} result]
|
||||
{:db (update db :intro-wizard
|
||||
(fn [data]
|
||||
(-> data
|
||||
(dissoc :processing?)
|
||||
(assoc :multiaccounts result
|
||||
:selected-storage-type :default
|
||||
:selected-id (-> result first :id)
|
||||
:step :choose-key))))
|
||||
{:db (update db
|
||||
:intro-wizard
|
||||
(fn [data]
|
||||
(-> data
|
||||
(dissoc :processing?)
|
||||
(assoc :multiaccounts result
|
||||
:selected-storage-type :default
|
||||
:selected-id (-> result first :id)
|
||||
:step :choose-key))))
|
||||
:navigate-to-fx :choose-name})
|
||||
|
||||
(fx/defn generate-and-derive-addresses
|
||||
(rf/defn generate-and-derive-addresses
|
||||
{:events [:generate-and-derive-addresses]}
|
||||
[{:keys [db]}]
|
||||
{:db (-> db
|
||||
(update :intro-wizard #(-> % (assoc :processing? true) (dissoc :recovering?)))
|
||||
(dissoc :recovered-account?))
|
||||
{:db (-> db
|
||||
(update :intro-wizard
|
||||
#(-> %
|
||||
(assoc :processing? true)
|
||||
(dissoc :recovering?)))
|
||||
(dissoc :recovered-account?))
|
||||
:multiaccount-generate-and-derive-addresses nil})
|
||||
|
||||
(fx/defn prepare-intro-wizard
|
||||
(rf/defn prepare-intro-wizard
|
||||
[{:keys [db]}]
|
||||
{:db (assoc db :intro-wizard {})})
|
||||
|
||||
(fx/defn save-multiaccount-and-login-with-keycard
|
||||
(rf/defn save-multiaccount-and-login-with-keycard
|
||||
[_ args]
|
||||
{:keycard/save-multiaccount-and-login args})
|
||||
|
||||
@@ -126,7 +132,7 @@
|
||||
config
|
||||
accounts-data)))
|
||||
|
||||
(fx/defn save-account-and-login
|
||||
(rf/defn save-account-and-login
|
||||
[_ key-uid multiaccount-data password settings node-config accounts-data]
|
||||
{::save-account-and-login [key-uid
|
||||
(types/clj->json multiaccount-data)
|
||||
@@ -150,53 +156,57 @@
|
||||
{:public-key public-key
|
||||
:address (eip55/address->checksum address)
|
||||
:name name
|
||||
:identicon identicon
|
||||
:identicon identicon
|
||||
:path constants/path-whisper
|
||||
:chat true})])
|
||||
|
||||
(fx/defn on-multiaccount-created
|
||||
(rf/defn on-multiaccount-created
|
||||
[{:keys [signing-phrase random-guid-generator db] :as cofx}
|
||||
{:keys [address chat-key keycard-instance-uid key-uid
|
||||
keycard-pairing keycard-paired-on mnemonic recovered]
|
||||
:as multiaccount}
|
||||
:as multiaccount}
|
||||
password
|
||||
{: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)
|
||||
multiaccount-data {:name name
|
||||
:address address
|
||||
:identicon identicon
|
||||
:key-uid key-uid
|
||||
(let [[wallet-account {:keys [public-key identicon name]} :as accounts-data] (prepare-accounts-data
|
||||
multiaccount)
|
||||
multiaccount-data {:name name
|
||||
:address address
|
||||
:identicon identicon
|
||||
:key-uid key-uid
|
||||
:keycard-pairing keycard-pairing}
|
||||
keycard-multiaccount? (boolean keycard-pairing)
|
||||
eip1581-address (get-in multiaccount [:derived
|
||||
constants/path-eip1581-keyword
|
||||
:address])
|
||||
eip1581-address (get-in multiaccount
|
||||
[:derived
|
||||
constants/path-eip1581-keyword
|
||||
:address])
|
||||
new-multiaccount
|
||||
(cond-> (merge
|
||||
{;; address of the master key
|
||||
:address address
|
||||
;; sha256 of master public key
|
||||
:key-uid key-uid
|
||||
;; The address from which we derive any wallet
|
||||
:wallet-root-address
|
||||
(get-in multiaccount [:derived
|
||||
constants/path-wallet-root-keyword
|
||||
:address])
|
||||
:name name
|
||||
:identicon identicon
|
||||
;; public key of the chat account
|
||||
:public-key public-key
|
||||
;; default address for Dapps
|
||||
:dapps-address (:address wallet-account)
|
||||
:latest-derived-path 0
|
||||
:signing-phrase signing-phrase
|
||||
:send-push-notifications? true
|
||||
:backup-enabled? true
|
||||
:installation-id (random-guid-generator)
|
||||
;; default mailserver (history node) setting
|
||||
:use-mailservers? true
|
||||
:recovered recovered}
|
||||
config/default-multiaccount)
|
||||
(cond->
|
||||
(merge
|
||||
{;; address of the master key
|
||||
:address address
|
||||
;; sha256 of master public key
|
||||
:key-uid key-uid
|
||||
;; The address from which we derive any wallet
|
||||
:wallet-root-address
|
||||
(get-in multiaccount
|
||||
[:derived
|
||||
constants/path-wallet-root-keyword
|
||||
:address])
|
||||
:name name
|
||||
:identicon identicon
|
||||
;; public key of the chat account
|
||||
:public-key public-key
|
||||
;; default address for Dapps
|
||||
:dapps-address (:address wallet-account)
|
||||
:latest-derived-path 0
|
||||
:signing-phrase signing-phrase
|
||||
:send-push-notifications? true
|
||||
:backup-enabled? true
|
||||
:installation-id (random-guid-generator)
|
||||
;; default mailserver (history node) setting
|
||||
:use-mailservers? true
|
||||
:recovered recovered}
|
||||
config/default-multiaccount)
|
||||
;; The address from which we derive any chat
|
||||
;; account/encryption keys
|
||||
eip1581-address
|
||||
@@ -205,23 +215,23 @@
|
||||
(assoc :mnemonic mnemonic)
|
||||
keycard-multiaccount?
|
||||
(assoc :keycard-instance-uid keycard-instance-uid
|
||||
:keycard-pairing keycard-pairing
|
||||
:keycard-paired-on keycard-paired-on))
|
||||
:keycard-pairing keycard-pairing
|
||||
:keycard-paired-on keycard-paired-on))
|
||||
db (assoc db
|
||||
:multiaccounts/login {:key-uid key-uid
|
||||
:name name
|
||||
:identicon identicon
|
||||
:password password
|
||||
:creating? true
|
||||
:processing true}
|
||||
:multiaccount new-multiaccount
|
||||
:multiaccount/accounts [wallet-account]
|
||||
:multiaccounts/login {:key-uid key-uid
|
||||
:name name
|
||||
:identicon identicon
|
||||
:password password
|
||||
:creating? true
|
||||
:processing true}
|
||||
:multiaccount new-multiaccount
|
||||
:multiaccount/accounts [wallet-account]
|
||||
: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
|
||||
:networks/current-network config/default-network
|
||||
:networks/networks config/default-networks)]
|
||||
(fx/merge cofx
|
||||
:networks/networks config/default-networks)]
|
||||
(rf/merge cofx
|
||||
{:db db}
|
||||
(if keycard-multiaccount?
|
||||
(save-multiaccount-and-login-with-keycard
|
||||
@@ -240,26 +250,26 @@
|
||||
(node/get-new-config db)
|
||||
accounts-data)))))
|
||||
|
||||
(fx/defn store-multiaccount-success
|
||||
{:events [::store-multiaccount-success]
|
||||
(rf/defn store-multiaccount-success
|
||||
{:events [::store-multiaccount-success]
|
||||
:interceptors [(re-frame/inject-cofx :random-guid-generator)
|
||||
(re-frame/inject-cofx ::get-signing-phrase)]}
|
||||
[{:keys [db] :as cofx} password derived]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{:db (dissoc db :intro-wizard)}
|
||||
(on-multiaccount-created (assoc (let [{:keys [selected-id multiaccounts]} (:intro-wizard db)]
|
||||
(some #(when (= selected-id (:id %)) %) multiaccounts))
|
||||
:derived derived
|
||||
:derived derived
|
||||
:recovered (get-in db [:intro-wizard :recovering?]))
|
||||
password
|
||||
{:save-mnemonic? true})))
|
||||
|
||||
(fx/defn on-key-selected
|
||||
(rf/defn on-key-selected
|
||||
{:events [:intro-wizard/on-key-selected]}
|
||||
[{:keys [db]} 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]}
|
||||
[{:keys [db]} storage-type]
|
||||
{:db (assoc-in db [:intro-wizard :selected-storage-type] storage-type)})
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
(ns status-im.multiaccounts.key-storage.core
|
||||
(: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.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.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.model :as multiaccounts.model]
|
||||
[status-im.multiaccounts.recover.core :as multiaccounts.recover]
|
||||
[status-im.native-module.core :as native-module]
|
||||
[status-im2.navigation.events :as navigation]
|
||||
[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.keycard.backup-key :as keycard.backup]
|
||||
[status-im.bottom-sheet.core :as bottom-sheet]))
|
||||
[status-im2.navigation.events :as navigation]
|
||||
[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"
|
||||
{:events [::key-and-storage-management-pressed]}
|
||||
[cofx]
|
||||
@@ -29,52 +29,57 @@
|
||||
:actions-not-logged-in)
|
||||
nil))
|
||||
|
||||
(fx/defn move-keystore-checked
|
||||
(rf/defn move-keystore-checked
|
||||
{:events [::move-keystore-checked]}
|
||||
[{:keys [db] :as cofx} 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]}
|
||||
[{:keys [db] :as cofx} checked?]
|
||||
{:db (assoc-in db [:multiaccounts/key-storage :reset-db-checked?] checked?)})
|
||||
|
||||
(fx/defn navigate-back
|
||||
(rf/defn navigate-back
|
||||
{:events [::navigate-back]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(fx/merge
|
||||
(rf/merge
|
||||
cofx
|
||||
{:db (-> db
|
||||
(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)))
|
||||
|
||||
(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"
|
||||
{:events [::enter-seed-pressed]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(fx/merge
|
||||
(rf/merge
|
||||
cofx
|
||||
{:db (assoc db :recovered-account? true)}
|
||||
(navigation/navigate-to-cofx :seed-phrase nil)))
|
||||
|
||||
(fx/defn seed-phrase-input-changed
|
||||
(rf/defn seed-phrase-input-changed
|
||||
{:events [::seed-phrase-input-changed]}
|
||||
[{:keys [db] :as cofx} masked-seed-phrase]
|
||||
(let [seed-phrase (security/safe-unmask-data masked-seed-phrase)]
|
||||
{:db (update db :multiaccounts/key-storage assoc
|
||||
:seed-phrase (when seed-phrase
|
||||
(string/lower-case seed-phrase))
|
||||
:seed-shape-invalid? (or (empty? seed-phrase)
|
||||
(not (mnemonic/valid-length? seed-phrase)))
|
||||
:seed-word-count (mnemonic/words-count seed-phrase))}))
|
||||
{:db (update db
|
||||
:multiaccounts/key-storage assoc
|
||||
:seed-phrase (when seed-phrase
|
||||
(string/lower-case seed-phrase))
|
||||
:seed-shape-invalid? (or (empty? 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]}
|
||||
[cofx _]
|
||||
(popover/show-popover cofx {:view :seed-key-uid-mismatch}))
|
||||
|
||||
(fx/defn key-uid-matches
|
||||
(rf/defn key-uid-matches
|
||||
{:events [::key-uid-matches]}
|
||||
[{:keys [db] :as cofx} _]
|
||||
(let [backup? (get-in db [:keycard :creating-backup?])]
|
||||
@@ -100,27 +105,32 @@
|
||||
::validate-seed-against-key-uid
|
||||
(partial validate-seed-against-key-uid
|
||||
{:import-mnemonic-fn native-module/multiaccount-import-mnemonic
|
||||
:on-success #(re-frame/dispatch [::key-uid-matches])
|
||||
:on-error #(re-frame/dispatch [::show-seed-key-uid-mismatch-error-popup])}))
|
||||
:on-success #(re-frame/dispatch [::key-uid-matches])
|
||||
: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]}
|
||||
[{:keys [db] :as cofx} validation-error]
|
||||
(let [error? (-> validation-error
|
||||
types/json->clj
|
||||
:error
|
||||
string/blank?
|
||||
not)
|
||||
(let [error? (-> validation-error
|
||||
types/json->clj
|
||||
:error
|
||||
string/blank?
|
||||
not)
|
||||
onboarding? (not (or (:multiaccounts/login db) (:multiaccount db)))]
|
||||
(if error?
|
||||
(popover/show-popover cofx {:view :custom-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
|
||||
:key-uid (or (-> db :multiaccounts/login :key-uid)
|
||||
(-> db :multiaccount :key-uid)
|
||||
(and onboarding? (-> db :keycard :application-info :key-uid)))}})))
|
||||
;; Unique key-uid of the account for which we are going to move
|
||||
;; keys
|
||||
:key-uid (or (-> db :multiaccounts/login :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]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(let [{:keys [seed-phrase]} (:multiaccounts/key-storage db)]
|
||||
@@ -128,7 +138,7 @@
|
||||
[(mnemonic/sanitize-passphrase seed-phrase)
|
||||
#(re-frame/dispatch [::seed-phrase-validated %])]}))
|
||||
|
||||
(fx/defn keycard-storage-pressed
|
||||
(rf/defn keycard-storage-pressed
|
||||
{:events [::keycard-storage-pressed]}
|
||||
[{:keys [db]} 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
|
||||
"
|
||||
(fx/defn import-multiaccount
|
||||
(rf/defn import-multiaccount
|
||||
[{:keys [db] :as cofx}]
|
||||
{:dispatch [:bottom-sheet/hide]
|
||||
{:dispatch [:bottom-sheet/hide]
|
||||
::multiaccounts.recover/import-multiaccount
|
||||
{:passphrase (get-in db [:multiaccounts/key-storage :seed-phrase])
|
||||
:password nil
|
||||
: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]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(fx/merge
|
||||
(rf/merge
|
||||
{:dispatch [:bottom-sheet/hide]
|
||||
:db (assoc-in db [:multiaccounts/key-storage :reset-db-checked?] true)}
|
||||
(import-multiaccount)))
|
||||
|
||||
(fx/defn storage-selected
|
||||
(rf/defn storage-selected
|
||||
{:events [::storage-selected]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(if (get-in db [:multiaccounts/key-storage :reset-db-checked?])
|
||||
(popover/show-popover cofx {:view :transfer-multiaccount-to-keycard-warning})
|
||||
(bottom-sheet/show-bottom-sheet cofx {:view :migrate-account-password})))
|
||||
|
||||
(fx/defn skip-password-pressed
|
||||
(rf/defn skip-password-pressed
|
||||
{:events [::skip-password-pressed]}
|
||||
[cofx]
|
||||
(popover/show-popover cofx {:view :transfer-multiaccount-to-keycard-warning}))
|
||||
|
||||
(fx/defn password-changed
|
||||
(rf/defn password-changed
|
||||
{:events [::password-changed]}
|
||||
[{db :db} password]
|
||||
(let [unmasked-pass (security/safe-unmask-data password)]
|
||||
{:db (update db :keycard assoc
|
||||
:migration-password password
|
||||
:migration-password-error nil
|
||||
{:db (update db
|
||||
:keycard assoc
|
||||
:migration-password password
|
||||
:migration-password-error nil
|
||||
:migration-password-valid? (and unmasked-pass (> (count unmasked-pass) 5)))}))
|
||||
|
||||
(fx/defn verify-password-result
|
||||
(rf/defn verify-password-result
|
||||
{:events [::verify-password-result]}
|
||||
[{:keys [db] :as cofx} result]
|
||||
(let [{:keys [error]} (types/json->clj result)]
|
||||
(if (string/blank? error)
|
||||
(fx/merge
|
||||
(rf/merge
|
||||
cofx
|
||||
{:db (update db :keycard dissoc :migration-password-error :migration-password-valid?)}
|
||||
(import-multiaccount))
|
||||
{:db (assoc-in db [:keycard :migration-password-error] (i18n/label :t/wrong-password))})))
|
||||
|
||||
(fx/defn verify-password
|
||||
(rf/defn verify-password
|
||||
{:events [::verify-password]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(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])))
|
||||
#(re-frame/dispatch [::verify-password-result %])))
|
||||
|
||||
(fx/defn handle-multiaccount-import
|
||||
(rf/defn handle-multiaccount-import
|
||||
{:events [::import-multiaccount-success]}
|
||||
[{:keys [db] :as cofx} root-data derived-data]
|
||||
(fx/merge cofx
|
||||
{:db (-> db
|
||||
(update :intro-wizard
|
||||
assoc
|
||||
:root-key root-data
|
||||
:derived derived-data
|
||||
:recovering? true
|
||||
:selected-storage-type :advanced)
|
||||
(assoc-in [:keycard :flow] :recovery)
|
||||
(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 :delete-account?]
|
||||
(true? (get-in db [:multiaccounts/key-storage :reset-db-checked?])))
|
||||
(dissoc :multiaccounts/key-storage))}
|
||||
(rf/merge cofx
|
||||
{:db (-> db
|
||||
(update :intro-wizard
|
||||
assoc
|
||||
:root-key root-data
|
||||
:derived derived-data
|
||||
:recovering? true
|
||||
:selected-storage-type :advanced)
|
||||
(assoc-in [:keycard :flow] :recovery)
|
||||
(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 :delete-account?]
|
||||
(true? (get-in db [:multiaccounts/key-storage :reset-db-checked?])))
|
||||
(dissoc :multiaccounts/key-storage))}
|
||||
(popover/hide-popover)
|
||||
(common/listen-to-hardware-back-button)
|
||||
(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]}
|
||||
[cofx _]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(popover/hide-popover)
|
||||
(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]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{:db (assoc db :goto-key-storage? true)}
|
||||
(multiaccounts.logout/logout)))
|
||||
|
||||
(fx/defn logout-and-goto-key-storage
|
||||
(rf/defn logout-and-goto-key-storage
|
||||
{:events [::logout-and-goto-key-storage]}
|
||||
[_]
|
||||
{:ui/show-confirmation
|
||||
@@ -264,9 +276,11 @@ We don't need to take the exact steps, just set the required state and redirect
|
||||
|
||||
(comment
|
||||
;; check import mnemonic output
|
||||
(native-module/multiaccount-import-mnemonic "rocket mixed rebel affair umbrella legal resemble scene virus park deposit cargo" nil
|
||||
(fn [result]
|
||||
(prn (types/json->clj result))))
|
||||
(native-module/multiaccount-import-mnemonic
|
||||
"rocket mixed rebel affair umbrella legal resemble scene virus park deposit cargo"
|
||||
nil
|
||||
(fn [result]
|
||||
(prn (types/json->clj result))))
|
||||
;; check delete account output
|
||||
(native-module/delete-multiaccount "0x3831d0f22996a65970a214f0a94bfa9a63a21dac235d8dadb91be8e32e7d3ab7"
|
||||
(fn [result]
|
||||
|
||||
@@ -1,48 +1,48 @@
|
||||
(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.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.visibility-status-updates :as visibility-status-updates-store]
|
||||
[status-im.ethereum.core :as ethereum]
|
||||
[status-im.ethereum.transactions.core :as transactions]
|
||||
[status-im.ethereum.eip55 :as eip55]
|
||||
[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.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.core :as multiaccounts]
|
||||
[status-im.native-module.core :as status]
|
||||
[status-im.node.core :as node]
|
||||
[status-im.notifications.core :as notifications]
|
||||
[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.mobile-sync-settings.core :as mobile-network]
|
||||
[status-im.utils.fx :as fx]
|
||||
[status-im.ui.components.react :as react]
|
||||
[status-im.utils.config :as config]
|
||||
[status-im.utils.keychain.core :as keychain]
|
||||
[status-im2.setup.log :as logging]
|
||||
[utils.security.core :as security]
|
||||
[status-im.utils.mobile-sync :as utils.mobile-sync]
|
||||
[status-im.utils.platform :as platform]
|
||||
[status-im.utils.types :as types]
|
||||
[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.core :as wallet]
|
||||
[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.navigation.events :as navigation]
|
||||
[status-im.signing.eip1559 :as eip1559]
|
||||
[status-im.data-store.chats :as data-store.chats]
|
||||
[status-im.data-store.visibility-status-updates :as visibility-status-updates-store]
|
||||
[status-im.ui.components.react :as react]
|
||||
[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]))
|
||||
[status-im2.setup.log :as logging]
|
||||
[taoensso.timbre :as log]
|
||||
[utils.re-frame :as rf]
|
||||
[utils.security.core :as security]))
|
||||
|
||||
(re-frame/reg-fx
|
||||
::initialize-communities-enabled
|
||||
@@ -89,11 +89,13 @@
|
||||
#(re-frame/dispatch [:wallet-connect/client-init %])
|
||||
#(log/error "[wallet-connect]" %))))
|
||||
|
||||
(defn rpc->accounts [accounts]
|
||||
(defn rpc->accounts
|
||||
[accounts]
|
||||
(reduce (fn [acc {:keys [chat type wallet] :as account}]
|
||||
(if chat
|
||||
acc
|
||||
(let [account (cond-> (update account :address
|
||||
(let [account (cond-> (update account
|
||||
:address
|
||||
eip55/address->checksum)
|
||||
type
|
||||
(update :type keyword))]
|
||||
@@ -119,25 +121,28 @@
|
||||
"0x86a12d91c813f69a53349ff640e32af97d5f5c1f8d42d54cf4c8aa8dea231955"
|
||||
"0x0011a30f5b2023bc228f6dd5608b3e7149646fa83f33350926ceb1925af78f08"})
|
||||
|
||||
(fx/defn check-invalid-ens [{:keys [db]}]
|
||||
(rf/defn check-invalid-ens
|
||||
[{:keys [db]}]
|
||||
(async-storage/get-item
|
||||
:invalid-ens-name-seen
|
||||
(fn [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
|
||||
(i18n/label :t/warning)
|
||||
(i18n/label :t/ens-username-invalid-name-warning)
|
||||
#(async-storage/set-item! :invalid-ens-name-seen true)))))
|
||||
nil)
|
||||
|
||||
(fx/defn initialize-wallet
|
||||
(rf/defn initialize-wallet
|
||||
{:events [::initialize-wallet]}
|
||||
[{:keys [db] :as cofx} accounts tokens custom-tokens
|
||||
favourites scan-all-tokens? new-account?]
|
||||
(fx/merge
|
||||
(rf/merge
|
||||
cofx
|
||||
{:db (assoc db :multiaccount/accounts
|
||||
{:db (assoc db
|
||||
:multiaccount/accounts
|
||||
(rpc->accounts accounts))
|
||||
;; NOTE: Local notifications should be enabled only after wallet was started
|
||||
::enable-local-notifications nil}
|
||||
@@ -146,16 +151,18 @@
|
||||
(wallet/initialize-favourites favourites)
|
||||
(wallet/get-pending-transactions)
|
||||
(wallet/fetch-collectibles-collection)
|
||||
(cond (and new-account?
|
||||
(not scan-all-tokens?))
|
||||
(wallet/set-zero-balances (first accounts))
|
||||
(cond
|
||||
(and new-account?
|
||||
(not scan-all-tokens?))
|
||||
(wallet/set-zero-balances (first accounts))
|
||||
|
||||
(and new-account? scan-all-tokens?
|
||||
(not (utils.mobile-sync/cellular? (:network/type db))))
|
||||
(wallet/set-max-block (get (first accounts) :address) 0)
|
||||
(and new-account?
|
||||
scan-all-tokens?
|
||||
(not (utils.mobile-sync/cellular? (:network/type db))))
|
||||
(wallet/set-max-block (get (first accounts) :address) 0)
|
||||
|
||||
:else
|
||||
(wallet/get-cached-balances scan-all-tokens?))
|
||||
:else
|
||||
(wallet/get-cached-balances scan-all-tokens?))
|
||||
(when-not (get db :wallet/new-account)
|
||||
(wallet/restart-wallet-service nil))
|
||||
(when (or (not (utils.mobile-sync/syncing-allowed? cofx))
|
||||
@@ -166,21 +173,21 @@
|
||||
(prices/update-prices)
|
||||
(wallet-connect-legacy/get-connector-session-from-db)))
|
||||
|
||||
(fx/defn login
|
||||
(rf/defn login
|
||||
{:events [:multiaccounts.login.ui/password-input-submitted]}
|
||||
[{:keys [db]}]
|
||||
(let [{:keys [key-uid password name identicon]} (:multiaccounts/login db)]
|
||||
{:db (-> db
|
||||
(assoc-in [:multiaccounts/login :processing] true)
|
||||
(dissoc :intro-wizard :recovered-account?)
|
||||
(update :keycard dissoc :flow))
|
||||
{:db (-> db
|
||||
(assoc-in [:multiaccounts/login :processing] true)
|
||||
(dissoc :intro-wizard :recovered-account?)
|
||||
(update :keycard dissoc :flow))
|
||||
::login [key-uid
|
||||
(types/clj->json {:name name
|
||||
:key-uid key-uid
|
||||
:identicon identicon})
|
||||
(types/clj->json {:name name
|
||||
:key-uid key-uid
|
||||
:identicon identicon})
|
||||
(ethereum/sha3 (security/safe-unmask-data password))]}))
|
||||
|
||||
(fx/defn export-db-submitted
|
||||
(rf/defn export-db-submitted
|
||||
{:events [:multiaccounts.login.ui/export-db-submitted]}
|
||||
[{:keys [db]}]
|
||||
(let [{:keys [key-uid password name identicon]} (:multiaccounts/login db)]
|
||||
@@ -192,24 +199,25 @@
|
||||
(fn [path]
|
||||
(when platform/ios?
|
||||
(let [uri (str "file://" path)]
|
||||
(.share ^js react/sharing (clj->js {:title "Unencrypted database"
|
||||
:url uri})))))]}))
|
||||
(.share ^js react/sharing
|
||||
(clj->js {:title "Unencrypted database"
|
||||
:url uri})))))]}))
|
||||
|
||||
(fx/defn import-db-submitted
|
||||
(rf/defn import-db-submitted
|
||||
{:events [:multiaccounts.login.ui/import-db-submitted]}
|
||||
[{:keys [db]}]
|
||||
(let [{:keys [key-uid password name identicon]} (:multiaccounts/login db)]
|
||||
{::import-db [key-uid
|
||||
(types/clj->json {:name name
|
||||
:key-uid key-uid
|
||||
:identicon identicon})
|
||||
(types/clj->json {:name name
|
||||
:key-uid key-uid
|
||||
:identicon identicon})
|
||||
(ethereum/sha3 (security/safe-unmask-data password))]}))
|
||||
|
||||
(fx/defn finish-keycard-setup
|
||||
(rf/defn finish-keycard-setup
|
||||
[{:keys [db] :as cofx}]
|
||||
{:db (update db :keycard dissoc :flow)})
|
||||
|
||||
(fx/defn initialize-dapp-permissions
|
||||
(rf/defn initialize-dapp-permissions
|
||||
{:events [::initialize-dapp-permissions]}
|
||||
[{:keys [db]} all-dapp-permissions]
|
||||
(let [dapp-permissions (reduce (fn [acc {:keys [dapp] :as dapp-permissions}]
|
||||
@@ -218,7 +226,7 @@
|
||||
all-dapp-permissions)]
|
||||
{:db (assoc db :dapps/permissions dapp-permissions)}))
|
||||
|
||||
(fx/defn initialize-browsers
|
||||
(rf/defn initialize-browsers
|
||||
{:events [::initialize-browsers]}
|
||||
[{:keys [db]} all-stored-browsers]
|
||||
(let [browsers (reduce (fn [acc {:keys [browser-id] :as browser}]
|
||||
@@ -227,7 +235,7 @@
|
||||
all-stored-browsers)]
|
||||
{:db (assoc db :browser/browsers browsers)}))
|
||||
|
||||
(fx/defn initialize-bookmarks
|
||||
(rf/defn initialize-bookmarks
|
||||
{:events [::initialize-bookmarks]}
|
||||
[{:keys [db]} stored-bookmarks]
|
||||
(let [bookmarks (reduce (fn [acc {:keys [url] :as bookmark}]
|
||||
@@ -236,28 +244,30 @@
|
||||
stored-bookmarks)]
|
||||
{:db (assoc db :bookmarks/bookmarks bookmarks)}))
|
||||
|
||||
(fx/defn initialize-invitations
|
||||
(rf/defn initialize-invitations
|
||||
{:events [::initialize-invitations]}
|
||||
[{:keys [db]} invitations]
|
||||
{:db (assoc db :group-chat/invitations (reduce (fn [acc {:keys [id] :as inv}]
|
||||
(assoc acc id (data-store.invitations/<-rpc inv)))
|
||||
{}
|
||||
invitations))})
|
||||
{:db (assoc db
|
||||
:group-chat/invitations
|
||||
(reduce (fn [acc {:keys [id] :as inv}]
|
||||
(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]}
|
||||
[{:keys [db]} 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]}
|
||||
[_]
|
||||
{:ui/close-application nil})
|
||||
|
||||
(fx/defn check-network-version
|
||||
(rf/defn check-network-version
|
||||
[_ network-id]
|
||||
{::json-rpc/call
|
||||
[{:method "net_version"
|
||||
[{:method "net_version"
|
||||
:on-success
|
||||
(fn [fetched-network-id]
|
||||
(when (not= network-id (str (int fetched-network-id)))
|
||||
@@ -270,7 +280,8 @@
|
||||
:fetched-network-id fetched-network-id})
|
||||
#(re-frame/dispatch [::close-app-confirmed]))))}]})
|
||||
|
||||
(defn normalize-tokens [network-id tokens]
|
||||
(defn normalize-tokens
|
||||
[network-id tokens]
|
||||
(mapv #(-> %
|
||||
(update :symbol keyword)
|
||||
((partial tokens/update-icon (ethereum/chain-id->chain-keyword (int network-id)))))
|
||||
@@ -281,8 +292,8 @@
|
||||
(fn [[network-id accounts recovered-account?]]
|
||||
(utils/set-timeout
|
||||
(fn []
|
||||
(json-rpc/call {:method "wallet_getTokens"
|
||||
:params [(int network-id)]
|
||||
(json-rpc/call {:method "wallet_getTokens"
|
||||
:params [(int network-id)]
|
||||
:on-success #(re-frame/dispatch [::initialize-wallet
|
||||
accounts
|
||||
(normalize-tokens network-id %)
|
||||
@@ -299,25 +310,25 @@
|
||||
(clj->js
|
||||
[(js/Promise.
|
||||
(fn [resolve reject]
|
||||
(json-rpc/call {:method "accounts_getAccounts"
|
||||
(json-rpc/call {:method "accounts_getAccounts"
|
||||
:on-success resolve
|
||||
:on-error reject})))
|
||||
:on-error reject})))
|
||||
(js/Promise.
|
||||
(fn [resolve _]
|
||||
(json-rpc/call {:method "wallet_getTokens"
|
||||
:params [(int network-id)]
|
||||
(json-rpc/call {:method "wallet_getTokens"
|
||||
:params [(int network-id)]
|
||||
:on-success resolve
|
||||
:on-error #(resolve nil)})))
|
||||
:on-error #(resolve nil)})))
|
||||
(js/Promise.
|
||||
(fn [resolve reject]
|
||||
(json-rpc/call {:method "wallet_getCustomTokens"
|
||||
(json-rpc/call {:method "wallet_getCustomTokens"
|
||||
:on-success resolve
|
||||
:on-error reject})))
|
||||
:on-error reject})))
|
||||
(js/Promise.
|
||||
(fn [resolve reject]
|
||||
(json-rpc/call {:method "wallet_getSavedAddresses"
|
||||
(json-rpc/call {:method "wallet_getSavedAddresses"
|
||||
:on-success resolve
|
||||
:on-error reject})))]))
|
||||
:on-error reject})))]))
|
||||
(.then (fn [[accounts tokens custom-tokens favourites]]
|
||||
(callback accounts
|
||||
(normalize-tokens network-id tokens)
|
||||
@@ -326,7 +337,8 @@
|
||||
(.catch (fn [_]
|
||||
(log/error "Failed to initialize wallet"))))))
|
||||
|
||||
(fx/defn initialize-browser [_]
|
||||
(rf/defn initialize-browser
|
||||
[_]
|
||||
{::json-rpc/call
|
||||
[{:method "wakuext_getBrowsers"
|
||||
:on-success #(re-frame/dispatch [::initialize-browsers %])}
|
||||
@@ -335,53 +347,57 @@
|
||||
{:method "permissions_getDappPermissions"
|
||||
: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])})
|
||||
|
||||
(fx/defn get-group-chat-invitations [_]
|
||||
(rf/defn get-group-chat-invitations
|
||||
[_]
|
||||
{::json-rpc/call
|
||||
[{:method "wakuext_getGroupChatInvitations"
|
||||
:on-success #(re-frame/dispatch [::initialize-invitations %])}]})
|
||||
|
||||
(fx/defn initialize-communities-enabled
|
||||
(rf/defn initialize-communities-enabled
|
||||
[cofx]
|
||||
{::initialize-communities-enabled nil})
|
||||
|
||||
(fx/defn initialize-transactions-management-enabled
|
||||
(rf/defn initialize-transactions-management-enabled
|
||||
[cofx]
|
||||
{::initialize-transactions-management-enabled nil})
|
||||
|
||||
(fx/defn initialize-wallet-connect
|
||||
(rf/defn initialize-wallet-connect
|
||||
[cofx]
|
||||
{::initialize-wallet-connect nil})
|
||||
|
||||
(fx/defn get-node-config-callback
|
||||
(rf/defn get-node-config-callback
|
||||
{:events [::get-node-config-callback]}
|
||||
[{:keys [db] :as cofx} node-config-json]
|
||||
(let [node-config (types/json->clj node-config-json)]
|
||||
{:db (assoc-in db [:multiaccount :wakuv2-config]
|
||||
(get node-config :WakuV2Config))}))
|
||||
{:db (assoc-in db
|
||||
[: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 %])))
|
||||
|
||||
(fx/defn get-settings-callback
|
||||
(rf/defn get-settings-callback
|
||||
{:events [::get-settings-callback]}
|
||||
[{:keys [db] :as cofx} settings]
|
||||
(let [{:networks/keys [current-network networks]
|
||||
:as settings}
|
||||
(data-store.settings/rpc->settings settings)
|
||||
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
|
||||
merged-networks (merge networks config/default-networks-by-id)]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{:db (-> db
|
||||
(dissoc :multiaccounts/login)
|
||||
(assoc :networks/current-network current-network
|
||||
:networks/networks merged-networks
|
||||
:multiaccount multiaccount))}
|
||||
:networks/networks merged-networks
|
||||
:multiaccount multiaccount))}
|
||||
(data-store.chats/fetch-chats-rpc
|
||||
{:on-success
|
||||
#(do (re-frame/dispatch [:chats-list/load-success %])
|
||||
@@ -408,31 +424,32 @@
|
||||
(when (= stored-key-uid key-uid)
|
||||
(re-frame/dispatch [:chat.ui/navigate-to-chat chat-id])))))))))
|
||||
|
||||
(fx/defn check-last-chat
|
||||
(rf/defn check-last-chat
|
||||
{:events [::check-last-chat]}
|
||||
[{:keys [db]}]
|
||||
{::open-last-chat (get-in db [:multiaccount :key-uid])})
|
||||
|
||||
(fx/defn update-wallet-accounts
|
||||
(rf/defn update-wallet-accounts
|
||||
[{:keys [db]} accounts]
|
||||
(let [existing-accounts (into {} (map #(vector (:address %1) %1) (:multiaccount/accounts db)))
|
||||
reduce-fn (fn [existing-accs new-acc]
|
||||
(let [address (:address new-acc)]
|
||||
(if (:removed new-acc)
|
||||
(dissoc existing-accs address)
|
||||
(assoc existing-accs address new-acc))))
|
||||
new-accounts (reduce reduce-fn existing-accounts (rpc->accounts accounts))]
|
||||
reduce-fn (fn [existing-accs new-acc]
|
||||
(let [address (:address new-acc)]
|
||||
(if (:removed new-acc)
|
||||
(dissoc existing-accs address)
|
||||
(assoc existing-accs address new-acc))))
|
||||
new-accounts (reduce reduce-fn existing-accounts (rpc->accounts accounts))]
|
||||
{:db (assoc db :multiaccount/accounts (vals new-accounts))}))
|
||||
|
||||
(fx/defn get-chats-callback
|
||||
(rf/defn get-chats-callback
|
||||
{:events [::get-chats-callback]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(let [{:networks/keys [current-network networks]} db
|
||||
notifications-enabled? (get-in db [:multiaccount :notifications-enabled?])
|
||||
network-id (str (get-in networks [current-network :config :NetworkId]))
|
||||
notifications-enabled? (get-in db [:multiaccount :notifications-enabled?])
|
||||
network-id (str (get-in networks
|
||||
[current-network :config :NetworkId]))
|
||||
remote-push-notifications-enabled?
|
||||
(get-in db [:multiaccount :remote-push-notifications-enabled?])]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(cond-> {::eip1559/check-eip1559-activation
|
||||
{:network-id network-id
|
||||
:on-enabled #(log/info "eip1550 is activated")
|
||||
@@ -442,7 +459,7 @@
|
||||
(fn [accounts tokens custom-tokens favourites]
|
||||
(re-frame/dispatch [::initialize-wallet
|
||||
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?)
|
||||
(assoc ::notifications/enable remote-push-notifications-enabled?))
|
||||
(transport/start-messenger)
|
||||
@@ -457,7 +474,8 @@
|
||||
(link-preview/request-link-preview-whitelist)
|
||||
(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-not (or (= keychain/auth-method-biometric 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 :tos])))
|
||||
|
||||
(fx/defn login-only-events
|
||||
(rf/defn login-only-events
|
||||
[{:keys [db] :as cofx} key-uid password save-password?]
|
||||
(let [auth-method (:auth-method db)
|
||||
new-auth-method (get-new-auth-method auth-method save-password?)]
|
||||
(log/debug "[login] login-only-events"
|
||||
"auth-method" auth-method
|
||||
"auth-method" auth-method
|
||||
"new-auth-method" new-auth-method)
|
||||
(fx/merge cofx
|
||||
{:db (assoc db :chats/loading? true)
|
||||
(rf/merge cofx
|
||||
{:db (assoc db :chats/loading? true)
|
||||
::json-rpc/call
|
||||
[{:method "settings_getSettings"
|
||||
:on-success #(do (re-frame/dispatch [::get-settings-callback %])
|
||||
@@ -488,24 +506,26 @@
|
||||
(notifications/load-notification-preferences)
|
||||
(when save-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?]
|
||||
(let [{:keys [multiaccount
|
||||
:multiaccounts/multiaccounts
|
||||
:multiaccount/accounts]} db
|
||||
{:keys [creating?]} (:multiaccounts/login db)
|
||||
first-account? (and creating?
|
||||
(empty? multiaccounts))
|
||||
tos-accepted? (get db :tos/accepted?)
|
||||
:multiaccount/accounts]}
|
||||
db
|
||||
{:keys [creating?]} (:multiaccounts/login db)
|
||||
first-account? (and creating?
|
||||
(empty? multiaccounts))
|
||||
tos-accepted? (get db :tos/accepted?)
|
||||
{:networks/keys [current-network networks]} db
|
||||
network-id (str (get-in networks [current-network :config :NetworkId]))]
|
||||
(fx/merge cofx
|
||||
{:db (-> db
|
||||
(dissoc :multiaccounts/login)
|
||||
(assoc :tos/next-root :onboarding-notification :chats/loading? false)
|
||||
(assoc-in [:multiaccount :multiaccounts/first-account] first-account?))
|
||||
(rf/merge cofx
|
||||
{:db (-> db
|
||||
(dissoc :multiaccounts/login)
|
||||
(assoc :tos/next-root :onboarding-notification :chats/loading? false)
|
||||
(assoc-in [:multiaccount :multiaccounts/first-account] first-account?))
|
||||
::get-tokens [network-id accounts recovered-account?]}
|
||||
(finish-keycard-setup)
|
||||
(transport/start-messenger)
|
||||
@@ -525,14 +545,16 @@
|
||||
(navigation/init-root :onboarding-notification)
|
||||
(navigation/init-root :tos))))))
|
||||
|
||||
(defn- keycard-setup? [cofx]
|
||||
(defn- keycard-setup?
|
||||
[cofx]
|
||||
(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
|
||||
(dissoc :connectivity/ui-status-properties)
|
||||
(update :keycard dissoc :from-key-storage-and-migration?)
|
||||
(update :keycard dissoc
|
||||
(update :keycard dissoc
|
||||
:on-card-read
|
||||
:card-read-in-progress?
|
||||
:pin
|
||||
@@ -544,23 +566,25 @@
|
||||
(assoc :logged-in-since now)
|
||||
(assoc :view-id :home)))
|
||||
|
||||
(fx/defn multiaccount-login-success
|
||||
(rf/defn multiaccount-login-success
|
||||
[{:keys [db now] :as cofx}]
|
||||
(let [{:keys [key-uid password save-password? creating?]}
|
||||
(:multiaccounts/login db)
|
||||
|
||||
multiaccounts (:multiaccounts/multiaccounts db)
|
||||
recovered-account? (get db :recovered-account?)
|
||||
login-only? (not (or creating?
|
||||
recovered-account?
|
||||
(keycard-setup? cofx)))
|
||||
from-migration? (get-in db [:keycard :from-key-storage-and-migration?])
|
||||
nodes nil]
|
||||
multiaccounts (:multiaccounts/multiaccounts db)
|
||||
recovered-account? (get db :recovered-account?)
|
||||
login-only? (not (or creating?
|
||||
recovered-account?
|
||||
(keycard-setup? cofx)))
|
||||
from-migration? (get-in db
|
||||
[:keycard
|
||||
:from-key-storage-and-migration?])
|
||||
nodes nil]
|
||||
(log/debug "[multiaccount] multiaccount-login-success"
|
||||
"login-only?" login-only?
|
||||
"login-only?" login-only?
|
||||
"recovered-account?" recovered-account?)
|
||||
(fx/merge cofx
|
||||
{:db (on-login-update-db db login-only? now)
|
||||
(rf/merge cofx
|
||||
{:db (on-login-update-db db login-only? now)
|
||||
::json-rpc/call
|
||||
[{:method "web3_clientVersion"
|
||||
:on-success #(re-frame/dispatch [::initialize-web3-client-version %])}]}
|
||||
@@ -571,7 +595,8 @@
|
||||
(not recovered-account?))
|
||||
(wallet/set-initial-blocks-range))
|
||||
(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?
|
||||
(login-only-events key-uid password save-password?)
|
||||
(create-only-events recovered-account?)))))
|
||||
@@ -582,7 +607,7 @@
|
||||
;; 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
|
||||
;; make it clearer
|
||||
(fx/defn open-login
|
||||
(rf/defn open-login
|
||||
[{:keys [db]} override-multiaccount]
|
||||
{:db (-> db
|
||||
(update :multiaccounts/login
|
||||
@@ -593,23 +618,26 @@
|
||||
:error
|
||||
:password))})
|
||||
|
||||
(fx/defn open-login-callback
|
||||
(rf/defn open-login-callback
|
||||
{:events [:multiaccounts.login.callback/get-user-password-success]}
|
||||
[{:keys [db] :as cofx} password]
|
||||
(let [key-uid (get-in db [:multiaccounts/login :key-uid])
|
||||
keycard-account? (boolean (get-in db [:multiaccounts/multiaccounts
|
||||
key-uid
|
||||
:keycard-pairing]))
|
||||
(let [key-uid (get-in db [:multiaccounts/login :key-uid])
|
||||
keycard-account? (boolean (get-in db
|
||||
[:multiaccounts/multiaccounts
|
||||
key-uid
|
||||
:keycard-pairing]))
|
||||
goto-key-storage? (:goto-key-storage? db)]
|
||||
(if password
|
||||
(fx/merge
|
||||
(rf/merge
|
||||
cofx
|
||||
{:db (update-in db [:multiaccounts/login] assoc
|
||||
:password password
|
||||
:save-password? true)
|
||||
{:db (update-in db
|
||||
[:multiaccounts/login]
|
||||
assoc
|
||||
:password password
|
||||
:save-password? true)
|
||||
:init-root-fx :progress}
|
||||
login)
|
||||
(fx/merge
|
||||
(rf/merge
|
||||
cofx
|
||||
{:db (dissoc db :goto-key-storage?)}
|
||||
(when keycard-account?
|
||||
@@ -622,25 +650,28 @@
|
||||
#(when goto-key-storage?
|
||||
(navigation/navigate-to-cofx % :actions-not-logged-in nil))))))
|
||||
|
||||
(fx/defn get-credentials
|
||||
(rf/defn get-credentials
|
||||
[{: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"
|
||||
"keycard-multiacc?" keycard-multiaccount?)
|
||||
"keycard-multiacc?"
|
||||
keycard-multiaccount?)
|
||||
(if keycard-multiaccount?
|
||||
(keychain/get-keycard-keys 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\""
|
||||
{:events [:multiaccounts.login/get-auth-method-success]}
|
||||
[{:keys [db] :as cofx} auth-method]
|
||||
(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"
|
||||
"auth-method" auth-method
|
||||
"auth-method" auth-method
|
||||
"keycard-multiacc?" keycard-multiaccount?)
|
||||
(fx/merge
|
||||
(rf/merge
|
||||
cofx
|
||||
{:db (assoc db :auth-method auth-method)}
|
||||
#(cond
|
||||
@@ -653,7 +684,7 @@
|
||||
(keycard.common/get-application-info % nil))
|
||||
(open-login-callback nil))))
|
||||
|
||||
(fx/defn biometric-auth-done
|
||||
(rf/defn biometric-auth-done
|
||||
{:events [:biometric-auth-done]}
|
||||
[{:keys [db] :as cofx} {:keys [bioauth-success bioauth-message bioauth-code]}]
|
||||
(let [key-uid (get-in db [:multiaccounts/login :key-uid])
|
||||
@@ -661,28 +692,28 @@
|
||||
(log/debug "[biometric] biometric-auth-done"
|
||||
"bioauth-success" bioauth-success
|
||||
"bioauth-message" bioauth-message
|
||||
"bioauth-code" bioauth-code)
|
||||
"bioauth-code" bioauth-code)
|
||||
(if bioauth-success
|
||||
(get-credentials cofx key-uid)
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{:db (assoc-in db
|
||||
[:multiaccounts/login :save-password?]
|
||||
(= auth-method keychain/auth-method-biometric))}
|
||||
[:multiaccounts/login :save-password?]
|
||||
(= auth-method keychain/auth-method-biometric))}
|
||||
(when-not (= auth-method keychain/auth-method-biometric)
|
||||
(keychain/save-auth-method key-uid keychain/auth-method-none))
|
||||
(biometric/show-message bioauth-message bioauth-code)
|
||||
(open-login-callback nil)))))
|
||||
|
||||
(fx/defn save-password
|
||||
(rf/defn save-password
|
||||
{:events [:multiaccounts/save-password]}
|
||||
[{:keys [db] :as cofx} save-password?]
|
||||
(let [bioauth-supported? (boolean (get db :supported-biometric-auth))
|
||||
previous-auth-method (get db :auth-method)]
|
||||
(log/debug "[login] save-password"
|
||||
"save-password?" save-password?
|
||||
"bioauth-supported?" bioauth-supported?
|
||||
"save-password?" save-password?
|
||||
"bioauth-supported?" bioauth-supported?
|
||||
"previous-auth-method" previous-auth-method)
|
||||
(fx/merge
|
||||
(rf/merge
|
||||
cofx
|
||||
{:db (cond-> db
|
||||
(not= previous-auth-method
|
||||
@@ -700,54 +731,54 @@
|
||||
(when-not (= previous-auth-method keychain/auth-method-none)
|
||||
(popover/show-popover {:view :disable-password-saving})))))))
|
||||
|
||||
(fx/defn welcome-lets-go
|
||||
(rf/defn welcome-lets-go
|
||||
{:events [:welcome-lets-go]}
|
||||
[_]
|
||||
{:init-root-fx :chat-stack})
|
||||
|
||||
(fx/defn multiaccount-selected
|
||||
(rf/defn multiaccount-selected
|
||||
{:events [:multiaccounts.login.ui/multiaccount-selected]}
|
||||
[{:keys [db] :as cofx} key-uid]
|
||||
;; We specifically pass a bunch of fields instead of the whole multiaccount
|
||||
;; 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))]
|
||||
(fx/merge
|
||||
(rf/merge
|
||||
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)}
|
||||
(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]}
|
||||
[{:keys [db]}]
|
||||
{:db (assoc db :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]}
|
||||
[{:keys [db]} {:keys [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]}
|
||||
[_]
|
||||
{::async-storage/get {:keys [:keycard-banner-hidden]
|
||||
: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]}
|
||||
[{:keys [db]} {:keys [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"
|
||||
{:events [:get-opted-in-to-new-terms-of-service]}
|
||||
[{:keys [db]}]
|
||||
{::async-storage/get {:keys [:new-terms-of-service-accepted]
|
||||
: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]}
|
||||
[{:keys [db]}]
|
||||
{::async-storage/set! {:new-terms-of-service-accepted true}
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
(:require [cljs.test :as test]
|
||||
[status-im.multiaccounts.biometric.core :as biometric]
|
||||
[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/testing "check save password, biometric unavailable"
|
||||
@@ -44,7 +44,7 @@
|
||||
"uncheck save password")
|
||||
(let [initial-cofx {:db {:auth-method keychain/auth-method-none
|
||||
:supported-biometric-auth true}}
|
||||
{:keys [db]} (login/save-password (fx/merge
|
||||
{:keys [db]} (login/save-password (rf/merge
|
||||
initial-cofx
|
||||
(login/save-password true)
|
||||
(biometric/enable)
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
(ns status-im.multiaccounts.logout.core
|
||||
(:require [re-frame.core :as re-frame]
|
||||
[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.utils.keychain.core :as keychain]
|
||||
[status-im.native-module.core :as status]
|
||||
[status-im.notifications.core :as notifications]
|
||||
[status-im.utils.keychain.core :as keychain]
|
||||
[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]}
|
||||
[{:keys [db] :as cofx} {:keys [auth-method logout?]}]
|
||||
(let [key-uid (get-in db [:multiaccount :key-uid])]
|
||||
(fx/merge cofx
|
||||
(let [key-uid (get-in db [:multiaccount :key-uid])]
|
||||
(rf/merge cofx
|
||||
{:init-root-fx :progress
|
||||
:chat.ui/clear-inputs nil
|
||||
:chat.ui/clear-inputs-old nil
|
||||
@@ -22,24 +22,26 @@
|
||||
::logout nil
|
||||
::multiaccounts/webview-debug-changed false
|
||||
: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)
|
||||
(wallet/clear-timeouts)
|
||||
(init/initialize-app-db))))
|
||||
|
||||
(fx/defn logout
|
||||
{:events [:logout :multiaccounts.logout.ui/logout-confirmed :multiaccounts.update.callback/save-settings-success]}
|
||||
(rf/defn logout
|
||||
{:events [:logout :multiaccounts.logout.ui/logout-confirmed
|
||||
:multiaccounts.update.callback/save-settings-success]}
|
||||
[cofx]
|
||||
;; we need to disable notifications before starting the logout process
|
||||
(fx/merge cofx
|
||||
{:dispatch [:wallet-connect-legacy/clean-up-sessions]
|
||||
(rf/merge cofx
|
||||
{:dispatch [:wallet-connect-legacy/clean-up-sessions]
|
||||
:dispatch-later [{:ms 100
|
||||
:dispatch [::logout-method
|
||||
{:auth-method keychain/auth-method-none
|
||||
:logout? true}]}]}
|
||||
(notifications/logout-disable)))
|
||||
|
||||
(fx/defn show-logout-confirmation
|
||||
(rf/defn show-logout-confirmation
|
||||
{:events [:multiaccounts.logout.ui/logout-pressed]}
|
||||
[_]
|
||||
{:ui/show-confirmation
|
||||
@@ -49,10 +51,10 @@
|
||||
:on-accept #(re-frame/dispatch [:multiaccounts.logout.ui/logout-confirmed])
|
||||
:on-cancel nil}})
|
||||
|
||||
(fx/defn biometric-logout
|
||||
(rf/defn biometric-logout
|
||||
{:events [:biometric-logout]}
|
||||
[cofx]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(logout-method {:auth-method keychain/auth-method-biometric-prepare
|
||||
:logout? false})
|
||||
(fn [{:keys [db]}]
|
||||
|
||||
@@ -1,49 +1,52 @@
|
||||
(ns status-im.multiaccounts.recover.core
|
||||
(:require [clojure.string :as string]
|
||||
[re-frame.core :as re-frame]
|
||||
[status-im.bottom-sheet.core :as bottom-sheet]
|
||||
[status-im.constants :as constants]
|
||||
[status-im.ethereum.core :as ethereum]
|
||||
[status-im.ethereum.mnemonic :as mnemonic]
|
||||
[status-im.keycard.nfc :as nfc]
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
[status-im.keycard.nfc :as nfc]
|
||||
[status-im.multiaccounts.core :as multiaccounts]
|
||||
[status-im.multiaccounts.create.core :as multiaccounts.create]
|
||||
[status-im.native-module.core :as status]
|
||||
[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.utils :as utils]
|
||||
[status-im.bottom-sheet.core :as bottom-sheet]
|
||||
[taoensso.timbre :as log]))
|
||||
[status-im2.navigation.events :as navigation]
|
||||
[taoensso.timbre :as log]
|
||||
[utils.re-frame :as rf]
|
||||
[utils.security.core :as security]))
|
||||
|
||||
(defn existing-account?
|
||||
[multiaccounts key-uid]
|
||||
{:pre [(not (nil? 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))
|
||||
|
||||
(fx/defn set-phrase
|
||||
(rf/defn set-phrase
|
||||
{:events [:multiaccounts.recover/passphrase-input-changed]}
|
||||
[{:keys [db]} masked-recovery-phrase]
|
||||
(let [recovery-phrase (security/safe-unmask-data masked-recovery-phrase)]
|
||||
(fx/merge
|
||||
{:db (update db :intro-wizard assoc
|
||||
:passphrase (string/lower-case recovery-phrase)
|
||||
:passphrase-error nil
|
||||
(rf/merge
|
||||
{:db (update db
|
||||
:intro-wizard assoc
|
||||
:passphrase (string/lower-case recovery-phrase)
|
||||
:passphrase-error nil
|
||||
:next-button-disabled? (or (empty? recovery-phrase)
|
||||
(not (mnemonic/valid-length? recovery-phrase))))})))
|
||||
|
||||
(fx/defn validate-phrase-for-warnings
|
||||
(rf/defn validate-phrase-for-warnings
|
||||
[{:keys [db]}]
|
||||
(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))}))
|
||||
|
||||
(fx/defn on-store-multiaccount-success
|
||||
(rf/defn on-store-multiaccount-success
|
||||
{:events [::store-multiaccount-success]
|
||||
:interceptors [(re-frame/inject-cofx :random-guid-generator)
|
||||
(re-frame/inject-cofx ::multiaccounts.create/get-signing-phrase)]}
|
||||
@@ -54,29 +57,32 @@
|
||||
:content (i18n/label :t/multiaccount-exists-title)
|
||||
:on-dismiss #(re-frame/dispatch [:pop-to-root-tab :multiaccounts])}}
|
||||
(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?
|
||||
;; trying to recover multiaccount created with keycard
|
||||
{:db (-> db
|
||||
(update :intro-wizard assoc
|
||||
:processing? false
|
||||
:passphrase-error :recover-keycard-multiaccount-not-supported)
|
||||
(update :intro-wizard dissoc
|
||||
:passphrase-valid?))}
|
||||
{:db (-> db
|
||||
(update :intro-wizard assoc
|
||||
:processing? false
|
||||
:passphrase-error :recover-keycard-multiaccount-not-supported)
|
||||
(update :intro-wizard
|
||||
dissoc
|
||||
:passphrase-valid?))}
|
||||
(let [multiaccount (assoc multiaccount :derived (get-in db [:intro-wizard :derived]))]
|
||||
(multiaccounts.create/on-multiaccount-created cofx
|
||||
multiaccount
|
||||
password
|
||||
{})))))))
|
||||
|
||||
(fx/defn store-multiaccount
|
||||
(rf/defn store-multiaccount
|
||||
{:events [::recover-multiaccount-confirmed]}
|
||||
[{:keys [db]} password]
|
||||
(let [{:keys [root-key]} (:intro-wizard db)
|
||||
(let [{:keys [root-key]} (:intro-wizard db)
|
||||
{:keys [id key-uid]} root-key
|
||||
callback #(re-frame/dispatch [::store-multiaccount-success % password])
|
||||
hashed-password (ethereum/sha3 (security/safe-unmask-data password))]
|
||||
{:db (assoc-in db [:intro-wizard :processing?] true)
|
||||
callback #(re-frame/dispatch [::store-multiaccount-success % password])
|
||||
hashed-password (ethereum/sha3 (security/safe-unmask-data password))]
|
||||
{:db (assoc-in db [:intro-wizard :processing?] true)
|
||||
::multiaccounts.create/store-multiaccount [id key-uid hashed-password callback]}))
|
||||
|
||||
(re-frame/reg-fx
|
||||
@@ -99,17 +105,18 @@
|
||||
(fn [result]
|
||||
(let [derived-data (multiaccounts.create/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
|
||||
public-key
|
||||
(fn [name identicon]
|
||||
(let [derived-data-extended
|
||||
(update derived-data
|
||||
constants/path-whisper-keyword
|
||||
merge {:name name :identicon identicon})]
|
||||
merge
|
||||
{:name name :identicon identicon})]
|
||||
(re-frame/dispatch [success-event root-data derived-data-extended]))))))))))))
|
||||
|
||||
(fx/defn show-existing-multiaccount-alert
|
||||
(rf/defn show-existing-multiaccount-alert
|
||||
[_ key-uid]
|
||||
{:utils/show-confirmation
|
||||
{:title (i18n/label :t/multiaccount-exists-title)
|
||||
@@ -121,24 +128,26 @@
|
||||
[:multiaccounts.login.ui/multiaccount-selected key-uid]))
|
||||
: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]}
|
||||
[{:keys [db] :as cofx} {:keys [key-uid] :as root-data} derived-data]
|
||||
(let [multiaccounts (:multiaccounts/multiaccounts db)]
|
||||
(fx/merge
|
||||
(rf/merge
|
||||
cofx
|
||||
{:db (update db :intro-wizard
|
||||
assoc :root-key root-data
|
||||
:derived derived-data
|
||||
:step :recovery-success)}
|
||||
{:db (update db
|
||||
:intro-wizard
|
||||
assoc
|
||||
:root-key root-data
|
||||
:derived derived-data
|
||||
:step :recovery-success)}
|
||||
(when (existing-account? multiaccounts key-uid)
|
||||
(show-existing-multiaccount-alert key-uid))
|
||||
(navigation/navigate-to-cofx :recover-multiaccount-success nil))))
|
||||
|
||||
(fx/defn enter-phrase-pressed
|
||||
(rf/defn enter-phrase-pressed
|
||||
{:events [::enter-phrase-pressed]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(fx/merge
|
||||
(rf/merge
|
||||
cofx
|
||||
{:db (-> db
|
||||
(assoc :intro-wizard
|
||||
@@ -153,7 +162,7 @@
|
||||
(bottom-sheet/hide-bottom-sheet)
|
||||
(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]}
|
||||
[{:keys [db] :as cofx} phrase-warnings]
|
||||
(let [{:keys [password passphrase]} (:intro-wizard db)]
|
||||
@@ -165,37 +174,41 @@
|
||||
(security/safe-unmask-data password))
|
||||
:success-event ::import-multiaccount-success}}))))
|
||||
|
||||
(fx/defn seed-phrase-next-pressed
|
||||
(rf/defn seed-phrase-next-pressed
|
||||
{:events [:multiaccounts.recover/enter-phrase-next-pressed]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(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]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(let [{:keys [password passphrase]} (:multiaccounts/recover db)]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{::import-multiaccount {:passphrase passphrase
|
||||
:password password
|
||||
:success-event ::import-multiaccount-success}}
|
||||
(popover/hide-popover))))
|
||||
|
||||
(fx/defn dec-step
|
||||
(rf/defn dec-step
|
||||
{:events [:multiaccounts.recover/dec-step]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(let [step (get-in db [:intro-wizard :step])]
|
||||
(if (= step :enter-phrase)
|
||||
{:db (dissoc db :intro-wizard)}
|
||||
{:db (update db :intro-wizard assoc :step
|
||||
{:db (update db
|
||||
:intro-wizard assoc
|
||||
:step
|
||||
(case step
|
||||
:recovery-success :enter-phrase
|
||||
:recovery-success :enter-phrase
|
||||
:select-key-storage :recovery-success
|
||||
:create-code :select-key-storage)
|
||||
:create-code :select-key-storage)
|
||||
:confirm-failure? false
|
||||
:key-code nil)})))
|
||||
:key-code nil)})))
|
||||
|
||||
(fx/defn cancel-pressed
|
||||
(rf/defn cancel-pressed
|
||||
{:events [:multiaccounts.recover/cancel-pressed]}
|
||||
[{:keys [db] :as cofx} skip-alert?]
|
||||
;; Workaround for multiple Cancel button clicks
|
||||
@@ -207,11 +220,11 @@
|
||||
(i18n/label :t/are-you-sure-to-cancel)
|
||||
(i18n/label :t/you-will-start-from-scratch)
|
||||
#(re-frame/dispatch [:multiaccounts.recover/cancel-pressed true]))
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
dec-step
|
||||
navigation/navigate-back)))))
|
||||
|
||||
(fx/defn select-storage-next-pressed
|
||||
(rf/defn select-storage-next-pressed
|
||||
{:events [:multiaccounts.recover/select-storage-next-pressed]
|
||||
:interceptors [(re-frame/inject-cofx :random-guid-generator)]}
|
||||
[{:keys [db] :as cofx}]
|
||||
@@ -219,51 +232,55 @@
|
||||
(if (= storage-type :advanced)
|
||||
;;TODO: fix circular dependency to remove dispatch here
|
||||
{:dispatch [:recovery.ui/keycard-option-pressed]}
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{:db (update db :intro-wizard assoc :step :create-code)}
|
||||
(navigation/navigate-to-cofx :create-password nil)))))
|
||||
|
||||
(fx/defn re-encrypt-pressed
|
||||
(rf/defn re-encrypt-pressed
|
||||
{:events [:multiaccounts.recover/re-encrypt-pressed]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(fx/merge cofx
|
||||
{:db (update db :intro-wizard
|
||||
assoc :step :select-key-storage
|
||||
(rf/merge cofx
|
||||
{:db (update db
|
||||
:intro-wizard
|
||||
assoc
|
||||
:step :select-key-storage
|
||||
:selected-storage-type :default)}
|
||||
(if (nfc/nfc-supported?)
|
||||
(navigation/navigate-to-cofx :select-key-storage nil)
|
||||
(select-storage-next-pressed))))
|
||||
|
||||
(fx/defn confirm-password-next-button-pressed
|
||||
{:events [:multiaccounts.recover/enter-password-next-pressed]
|
||||
(rf/defn confirm-password-next-button-pressed
|
||||
{:events [:multiaccounts.recover/enter-password-next-pressed]
|
||||
:interceptors [(re-frame/inject-cofx :random-guid-generator)]}
|
||||
[cofx key-code]
|
||||
(store-multiaccount cofx key-code))
|
||||
|
||||
(fx/defn count-words
|
||||
(rf/defn count-words
|
||||
[{:keys [db]}]
|
||||
(let [passphrase (get-in db [:intro-wizard :passphrase])]
|
||||
{:db (assoc-in db [:intro-wizard :passphrase-word-count]
|
||||
(mnemonic/words-count passphrase))}))
|
||||
{:db (assoc-in db
|
||||
[:intro-wizard :passphrase-word-count]
|
||||
(mnemonic/words-count passphrase))}))
|
||||
|
||||
(fx/defn run-validation
|
||||
(rf/defn run-validation
|
||||
[{:keys [db] :as cofx}]
|
||||
(let [passphrase (get-in db [:intro-wizard :passphrase])]
|
||||
(when (= (last passphrase) " ")
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(validate-phrase-for-warnings)))))
|
||||
|
||||
(fx/defn enter-phrase-input-changed
|
||||
(rf/defn enter-phrase-input-changed
|
||||
{:events [:multiaccounts.recover/enter-phrase-input-changed]}
|
||||
[cofx input]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(set-phrase input)
|
||||
(count-words)
|
||||
(run-validation)))
|
||||
|
||||
(fx/defn enter-passphrase-input-changed
|
||||
(rf/defn enter-passphrase-input-changed
|
||||
{:events [:multiaccounts.recover/enter-passphrase-input-changed]}
|
||||
[{:keys [db]} masked-passphrase]
|
||||
{:db (update db :intro-wizard assoc
|
||||
:password masked-passphrase
|
||||
{:db (update db
|
||||
:intro-wizard assoc
|
||||
:password masked-passphrase
|
||||
:password-error nil)})
|
||||
|
||||
@@ -1,44 +1,44 @@
|
||||
(ns status-im.multiaccounts.reset-password.core
|
||||
(:require [re-frame.core :as re-frame]
|
||||
[status-im.utils.fx :as fx]
|
||||
[status-im.utils.types :as types]
|
||||
[clojure.string :as string]
|
||||
[utils.security.core :as security]
|
||||
[status-im.utils.keychain.core :as keychain]
|
||||
[status-im.popover.core :as popover]
|
||||
(:require [clojure.string :as string]
|
||||
[re-frame.core :as re-frame]
|
||||
[status-im.ethereum.core :as ethereum]
|
||||
[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]}
|
||||
[{:keys [db]} input-id value]
|
||||
(let [new-password (get-in db [:multiaccount/reset-password-form-vals :new-password])
|
||||
error (when (and (= input-id :confirm-new-password)
|
||||
(pos? (count new-password))
|
||||
(pos? (count value))
|
||||
(not= value new-password))
|
||||
:t/password-mismatch)]
|
||||
error (when (and (= input-id :confirm-new-password)
|
||||
(pos? (count new-password))
|
||||
(pos? (count value))
|
||||
(not= value new-password))
|
||||
:t/password-mismatch)]
|
||||
{:db (-> db
|
||||
(assoc-in [:multiaccount/reset-password-form-vals input-id] value)
|
||||
(assoc-in [:multiaccount/reset-password-errors input-id] error))}))
|
||||
|
||||
(fx/defn clear-form-vals
|
||||
(rf/defn clear-form-vals
|
||||
{:events [::clear-form-vals]}
|
||||
[{:keys [db]}]
|
||||
{: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]}
|
||||
[{:keys [db]} 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]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(let [{:keys [key-uid]} (:multiaccount db)
|
||||
auth-method (get db :auth-method keychain/auth-method-none)
|
||||
new-password (get-in db [:multiaccount/reset-password-form-vals :new-password])]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{:db (dissoc
|
||||
db
|
||||
:multiaccount/reset-password-form-vals
|
||||
@@ -49,7 +49,8 @@
|
||||
(when (= auth-method keychain/auth-method-biometric)
|
||||
(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)]
|
||||
(if (not (string/blank? error))
|
||||
(re-frame/dispatch [::password-reset-error error])
|
||||
@@ -64,18 +65,20 @@
|
||||
(ethereum/sha3 (security/safe-unmask-data new-password))
|
||||
change-db-password-cb)))
|
||||
|
||||
(fx/defn handle-verification-success
|
||||
(rf/defn handle-verification-success
|
||||
{:events [::handle-verification-success]}
|
||||
[{:keys [db] :as cofx} form-vals]
|
||||
(let [{:keys [key-uid name]} (:multiaccount db)]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{::change-db-password [key-uid form-vals]
|
||||
:db (assoc db
|
||||
:multiaccount/resetting-password? true)}
|
||||
:db (assoc db
|
||||
:multiaccount/resetting-password?
|
||||
true)}
|
||||
(popover/show-popover {:view :password-reset-popover
|
||||
:prevent-closing? true}))))
|
||||
|
||||
(defn handle-verification [form-vals result]
|
||||
(defn handle-verification
|
||||
[form-vals result]
|
||||
(let [{:keys [error]} (types/json->clj result)]
|
||||
(if (not (string/blank? error))
|
||||
(re-frame/dispatch [::handle-verification-error :t/wrong-password])
|
||||
@@ -83,12 +86,13 @@
|
||||
|
||||
(re-frame/reg-fx
|
||||
::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))]
|
||||
(status/verify address hashed-pass
|
||||
(status/verify address
|
||||
hashed-pass
|
||||
(partial handle-verification form-vals)))))
|
||||
|
||||
(fx/defn reset
|
||||
(rf/defn reset
|
||||
{:events [::reset]}
|
||||
[{:keys [db]} form-vals]
|
||||
{::validate-current-password-and-reset
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
(ns status-im.multiaccounts.update.core
|
||||
(:require [status-im.constants :as constants]
|
||||
[status-im.ethereum.json-rpc :as json-rpc]
|
||||
[status-im.utils.fx :as fx]
|
||||
[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}]
|
||||
(let [multiaccount (:multiaccount db)
|
||||
(rf/defn send-multiaccount-update
|
||||
[{:keys [db] :as cofx}]
|
||||
(let [multiaccount (:multiaccount db)
|
||||
{:keys [name preferred-name address]} multiaccount]
|
||||
{::json-rpc/call [{:method "wakuext_sendContactUpdates"
|
||||
:params [(or preferred-name name) ""]
|
||||
{::json-rpc/call [{:method "wakuext_sendContactUpdates"
|
||||
:params [(or preferred-name name) ""]
|
||||
: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.
|
||||
Optionally, one can specify a success-event to be dispatched after fields are persisted."
|
||||
[{:keys [db] :as cofx}
|
||||
@@ -22,20 +23,22 @@
|
||||
(if (empty? current-multiaccount)
|
||||
;; 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
|
||||
(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"))
|
||||
(fx/merge cofx
|
||||
{:db (if setting-value
|
||||
(assoc-in db [:multiaccount setting] setting-value)
|
||||
(update db :multiaccount dissoc setting))
|
||||
(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"))
|
||||
(rf/merge cofx
|
||||
{:db (if setting-value
|
||||
(assoc-in db [:multiaccount setting] setting-value)
|
||||
(update db :multiaccount dissoc setting))
|
||||
::json-rpc/call
|
||||
[{:method "settings_saveSetting"
|
||||
:params [setting setting-value]
|
||||
[{:method "settings_saveSetting"
|
||||
:params [setting setting-value]
|
||||
:on-success on-success}]}
|
||||
(when (and (not dont-sync?)
|
||||
(#{:name :prefered-name} setting))
|
||||
(send-multiaccount-update))))))
|
||||
|
||||
(fx/defn clean-seed-phrase
|
||||
(rf/defn clean-seed-phrase
|
||||
"A helper function that removes seed phrase from storage."
|
||||
[cofx on-success]
|
||||
(multiaccount-update cofx :mnemonic nil on-success))
|
||||
@@ -44,38 +47,47 @@
|
||||
"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."
|
||||
[synced-stickers stickers-from-db]
|
||||
(mapv #(assoc % :url (->> (get stickers-from-db (or (:packID %) (:pack %)))
|
||||
(:stickers)
|
||||
(filter (fn [sticker-db] (= (:hash sticker-db) (:hash %))))
|
||||
(first)
|
||||
(:url)))
|
||||
(mapv #(assoc %
|
||||
:url
|
||||
(->> (get stickers-from-db (or (:packID %) (:pack %)))
|
||||
(:stickers)
|
||||
(filter (fn [sticker-db] (= (:hash sticker-db) (:hash %))))
|
||||
(first)
|
||||
(:url)))
|
||||
synced-stickers))
|
||||
|
||||
(fx/defn optimistic
|
||||
(rf/defn optimistic
|
||||
[{:keys [db] :as cofx} setting setting-value]
|
||||
(let [current-multiaccount (:multiaccount db)
|
||||
setting-value (if (= :currency setting)
|
||||
(keyword setting-value)
|
||||
setting-value)
|
||||
db (case setting
|
||||
:stickers/packs-pending
|
||||
(let [packs-pending (keys (js->clj setting-value))]
|
||||
(update db :stickers/packs-pending conj packs-pending))
|
||||
:stickers/packs-installed
|
||||
(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))
|
||||
: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)]
|
||||
setting-value (if (= :currency setting)
|
||||
(keyword setting-value)
|
||||
setting-value)
|
||||
db (case setting
|
||||
:stickers/packs-pending
|
||||
(let [packs-pending (keys (js->clj setting-value))]
|
||||
(update db :stickers/packs-pending conj packs-pending))
|
||||
:stickers/packs-installed
|
||||
(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))
|
||||
: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
|
||||
(assoc-in db [:multiaccount setting] setting-value)
|
||||
(update db :multiaccount dissoc setting))}))
|
||||
|
||||
(fx/defn set-many-js
|
||||
(rf/defn set-many-js
|
||||
[cofx settings-js]
|
||||
(apply fx/merge
|
||||
(apply rf/merge
|
||||
cofx
|
||||
(map
|
||||
#(optimistic
|
||||
@@ -83,15 +95,15 @@
|
||||
(.-value %))
|
||||
settings-js)))
|
||||
|
||||
(fx/defn toggle-backup-enabled
|
||||
(rf/defn toggle-backup-enabled
|
||||
{:events [:multiaccounts.ui/switch-backup-enabled]}
|
||||
[cofx enabled?]
|
||||
(multiaccount-update cofx :backup-enabled? enabled? {}))
|
||||
|
||||
(fx/defn toggle-opensea-nfts-visibility
|
||||
(rf/defn toggle-opensea-nfts-visibility
|
||||
{:events [::toggle-opensea-nfts-visiblity]}
|
||||
[cofx visible?]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{:db (assoc-in (:db cofx) [:multiaccount :opensea-enabled?] visible?)
|
||||
;; need to add fully qualified namespace to counter circular deps
|
||||
:dispatch [:status-im.wallet.core/fetch-collectibles-collection]}
|
||||
|
||||
+230
-203
@@ -1,31 +1,31 @@
|
||||
(ns status-im.navigation.core
|
||||
(:require
|
||||
["react-native" :as rn]
|
||||
["react-native-gesture-handler" :refer (gestureHandlerRootHOC)]
|
||||
["react-native-navigation" :refer (Navigation)]
|
||||
[clojure.set :as clojure.set]
|
||||
[quo.components.text-input :as quo.text-input]
|
||||
[quo.design-system.colors :as quo.colors]
|
||||
[re-frame.core :as re-frame]
|
||||
[status-im.multiaccounts.login.core :as login-core]
|
||||
[status-im.navigation.roots :as roots]
|
||||
[status-im.navigation.state :as state]
|
||||
[status-im.ui.components.icons.icons :as icons]
|
||||
[status-im.ui.components.react :as react]
|
||||
[status-im.ui.screens.views :as views]
|
||||
[status-im.utils.fx :as fx]
|
||||
[status-im.utils.platform :as platform]
|
||||
[taoensso.timbre :as log]))
|
||||
(:require ["react-native" :as rn]
|
||||
["react-native-gesture-handler" :refer (gestureHandlerRootHOC)]
|
||||
["react-native-navigation" :refer (Navigation)]
|
||||
[clojure.set :as clojure.set]
|
||||
[quo.components.text-input :as quo.text-input]
|
||||
[quo.design-system.colors :as quo.colors]
|
||||
[re-frame.core :as re-frame]
|
||||
[status-im.multiaccounts.login.core :as login-core]
|
||||
[status-im.navigation.roots :as roots]
|
||||
[status-im.navigation.state :as state]
|
||||
[status-im.ui.components.icons.icons :as icons]
|
||||
[status-im.ui.components.react :as react]
|
||||
[status-im.ui.screens.views :as views]
|
||||
[status-im.utils.platform :as platform]
|
||||
[taoensso.timbre :as log]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(def debug? ^boolean js/goog.DEBUG)
|
||||
|
||||
(def splash-screen (-> rn .-NativeModules .-SplashScreen))
|
||||
|
||||
(defonce set-navigation-default-options
|
||||
(.setDefaultOptions Navigation (clj->js {:layout {:orientation "portrait"}})))
|
||||
(.setDefaultOptions Navigation (clj->js {:layout {:orientation "portrait"}})))
|
||||
|
||||
;; REGISTER COMPONENT (LAZY)
|
||||
(defn reg-comp [key]
|
||||
(defn reg-comp
|
||||
[key]
|
||||
(log/debug "reg-comp" key)
|
||||
(if-let [comp (get views/components (keyword key))]
|
||||
(.registerComponent Navigation key (fn [] (views/component comp)))
|
||||
@@ -33,9 +33,10 @@
|
||||
(.registerComponent Navigation key (fn [] (gestureHandlerRootHOC screen)) (fn [] screen)))))
|
||||
|
||||
(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")
|
||||
(when @state/curr-modal
|
||||
(reset! state/curr-modal false)
|
||||
@@ -45,21 +46,24 @@
|
||||
(reset! state/modals [])))
|
||||
|
||||
;; PUSH SCREEN
|
||||
(defn navigate [comp]
|
||||
(defn navigate
|
||||
[comp]
|
||||
(log/debug "NAVIGATE" comp)
|
||||
(let [{:keys [options]} (get views/screens comp)]
|
||||
(.push Navigation
|
||||
(name @state/root-comp-id)
|
||||
(clj->js {:component {:id comp
|
||||
:name comp
|
||||
:options (merge options
|
||||
(roots/status-bar-options)
|
||||
(roots/merge-top-bar (roots/topbar-options) options))}}))
|
||||
(clj->js
|
||||
{:component {:id comp
|
||||
:name comp
|
||||
:options (merge 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
|
||||
(dismiss-all-modals)))
|
||||
|
||||
;; OPEN MODAL
|
||||
(defn update-modal-topbar-options [options]
|
||||
(defn update-modal-topbar-options
|
||||
[options]
|
||||
(log/debug "update-modal-topbar-options" options)
|
||||
(merge options
|
||||
(roots/merge-top-bar {:elevation 0
|
||||
@@ -71,7 +75,8 @@
|
||||
:icon (icons/icon-source :main-icons/close)}}
|
||||
options)))
|
||||
|
||||
(defn open-modal [comp]
|
||||
(defn open-modal
|
||||
[comp]
|
||||
(log/debug "open-modal" comp)
|
||||
(let [{:keys [options]} (get views/screens comp)]
|
||||
(if @state/dissmissing
|
||||
@@ -92,25 +97,27 @@
|
||||
(re-frame/reg-fx :open-modal-fx open-modal)
|
||||
|
||||
;; DISSMISS MODAL
|
||||
(defn dissmissModal []
|
||||
(defn dissmissModal
|
||||
[]
|
||||
(log/debug "dissmissModal")
|
||||
(reset! state/dissmissing true)
|
||||
(.dismissModal Navigation (name (last @state/modals))))
|
||||
|
||||
(defonce register-nav-button-reg
|
||||
(.registerNavigationButtonPressedListener
|
||||
(.events Navigation)
|
||||
(fn [^js evn]
|
||||
(let [id (.-buttonId evn)]
|
||||
(if (= "dismiss-modal" id)
|
||||
(do
|
||||
(when-let [event (get-in views/screens [(last @state/modals) :on-dissmiss])]
|
||||
(re-frame/dispatch event))
|
||||
(dissmissModal))
|
||||
(when-let [handler (get-in views/screens [(keyword id) :right-handler])]
|
||||
(handler)))))))
|
||||
(.registerNavigationButtonPressedListener
|
||||
(.events Navigation)
|
||||
(fn [^js evn]
|
||||
(let [id (.-buttonId evn)]
|
||||
(if (= "dismiss-modal" id)
|
||||
(do
|
||||
(when-let [event (get-in views/screens [(last @state/modals) :on-dissmiss])]
|
||||
(re-frame/dispatch event))
|
||||
(dissmissModal))
|
||||
(when-let [handler (get-in views/screens [(keyword id) :right-handler])]
|
||||
(handler)))))))
|
||||
|
||||
(defn set-view-id [view-id]
|
||||
(defn set-view-id
|
||||
[view-id]
|
||||
(log/debug "set-view-id" view-id)
|
||||
(when-let [{:keys [on-focus]} (get views/screens view-id)]
|
||||
(re-frame/dispatch [:set-view-id view-id])
|
||||
@@ -119,51 +126,53 @@
|
||||
(re-frame/dispatch on-focus))))
|
||||
|
||||
(defonce register-modal-reg
|
||||
(.registerModalDismissedListener
|
||||
(.events Navigation)
|
||||
(fn [_]
|
||||
(if (> (count @state/modals) 1)
|
||||
(let [new-modals (butlast @state/modals)]
|
||||
(reset! state/modals (vec new-modals))
|
||||
(set-view-id (last new-modals)))
|
||||
(do
|
||||
(reset! state/modals [])
|
||||
(reset! state/curr-modal false)
|
||||
(set-view-id @state/pushed-screen-id)))
|
||||
(.registerModalDismissedListener
|
||||
(.events Navigation)
|
||||
(fn [_]
|
||||
(if (> (count @state/modals) 1)
|
||||
(let [new-modals (butlast @state/modals)]
|
||||
(reset! state/modals (vec new-modals))
|
||||
(set-view-id (last new-modals)))
|
||||
(do
|
||||
(reset! state/modals [])
|
||||
(reset! state/curr-modal false)
|
||||
(set-view-id @state/pushed-screen-id)))
|
||||
|
||||
(let [comp @state/dissmissing]
|
||||
(reset! state/dissmissing false)
|
||||
(when (keyword? comp)
|
||||
(open-modal comp))))))
|
||||
(let [comp @state/dissmissing]
|
||||
(reset! state/dissmissing false)
|
||||
(when (keyword? comp)
|
||||
(open-modal comp))))))
|
||||
|
||||
;; SCREEN DID APPEAR
|
||||
(defonce screen-appear-reg
|
||||
(.registerComponentDidAppearListener
|
||||
(.events Navigation)
|
||||
(fn [^js evn]
|
||||
(let [view-id (keyword (.-componentName evn))]
|
||||
(log/debug "screen-appear-reg" view-id)
|
||||
(when (get views/screens view-id)
|
||||
(when (and (not= view-id :bottom-sheet)
|
||||
(not= view-id :popover)
|
||||
(not= view-id :visibility-status-popover))
|
||||
(set-view-id view-id)
|
||||
(when-not @state/curr-modal
|
||||
(reset! state/pushed-screen-id view-id))))))))
|
||||
(.registerComponentDidAppearListener
|
||||
(.events Navigation)
|
||||
(fn [^js evn]
|
||||
(let [view-id (keyword (.-componentName evn))]
|
||||
(log/debug "screen-appear-reg" view-id)
|
||||
(when (get views/screens view-id)
|
||||
(when (and (not= view-id :bottom-sheet)
|
||||
(not= view-id :popover)
|
||||
(not= view-id :visibility-status-popover))
|
||||
(set-view-id view-id)
|
||||
(when-not @state/curr-modal
|
||||
(reset! state/pushed-screen-id view-id))))))))
|
||||
|
||||
;; SCREEN DID DISAPPEAR
|
||||
(defonce screen-disappear-reg
|
||||
(.registerComponentDidDisappearListener
|
||||
(.events Navigation)
|
||||
(fn [^js 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"}
|
||||
(.-componentName evn))
|
||||
(re-frame/dispatch [::view-disappeared view-id])
|
||||
(doseq [[_ {:keys [ref value]}] @quo.text-input/text-input-refs]
|
||||
(.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}))))))))
|
||||
(.registerComponentDidDisappearListener
|
||||
(.events Navigation)
|
||||
(fn [^js 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"}
|
||||
(.-componentName evn))
|
||||
(re-frame/dispatch [::view-disappeared view-id])
|
||||
(doseq [[_ {:keys [ref value]}] @quo.text-input/text-input-refs]
|
||||
(.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
|
||||
(re-frame/reg-fx
|
||||
@@ -182,31 +191,34 @@
|
||||
(reset! state/root-id @state/root-comp-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]}
|
||||
[{:keys [db]}]
|
||||
(log/debug :set-multiaccounts-root)
|
||||
(let [key-uid (get-in db [:multiaccounts/login :key-uid])
|
||||
keycard-account? (boolean (get-in db [:multiaccounts/multiaccounts
|
||||
key-uid
|
||||
:keycard-pairing]))]
|
||||
keycard-account? (boolean (get-in db
|
||||
[:multiaccounts/multiaccounts
|
||||
key-uid
|
||||
:keycard-pairing]))]
|
||||
{:init-root-fx (if keycard-account? :multiaccounts-keycard :multiaccounts)}))
|
||||
|
||||
(defonce rset-app-launched
|
||||
(.registerAppLaunchedListener (.events Navigation)
|
||||
(fn []
|
||||
(reset! state/curr-modal false)
|
||||
(reset! state/dissmissing false)
|
||||
(if (or (= @state/root-id :multiaccounts)
|
||||
(= @state/root-id :multiaccounts-keycard))
|
||||
(re-frame/dispatch-sync [::set-multiaccount-root])
|
||||
(when @state/root-id
|
||||
(reset! state/root-comp-id @state/root-id)
|
||||
(.setRoot Navigation (clj->js (get (roots/roots) @state/root-id)))
|
||||
(re-frame/dispatch [::login-core/check-last-chat])))
|
||||
(.hide ^js splash-screen))))
|
||||
(defonce
|
||||
rset-app-launched
|
||||
(.registerAppLaunchedListener (.events Navigation)
|
||||
(fn []
|
||||
(reset! state/curr-modal false)
|
||||
(reset! state/dissmissing false)
|
||||
(if (or (= @state/root-id :multiaccounts)
|
||||
(= @state/root-id :multiaccounts-keycard))
|
||||
(re-frame/dispatch-sync [::set-multiaccount-root])
|
||||
(when @state/root-id
|
||||
(reset! state/root-comp-id @state/root-id)
|
||||
(.setRoot Navigation (clj->js (get (roots/roots) @state/root-id)))
|
||||
(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)
|
||||
(let [{:keys [options]} (get views/screens comp)]
|
||||
{:component {:id comp
|
||||
@@ -227,24 +239,28 @@
|
||||
(get-screen-component comp))))))
|
||||
|
||||
;; BOTTOM TABS
|
||||
(def tab-root-ids {0 :chat-stack
|
||||
1 :browser-stack
|
||||
2 :wallet-stack
|
||||
3 :status-stack
|
||||
4 :profile-stack})
|
||||
(def tab-root-ids
|
||||
{0 :chat-stack
|
||||
1 :browser-stack
|
||||
2 :wallet-stack
|
||||
3 :status-stack
|
||||
4 :profile-stack})
|
||||
|
||||
(def tab-key-idx {:chat 0
|
||||
:browser 1
|
||||
:wallet 2
|
||||
:status 3
|
||||
:profile 4})
|
||||
(def tab-key-idx
|
||||
{:chat 0
|
||||
:browser 1
|
||||
:wallet 2
|
||||
:status 3
|
||||
:profile 4})
|
||||
|
||||
(re-frame/reg-fx
|
||||
:change-tab-fx
|
||||
(fn [tab]
|
||||
(log/debug :change-tab-fx)
|
||||
(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
|
||||
(dismiss-all-modals)))
|
||||
|
||||
@@ -253,21 +269,22 @@
|
||||
:change-tab-count-fx
|
||||
(fn [[tab cnt]]
|
||||
(log/debug :change-tab-count-fx tab cnt)
|
||||
(.mergeOptions Navigation
|
||||
(name (get tab-root-ids (get tab-key-idx tab)))
|
||||
(clj->js {:bottomTab (cond
|
||||
(or (pos? cnt) (pos? (:other cnt)))
|
||||
(if (and (= :chat tab) platform/ios?)
|
||||
{:dotIndicator {:visible true}}
|
||||
{:badge (str (or (:other cnt) cnt)) :dotIndicator {:visible false}})
|
||||
(pos? (:public cnt))
|
||||
(if platform/ios?
|
||||
{:dotIndicator {:visible true}}
|
||||
{:badge nil :dotIndicator {:visible true}})
|
||||
:else
|
||||
(if (and (= :chat tab) platform/ios?)
|
||||
{:dotIndicator {:visible false}}
|
||||
{:dotIndicator {:visible false} :badge ""}))}))))
|
||||
(.mergeOptions
|
||||
Navigation
|
||||
(name (get tab-root-ids (get tab-key-idx tab)))
|
||||
(clj->js {:bottomTab (cond
|
||||
(or (pos? cnt) (pos? (:other cnt)))
|
||||
(if (and (= :chat tab) platform/ios?)
|
||||
{:dotIndicator {:visible true}}
|
||||
{:badge (str (or (:other cnt) cnt)) :dotIndicator {:visible false}})
|
||||
(pos? (:public cnt))
|
||||
(if platform/ios?
|
||||
{:dotIndicator {:visible true}}
|
||||
{:badge nil :dotIndicator {:visible true}})
|
||||
:else
|
||||
(if (and (= :chat tab) platform/ios?)
|
||||
{:dotIndicator {:visible false}}
|
||||
{:dotIndicator {:visible false} :badge ""}))}))))
|
||||
|
||||
(re-frame/reg-fx
|
||||
:pop-to-root-tab-fx
|
||||
@@ -277,54 +294,57 @@
|
||||
(.popToRoot Navigation (name comp))))
|
||||
|
||||
(defonce register-bottom-tab-reg
|
||||
(.registerBottomTabSelectedListener
|
||||
(.events Navigation)
|
||||
(fn [^js evn]
|
||||
(let [selected-tab-index (.-selectedTabIndex evn)
|
||||
comp (get tab-root-ids selected-tab-index)
|
||||
tab-key (get (clojure.set/map-invert tab-key-idx) selected-tab-index)]
|
||||
(re-frame/dispatch [:set :current-tab tab-key])
|
||||
(when (= @state/root-comp-id comp)
|
||||
(when (= :chat tab-key)
|
||||
(re-frame/dispatch [:close-chat]))
|
||||
(when platform/android?
|
||||
(.popToRoot Navigation (name comp))))
|
||||
(reset! state/root-comp-id comp)))))
|
||||
(.registerBottomTabSelectedListener
|
||||
(.events Navigation)
|
||||
(fn [^js evn]
|
||||
(let [selected-tab-index (.-selectedTabIndex evn)
|
||||
comp (get tab-root-ids selected-tab-index)
|
||||
tab-key (get (clojure.set/map-invert tab-key-idx) selected-tab-index)]
|
||||
(re-frame/dispatch [:set :current-tab tab-key])
|
||||
(when (= @state/root-comp-id comp)
|
||||
(when (= :chat tab-key)
|
||||
(re-frame/dispatch [:close-chat]))
|
||||
(when platform/android?
|
||||
(.popToRoot Navigation (name comp))))
|
||||
(reset! state/root-comp-id comp)))))
|
||||
|
||||
;; OVERLAY (Popover and bottom sheets)
|
||||
(defn dissmiss-overlay [comp]
|
||||
(defn dissmiss-overlay
|
||||
[comp]
|
||||
(.catch (.dismissOverlay Navigation comp) #()))
|
||||
|
||||
(defn show-overlay [comp]
|
||||
(defn show-overlay
|
||||
[comp]
|
||||
(dissmiss-overlay comp)
|
||||
(.showOverlay Navigation
|
||||
(clj->js
|
||||
{:component {:name comp
|
||||
:id comp
|
||||
:options (merge (cond-> (roots/status-bar-options)
|
||||
(and platform/android? (not (quo.colors/dark?)))
|
||||
(assoc-in [:statusBar :backgroundColor] "#99999A"))
|
||||
{:layout {:componentBackgroundColor (if platform/android?
|
||||
(:backdrop @quo.colors/theme)
|
||||
"transparent")}
|
||||
:overlay {:interceptTouchOutside true}})}})))
|
||||
(.showOverlay
|
||||
Navigation
|
||||
(clj->js
|
||||
{:component {:name comp
|
||||
:id comp
|
||||
:options (merge (cond-> (roots/status-bar-options)
|
||||
(and platform/android? (not (quo.colors/dark?)))
|
||||
(assoc-in [:statusBar :backgroundColor] "#99999A"))
|
||||
{:layout {:componentBackgroundColor (if platform/android?
|
||||
(:backdrop @quo.colors/theme)
|
||||
"transparent")}
|
||||
:overlay {:interceptTouchOutside true}})}})))
|
||||
|
||||
;; POPOVER
|
||||
(defonce popover-reg
|
||||
(.registerComponent Navigation
|
||||
"popover"
|
||||
(fn [] (gestureHandlerRootHOC views/popover-comp))
|
||||
(fn [] views/popover-comp)))
|
||||
(.registerComponent Navigation
|
||||
"popover"
|
||||
(fn [] (gestureHandlerRootHOC views/popover-comp))
|
||||
(fn [] views/popover-comp)))
|
||||
|
||||
(re-frame/reg-fx :show-popover (fn [] (show-overlay "popover")))
|
||||
(re-frame/reg-fx :hide-popover (fn [] (dissmiss-overlay "popover")))
|
||||
|
||||
;; VISIBILITY STATUS POPOVER
|
||||
(defonce visibility-status-popover-reg
|
||||
(.registerComponent Navigation
|
||||
"visibility-status-popover"
|
||||
(fn [] (gestureHandlerRootHOC views/visibility-status-popover-comp))
|
||||
(fn [] views/visibility-status-popover-comp)))
|
||||
(.registerComponent Navigation
|
||||
"visibility-status-popover"
|
||||
(fn [] (gestureHandlerRootHOC views/visibility-status-popover-comp))
|
||||
(fn [] views/visibility-status-popover-comp)))
|
||||
|
||||
(re-frame/reg-fx :show-visibility-status-popover
|
||||
(fn [] (show-overlay "visibility-status-popover")))
|
||||
@@ -333,10 +353,10 @@
|
||||
|
||||
;; BOTTOM SHEETS
|
||||
(defonce bottom-sheet-reg
|
||||
(.registerComponent Navigation
|
||||
"bottom-sheet"
|
||||
(fn [] (gestureHandlerRootHOC views/sheet-comp))
|
||||
(fn [] views/sheet-comp)))
|
||||
(.registerComponent Navigation
|
||||
"bottom-sheet"
|
||||
(fn [] (gestureHandlerRootHOC views/sheet-comp))
|
||||
(fn [] views/sheet-comp)))
|
||||
|
||||
(re-frame/reg-fx :show-bottom-sheet (fn [] (show-overlay "bottom-sheet")))
|
||||
(re-frame/reg-fx :hide-bottom-sheet (fn [] (dissmiss-overlay "bottom-sheet")))
|
||||
@@ -344,37 +364,41 @@
|
||||
;; WALLET CONNECT
|
||||
|
||||
(defonce wallet-connect-sheet-reg
|
||||
(.registerComponent Navigation
|
||||
"wallet-connect-sheet"
|
||||
(fn [] (gestureHandlerRootHOC views/wallet-connect-comp))
|
||||
(fn [] views/wallet-connect-comp)))
|
||||
(.registerComponent Navigation
|
||||
"wallet-connect-sheet"
|
||||
(fn [] (gestureHandlerRootHOC views/wallet-connect-comp))
|
||||
(fn [] views/wallet-connect-comp)))
|
||||
|
||||
(defonce wallet-connect-success-sheet-reg
|
||||
(.registerComponent Navigation
|
||||
"wallet-connect-success-sheet"
|
||||
(fn [] (gestureHandlerRootHOC views/wallet-connect-success-comp))
|
||||
(fn [] views/wallet-connect-success-comp)))
|
||||
(.registerComponent Navigation
|
||||
"wallet-connect-success-sheet"
|
||||
(fn [] (gestureHandlerRootHOC views/wallet-connect-success-comp))
|
||||
(fn [] views/wallet-connect-success-comp)))
|
||||
|
||||
(defonce wallet-connect-app-management-sheet-reg
|
||||
(.registerComponent Navigation
|
||||
"wallet-connect-app-management-sheet"
|
||||
(fn [] (gestureHandlerRootHOC views/wallet-connect-app-management-comp))
|
||||
(fn [] views/wallet-connect-app-management-comp)))
|
||||
(.registerComponent Navigation
|
||||
"wallet-connect-app-management-sheet"
|
||||
(fn [] (gestureHandlerRootHOC 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 :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 :hide-wallet-connect-success-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")))
|
||||
(re-frame/reg-fx :show-wallet-connect-success-sheet
|
||||
(fn [] (show-overlay "wallet-connect-success-sheet")))
|
||||
(re-frame/reg-fx :hide-wallet-connect-success-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
|
||||
|
||||
(defonce signing-sheet-reg
|
||||
(.registerComponent Navigation
|
||||
"signing-sheet"
|
||||
(fn [] (gestureHandlerRootHOC views/signing-comp))
|
||||
(fn [] views/signing-comp)))
|
||||
(.registerComponent Navigation
|
||||
"signing-sheet"
|
||||
(fn [] (gestureHandlerRootHOC views/signing-comp))
|
||||
(fn [] views/signing-comp)))
|
||||
|
||||
(re-frame/reg-fx :show-signing-sheet (fn [] (show-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 ?
|
||||
|
||||
(defonce select-acc-sheet-reg
|
||||
(.registerComponent Navigation
|
||||
"select-acc-sheet"
|
||||
(fn [] (gestureHandlerRootHOC views/select-acc-comp))
|
||||
(fn [] views/select-acc-comp)))
|
||||
(.registerComponent Navigation
|
||||
"select-acc-sheet"
|
||||
(fn [] (gestureHandlerRootHOC 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 :hide-select-acc-sheet (fn [] (dissmiss-overlay "select-acc-sheet")))
|
||||
@@ -414,28 +438,31 @@
|
||||
(.pop Navigation (name @state/root-comp-id))
|
||||
(navigate view-id)))
|
||||
|
||||
(def community-screens '(:community-management
|
||||
:community-members
|
||||
:community-requests-to-join
|
||||
:create-community-channel
|
||||
:community-emoji-thumbnail-picker
|
||||
:create-community-category
|
||||
:community-edit-chats
|
||||
:community-edit
|
||||
:community-reorder-categories))
|
||||
(def community-screens
|
||||
'(:community-management
|
||||
:community-members
|
||||
:community-requests-to-join
|
||||
:create-community-channel
|
||||
:community-emoji-thumbnail-picker
|
||||
:create-community-category
|
||||
:community-edit-chats
|
||||
:community-edit
|
||||
:community-reorder-categories))
|
||||
|
||||
;; change view-id if it is still same after component is disappeared
|
||||
;; https://github.com/wix/react-native-navigation/issues/5744#issuecomment-563226820
|
||||
(fx/defn view-disappeared
|
||||
(rf/defn view-disappeared
|
||||
{:events [::view-disappeared]}
|
||||
[{:keys [db]} view-id]
|
||||
(when (= view-id (:view-id db))
|
||||
{:db (assoc db :view-id (cond
|
||||
(= view-id :community-emoji-thumbnail-picker)
|
||||
:create-community-channel
|
||||
{:db (assoc db
|
||||
:view-id
|
||||
(cond
|
||||
(= view-id :community-emoji-thumbnail-picker)
|
||||
:create-community-channel
|
||||
|
||||
(some #(= view-id %) community-screens)
|
||||
:community
|
||||
(some #(= view-id %) community-screens)
|
||||
:community
|
||||
|
||||
:else
|
||||
:home))}))
|
||||
:else
|
||||
:home))}))
|
||||
|
||||
@@ -5,37 +5,42 @@
|
||||
[status-im.ethereum.json-rpc :as json-rpc]
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
[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.types :as types]))
|
||||
[status-im.utils.types :as types]
|
||||
[status-im2.navigation.events :as navigation]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(def url-regex
|
||||
#"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))))
|
||||
|
||||
(def default-manage
|
||||
{:name {:value ""}
|
||||
:url {:value ""}
|
||||
{:name {:value ""}
|
||||
:url {:value ""}
|
||||
:symbol {:value ""}
|
||||
:chain {:value :mainnet}})
|
||||
:chain {:value :mainnet}})
|
||||
|
||||
(defn validate-string [{:keys [value]}]
|
||||
(defn validate-string
|
||||
[{:keys [value]}]
|
||||
{:value value
|
||||
:error (string/blank? value)})
|
||||
|
||||
(defn validate-network-id [{:keys [value]}]
|
||||
(defn validate-network-id
|
||||
[{:keys [value]}]
|
||||
{:value value
|
||||
:error (and (not (string/blank? value))
|
||||
(= (int value) 0))})
|
||||
|
||||
(defn validate-url [{:keys [value]}]
|
||||
(defn validate-url
|
||||
[{:keys [value]}]
|
||||
{:value value
|
||||
:error (not (valid-rpc-url? value))})
|
||||
|
||||
(defn validate-manage [manage]
|
||||
(defn validate-manage
|
||||
[manage]
|
||||
(-> manage
|
||||
(update :url validate-url)
|
||||
(update :name validate-string)
|
||||
@@ -43,20 +48,23 @@
|
||||
(update :chain validate-string)
|
||||
(update :network-id validate-network-id)))
|
||||
|
||||
(defn valid-manage? [manage]
|
||||
(defn valid-manage?
|
||||
[manage]
|
||||
(->> (validate-manage manage)
|
||||
vals
|
||||
(map :error)
|
||||
(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])]
|
||||
(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]))
|
||||
|
||||
(fx/defn set-input
|
||||
(rf/defn set-input
|
||||
{:events [::input-changed]}
|
||||
[{:keys [db]} input-key value]
|
||||
{:db (-> db
|
||||
@@ -64,13 +72,13 @@
|
||||
(update-in [:networks/manage] validate-manage))})
|
||||
|
||||
;; No edit functionality actually implemented
|
||||
(fx/defn edit
|
||||
(rf/defn edit
|
||||
{:events [::add-network-pressed]}
|
||||
[{db :db}]
|
||||
{:db (assoc db :networks/manage (validate-manage default-manage))
|
||||
:dispatch [:navigate-to :edit-network]})
|
||||
|
||||
(fx/defn connect-success
|
||||
(rf/defn connect-success
|
||||
{:events [::connect-success]}
|
||||
[_ network-id]
|
||||
{:ui/show-confirmation
|
||||
@@ -80,14 +88,14 @@
|
||||
:on-accept #(re-frame/dispatch [::save-network-settings-pressed network-id])
|
||||
:on-cancel nil}})
|
||||
|
||||
(fx/defn connect-failure
|
||||
(rf/defn connect-failure
|
||||
{:events [::connect-failure]}
|
||||
[_ reason]
|
||||
{:utils/show-popup
|
||||
{:title (i18n/label :t/error)
|
||||
:content (str reason)}})
|
||||
|
||||
(fx/defn connect
|
||||
(rf/defn connect
|
||||
{:events [::connect-network-pressed]}
|
||||
[{:keys [db] :as cofx} network-id]
|
||||
(if-let [config (get-in db [:networks/networks network-id :config])]
|
||||
@@ -104,18 +112,20 @@
|
||||
(js/parseInt res))]
|
||||
(if (and network-id (= expected-network-id rpc-network-id))
|
||||
(re-frame/dispatch [::connect-success network-id])
|
||||
(re-frame/dispatch [::connect-failure (if (not= expected-network-id rpc-network-id)
|
||||
(i18n/label :t/network-invalid-network-id)
|
||||
(i18n/label :t/network-invalid-url))]))))
|
||||
(re-frame/dispatch [::connect-failure
|
||||
(if (not= expected-network-id rpc-network-id)
|
||||
(i18n/label :t/network-invalid-network-id)
|
||||
(i18n/label :t/network-invalid-url))]))))
|
||||
:on-error (fn [{:keys [response-body 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))]
|
||||
(re-frame/dispatch [::connect-failure reason])))}}
|
||||
(connect-success cofx network-id))
|
||||
(connect-failure cofx "A network with the specified id doesn't exist")))
|
||||
|
||||
(fx/defn delete
|
||||
(rf/defn delete
|
||||
{:events [::delete-network-pressed]}
|
||||
[{:keys [db]} network]
|
||||
(let [current-network? (= (:networks/current-network db) network)]
|
||||
@@ -125,26 +135,27 @@
|
||||
{:ui/show-confirmation {:title (i18n/label :t/delete-network-title)
|
||||
:content (i18n/label :t/delete-network-confirmation)
|
||||
: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}})))
|
||||
|
||||
(fx/defn save-network-settings
|
||||
(rf/defn save-network-settings
|
||||
{:events [::save-network-settings-pressed]}
|
||||
[{:keys [db] :as cofx} network]
|
||||
(fx/merge cofx
|
||||
{:db (assoc db :networks/current-network network)
|
||||
::json-rpc/call [{:method "settings_saveSetting"
|
||||
:params [:networks/current-network network]
|
||||
(rf/merge cofx
|
||||
{:db (assoc db :networks/current-network network)
|
||||
::json-rpc/call [{:method "settings_saveSetting"
|
||||
:params [:networks/current-network network]
|
||||
:on-success #()}]}
|
||||
(node/prepare-new-config {:on-success #(re-frame/dispatch [:logout])})))
|
||||
|
||||
(fx/defn remove-network
|
||||
(rf/defn remove-network
|
||||
{:events [::remove-network-confirmed]}
|
||||
[{:keys [db] :as cofx} network]
|
||||
(let [networks (dissoc (:networks/networks db) network)]
|
||||
{:db (assoc db :networks/networks networks)
|
||||
::json-rpc/call [{:method "settings_saveSetting"
|
||||
:params [:networks/networks (vals networks)]
|
||||
{:db (assoc db :networks/networks networks)
|
||||
::json-rpc/call [{:method "settings_saveSetting"
|
||||
:params [:networks/networks (vals networks)]
|
||||
:on-success #(re-frame/dispatch [:navigate-back])}]}))
|
||||
|
||||
(defn new-network
|
||||
@@ -155,40 +166,41 @@
|
||||
:DataDir data-dir
|
||||
:UpstreamConfig {:Enabled true
|
||||
:URL upstream-url}}]
|
||||
{:id random-id
|
||||
:name network-name
|
||||
:symbol symbol
|
||||
:config config}))
|
||||
{:id random-id
|
||||
:name network-name
|
||||
:symbol symbol
|
||||
:config config}))
|
||||
|
||||
(fx/defn save
|
||||
{:events [::save-network-pressed]
|
||||
(rf/defn save
|
||||
{:events [::save-network-pressed]
|
||||
:interceptors [(re-frame/inject-cofx :random-id-generator)]}
|
||||
[{{: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)
|
||||
;; rename network-id from UI to chain-id
|
||||
(let [{:keys [name url chain network-id symbol]} manage
|
||||
random-id (string/replace (random-id-generator) "-" "")
|
||||
network (new-network random-id
|
||||
(:value name)
|
||||
(:value symbol)
|
||||
(:value url)
|
||||
(:value chain)
|
||||
(:value network-id))
|
||||
custom-chain-type? (= :custom (:value chain))
|
||||
new-networks (assoc networks random-id network)]
|
||||
random-id (string/replace (random-id-generator) "-" "")
|
||||
network (new-network random-id
|
||||
(:value name)
|
||||
(:value symbol)
|
||||
(:value url)
|
||||
(:value chain)
|
||||
(:value network-id))
|
||||
custom-chain-type? (= :custom (:value chain))
|
||||
new-networks (assoc networks random-id network)]
|
||||
(if (or (not custom-chain-type?)
|
||||
(chain-id-available? networks network))
|
||||
{:db (-> db
|
||||
(dissoc :networks/manage)
|
||||
(assoc :networks/networks new-networks))
|
||||
::json-rpc/call [{:method "settings_saveSetting"
|
||||
:params [:networks/networks (vals new-networks)]
|
||||
{:db (-> db
|
||||
(dissoc :networks/manage)
|
||||
(assoc :networks/networks new-networks))
|
||||
::json-rpc/call [{:method "settings_saveSetting"
|
||||
:params [:networks/networks (vals new-networks)]
|
||||
:on-success #(re-frame/dispatch [:navigate-back])}]}
|
||||
{:ui/show-error "chain-id already defined"}))
|
||||
{:ui/show-error "invalid network parameters"}))
|
||||
|
||||
(fx/defn open-network-details
|
||||
(rf/defn open-network-details
|
||||
{:events [::network-entry-pressed]}
|
||||
[cofx network]
|
||||
(navigation/navigate-to-cofx cofx :network-details {:networks/selected-network network}))
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
(ns status-im.network.net-info
|
||||
(:require [re-frame.core :as re-frame]
|
||||
[status-im.native-module.core :as status]
|
||||
(:require ["@react-native-community/netinfo" :default net-info]
|
||||
[re-frame.core :as re-frame]
|
||||
[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]
|
||||
["@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?]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{:db (assoc db :network-status (if is-connected? :online :offline))}
|
||||
(when (and is-connected?
|
||||
(or (not= (count (get-in db [:wallet :accounts]))
|
||||
@@ -17,14 +17,14 @@
|
||||
(wallet/has-empty-balances? db)))
|
||||
(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?]
|
||||
(fx/merge cofx
|
||||
{:db (assoc db :network/type network-type)
|
||||
(rf/merge cofx
|
||||
{:db (assoc db :network/type network-type)
|
||||
:network/notify-status-go [network-type expensive?]}
|
||||
(mobile-network/on-network-status-change)))
|
||||
|
||||
(fx/defn handle-network-info-change
|
||||
(rf/defn handle-network-info-change
|
||||
{:events [::network-info-changed]}
|
||||
[{:keys [db] :as cofx} {:keys [isConnected type details] :as state}]
|
||||
(let [old-network-status (:network-status db)
|
||||
@@ -33,18 +33,19 @@
|
||||
status-changed? (= connectivity-status old-network-status)
|
||||
type-changed? (= type old-network-type)]
|
||||
(log/debug "[net-info]"
|
||||
"old-network-status" old-network-status
|
||||
"old-network-type" old-network-type
|
||||
"old-network-status" old-network-status
|
||||
"old-network-type" old-network-type
|
||||
"connectivity-status" connectivity-status
|
||||
"type" type
|
||||
"details" details)
|
||||
(fx/merge cofx
|
||||
"type" type
|
||||
"details" details)
|
||||
(rf/merge cofx
|
||||
(when-not status-changed?
|
||||
(change-network-status isConnected))
|
||||
(when-not type-changed?
|
||||
(change-network-type old-network-type type (:is-connection-expensive details))))))
|
||||
|
||||
(defn add-net-info-listener []
|
||||
(defn add-net-info-listener
|
||||
[]
|
||||
(when net-info
|
||||
(.addEventListener ^js net-info
|
||||
#(re-frame/dispatch [::network-info-changed
|
||||
|
||||
@@ -3,11 +3,12 @@
|
||||
[status-im.ethereum.json-rpc :as json-rpc]
|
||||
[status-im.native-module.core :as status]
|
||||
[status-im.utils.config :as config]
|
||||
[status-im.utils.fx :as fx]
|
||||
[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 $
|
||||
(get $ network)
|
||||
(vals $)
|
||||
@@ -16,18 +17,19 @@
|
||||
(assoc-in config [:ClusterConfig :BootNodes] bootnodes)
|
||||
config)))
|
||||
|
||||
(defn- add-log-level [config log-level]
|
||||
(defn- add-log-level
|
||||
[config log-level]
|
||||
(if (empty? log-level)
|
||||
(assoc config
|
||||
:MaxPeers 20
|
||||
:MaxPeers 20
|
||||
:MaxPendingPeers 20
|
||||
:LogLevel "ERROR"
|
||||
:LogEnabled false)
|
||||
:LogLevel "ERROR"
|
||||
:LogEnabled false)
|
||||
(assoc config
|
||||
:MaxPeers 20
|
||||
:MaxPeers 20
|
||||
:MaxPendingPeers 20
|
||||
:LogLevel log-level
|
||||
:LogEnabled true)))
|
||||
:LogLevel log-level
|
||||
:LogEnabled true)))
|
||||
|
||||
(defn get-network-genesis-hash-prefix
|
||||
"returns the hex representation of the first 8 bytes of
|
||||
@@ -50,22 +52,25 @@
|
||||
(defn get-topics
|
||||
[network]
|
||||
(let [les-topic (get-les-topic network)]
|
||||
(cond-> {"whisper" {:Min 2, :Max 2}}
|
||||
les-topic (assoc les-topic {:Min 2, :Max 2}))))
|
||||
(cond-> {"whisper" {: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
|
||||
:Name "StatusIM")
|
||||
:Name
|
||||
"StatusIM")
|
||||
config/dev-build?
|
||||
(assoc :ListenAddr ":30304"
|
||||
:DataDir (str (:DataDir config) "_dev"))))
|
||||
:DataDir (str (:DataDir config) "_dev"))))
|
||||
|
||||
(def login-node-config
|
||||
{:WalletConfig (cond-> {:Enabled true}
|
||||
(not= 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))
|
||||
|
||||
(defn- pick-nodes
|
||||
@@ -75,14 +80,17 @@
|
||||
[limit nodes]
|
||||
(take limit (shuffle nodes)))
|
||||
|
||||
(defn fleets [{:keys [custom-fleets]}]
|
||||
(defn fleets
|
||||
[{:keys [custom-fleets]}]
|
||||
(as-> [(js/require "./fleets.js")] $
|
||||
(mapv #(:fleets (types/json->clj %)) $)
|
||||
(conj $ custom-fleets)
|
||||
(reduce merge $)))
|
||||
|
||||
(defn current-fleet-key [db]
|
||||
(keyword (get-in db [:multiaccount :fleet]
|
||||
(defn current-fleet-key
|
||||
[db]
|
||||
(keyword (get-in db
|
||||
[:multiaccount :fleet]
|
||||
config/fleet)))
|
||||
|
||||
(defn get-current-fleet
|
||||
@@ -93,15 +101,16 @@
|
||||
(defn- get-multiaccount-node-config
|
||||
[{:keys [multiaccount :networks/networks :networks/current-network]
|
||||
:as db}]
|
||||
(let [wakuv2-config (get multiaccount :wakuv2-config {})
|
||||
current-fleet-key (current-fleet-key db)
|
||||
current-fleet (get-current-fleet db)
|
||||
wakuv2-enabled (boolean (:waku current-fleet))
|
||||
waku-nodes (:waku-nodes current-fleet)
|
||||
rendezvous-nodes (pick-nodes 3 (vals (:rendezvous current-fleet)))
|
||||
(let [wakuv2-config (get multiaccount :wakuv2-config {})
|
||||
current-fleet-key (current-fleet-key db)
|
||||
current-fleet (get-current-fleet db)
|
||||
wakuv2-enabled (boolean (:waku current-fleet))
|
||||
waku-nodes (:waku-nodes current-fleet)
|
||||
rendezvous-nodes (pick-nodes 3 (vals (:rendezvous current-fleet)))
|
||||
{:keys [installation-id log-level
|
||||
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)]
|
||||
(cond-> (get-in networks [current-network :config])
|
||||
:always
|
||||
@@ -111,10 +120,10 @@
|
||||
(get-login-node-config)
|
||||
|
||||
current-fleet
|
||||
(assoc :NoDiscovery wakuv2-enabled
|
||||
:Rendezvous (if wakuv2-enabled false (boolean (seq rendezvous-nodes)))
|
||||
:ClusterConfig {:Enabled true
|
||||
:Fleet (name current-fleet-key)
|
||||
(assoc :NoDiscovery wakuv2-enabled
|
||||
:Rendezvous (if wakuv2-enabled false (boolean (seq rendezvous-nodes)))
|
||||
:ClusterConfig {:Enabled true
|
||||
:Fleet (name current-fleet-key)
|
||||
:DiscV5BootstrapNodes
|
||||
(if wakuv2-enabled
|
||||
waku-nodes
|
||||
@@ -124,29 +133,30 @@
|
||||
:TrustedMailServers
|
||||
(if wakuv2-enabled [] (pick-nodes 6 (vals (:mail current-fleet))))
|
||||
:StaticNodes
|
||||
(if wakuv2-enabled []
|
||||
(into (pick-nodes 2
|
||||
(vals (:whisper current-fleet)))
|
||||
(vals (:static current-fleet))))
|
||||
:RendezvousNodes (if wakuv2-enabled [] rendezvous-nodes)
|
||||
:WakuNodes (or waku-nodes [])})
|
||||
(if wakuv2-enabled
|
||||
[]
|
||||
(into (pick-nodes 2
|
||||
(vals (:whisper current-fleet)))
|
||||
(vals (:static current-fleet))))
|
||||
:RendezvousNodes (if wakuv2-enabled [] rendezvous-nodes)
|
||||
:WakuNodes (or waku-nodes [])})
|
||||
|
||||
:always
|
||||
(assoc :LocalNotificationsConfig {:Enabled true}
|
||||
:BrowsersConfig {:Enabled true}
|
||||
:PermissionsConfig {:Enabled true}
|
||||
:MailserversConfig {:Enabled true}
|
||||
:EnableNTPSync true
|
||||
:BrowsersConfig {:Enabled true}
|
||||
:PermissionsConfig {:Enabled true}
|
||||
:MailserversConfig {:Enabled true}
|
||||
:EnableNTPSync true
|
||||
:WakuConfig
|
||||
{:Enabled true
|
||||
:BloomFilterMode waku-bloom-filter-mode
|
||||
:LightClient true
|
||||
:MinimumPoW 0.000001}
|
||||
:WakuV2Config (assoc wakuv2-config
|
||||
:DiscoveryLimit 20
|
||||
:EnableDiscV5 true
|
||||
:Enabled wakuv2-enabled
|
||||
:Host "0.0.0.0")
|
||||
:WakuV2Config (assoc wakuv2-config
|
||||
:DiscoveryLimit 20
|
||||
:EnableDiscV5 true
|
||||
:Enabled wakuv2-enabled
|
||||
:Host "0.0.0.0")
|
||||
:ShhextConfig
|
||||
{:BackupDisabledDataDir (utils.platform/no-backup-directory)
|
||||
:InstallationID installation-id
|
||||
@@ -158,8 +168,8 @@
|
||||
:VerifyTransactionChainID config/verify-transaction-chain-id
|
||||
:DataSyncEnabled true
|
||||
:PFSEnabled true}
|
||||
:RequireTopics (get-topics current-network)
|
||||
:StatusAccountsConfig {:Enabled true})
|
||||
:RequireTopics (get-topics current-network)
|
||||
:StatusAccountsConfig {:Enabled true})
|
||||
|
||||
(and
|
||||
config/bootnodes-settings-enabled?
|
||||
@@ -173,7 +183,7 @@
|
||||
[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
|
||||
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`,
|
||||
@@ -185,7 +195,7 @@ app-db"
|
||||
:params [:node-config config]
|
||||
: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"
|
||||
[{:keys [db]} {:keys [on-success]}]
|
||||
(let [key-uid (get-in db [:multiaccount :key-uid])]
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
(ns status-im.notifications.core
|
||||
(:require [re-frame.core :as re-frame]
|
||||
[taoensso.timbre :as log]
|
||||
[status-im.utils.fx :as fx]
|
||||
(:require ["@react-native-community/push-notification-ios" :default pn-ios]
|
||||
[quo.platform :as platform]
|
||||
[re-frame.core :as re-frame]
|
||||
[status-im.ethereum.json-rpc :as json-rpc]
|
||||
[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.local :as local]
|
||||
[quo.platform :as platform]
|
||||
[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-custom 2)
|
||||
@@ -16,12 +16,14 @@
|
||||
(def apn-token-type 1)
|
||||
(def firebase-token-type 2)
|
||||
(def listeners-added? (atom nil))
|
||||
(defn server<-rpc [{:keys [type publicKey registered]}]
|
||||
(defn server<-rpc
|
||||
[{:keys [type publicKey registered]}]
|
||||
{:public-key publicKey
|
||||
:type type
|
||||
:type type
|
||||
:registered registered})
|
||||
|
||||
(defn add-event-listeners []
|
||||
(defn add-event-listeners
|
||||
[]
|
||||
(when-not @listeners-added?
|
||||
(reset! listeners-added? true)
|
||||
(.addEventListener
|
||||
@@ -35,23 +37,27 @@
|
||||
(fn [error]
|
||||
(re-frame/dispatch [:notifications/switch-error true error])))))
|
||||
|
||||
(defn enable-ios-notifications []
|
||||
(defn enable-ios-notifications
|
||||
[]
|
||||
(add-event-listeners)
|
||||
(-> (.requestPermissions ^js pn-ios)
|
||||
(.then #())
|
||||
(.catch #())))
|
||||
|
||||
(defn disable-ios-notifications []
|
||||
(defn disable-ios-notifications
|
||||
[]
|
||||
(.abandonPermissions ^js pn-ios)
|
||||
(re-frame/dispatch [:notifications/unregistered-from-push-notifications]))
|
||||
|
||||
(defn enable-android-notifications []
|
||||
(defn enable-android-notifications
|
||||
[]
|
||||
(pn-android/create-channel
|
||||
{:channel-id "status-im-notifications"
|
||||
:channel-name "Status push notifications"})
|
||||
(pn-android/enable-notifications))
|
||||
|
||||
(defn disable-android-notifications []
|
||||
(defn disable-android-notifications
|
||||
[]
|
||||
(pn-android/disable-notifications))
|
||||
|
||||
;; FIXME: Repalce with request permission from audio messages PR lib
|
||||
@@ -59,7 +65,7 @@
|
||||
::request-permission
|
||||
identity)
|
||||
|
||||
(fx/defn request-permission
|
||||
(rf/defn request-permission
|
||||
{:events [::request-permission]}
|
||||
[_]
|
||||
{::request-permission true})
|
||||
@@ -103,15 +109,16 @@
|
||||
(doseq [chat-id chat-ids]
|
||||
(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]}
|
||||
[cofx token]
|
||||
{::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-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]}
|
||||
[cofx]
|
||||
{::json-rpc/call [{:method "wakuext_unregisterFromPushNotifications"
|
||||
@@ -119,7 +126,7 @@
|
||||
:on-success #(log/info "[push-notifications] unregister-success" %)
|
||||
:on-error #(re-frame/dispatch [:notifications/switch-error false %])}]})
|
||||
|
||||
(fx/defn logout-disable
|
||||
(rf/defn logout-disable
|
||||
[cofx]
|
||||
(merge {::logout-disable nil}
|
||||
{::json-rpc/call [{:method "wakuext_unregisterFromPushNotifications"
|
||||
@@ -127,113 +134,132 @@
|
||||
:on-success #(log/info "[push-notifications] unregister-success" %)
|
||||
:on-error #(log/info "[push-notifications] unregister-error" %)}]}))
|
||||
|
||||
(fx/defn notification-switch-error
|
||||
(rf/defn notification-switch-error
|
||||
{:events [:notifications/switch-error]}
|
||||
[cofx enabled?]
|
||||
(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]}
|
||||
[{:keys [db] :as cofx} enabled? remote-push-notifications?]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(if enabled?
|
||||
{::enable remote-push-notifications?}
|
||||
{::enable remote-push-notifications?}
|
||||
{::disable nil})
|
||||
(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
|
||||
: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]}
|
||||
[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]}
|
||||
[cofx 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]}
|
||||
[{:keys [db] :as cofx} enabled?]
|
||||
(let [method (if enabled?
|
||||
"wakuext_enablePushNotificationsFromContactsOnly"
|
||||
"wakuext_disablePushNotificationsFromContactsOnly")]
|
||||
(fx/merge cofx
|
||||
{::json-rpc/call [{:method method
|
||||
:params []
|
||||
:on-success #(log/info "[push-notifications] contacts-notification-success" %)
|
||||
:on-error #(re-frame/dispatch [::non-contacts-update-error enabled? %])}]}
|
||||
(rf/merge
|
||||
cofx
|
||||
{::json-rpc/call [{:method method
|
||||
:params []
|
||||
: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]}
|
||||
[{:keys [db] :as cofx} enabled?]
|
||||
(let [method (if enabled?
|
||||
"wakuext_enablePushNotificationsBlockMentions"
|
||||
"wakuext_disablePushNotificationsBlockMentions")]
|
||||
(log/info "USING METHOD" method enabled?)
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{::json-rpc/call [{:method method
|
||||
:params []
|
||||
: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?)))))
|
||||
|
||||
(fx/defn switch-push-notifications-server-enabled
|
||||
(rf/defn switch-push-notifications-server-enabled
|
||||
{:events [::switch-push-notifications-server-enabled]}
|
||||
[{:keys [db] :as cofx} enabled?]
|
||||
(let [method (if enabled?
|
||||
"wakuext_startPushNotificationsServer"
|
||||
"wakuext_stopPushNotificationsServer")]
|
||||
(fx/merge cofx
|
||||
{::json-rpc/call [{:method method
|
||||
:params []
|
||||
:on-success #(log/info "[push-notifications] switch-server-enabled successful" %)
|
||||
:on-error #(re-frame/dispatch [::push-notifications-server-update-error enabled? %])}]}
|
||||
(rf/merge
|
||||
cofx
|
||||
{::json-rpc/call [{:method method
|
||||
:params []
|
||||
: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]}
|
||||
[{:keys [db] :as cofx} enabled?]
|
||||
(let [method (if enabled?
|
||||
"wakuext_enableSendingNotifications"
|
||||
"wakuext_disableSendingNotifications")]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{::json-rpc/call [{:method method
|
||||
:params []
|
||||
:on-success #(log/info "[push-notifications] switch-send-notifications successful" %)
|
||||
:on-error #(re-frame/dispatch [::push-notifications-send-update-error enabled? %])}]}
|
||||
:on-success
|
||||
#(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?)))))
|
||||
|
||||
(fx/defn handle-add-server-error
|
||||
(rf/defn handle-add-server-error
|
||||
{:events [::push-notifications-add-server-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]}
|
||||
[{:keys [db] :as cofx} public-key]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{::json-rpc/call [{:method "wakuext_addPushNotificationsServer"
|
||||
:params [public-key]
|
||||
:on-success #(do
|
||||
(log/info "[push-notifications] switch-send-notifications successful" %)
|
||||
(re-frame/dispatch [::fetch-servers]))
|
||||
:on-error #(re-frame/dispatch [::push-notifications-add-server-error public-key %])}]}))
|
||||
:on-success
|
||||
#(do
|
||||
(log/info "[push-notifications] switch-send-notifications successful"
|
||||
%)
|
||||
(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]}
|
||||
[{:keys [db]} 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]}
|
||||
[cofx]
|
||||
{::json-rpc/call [{:method "wakuext_getPushNotificationsServers"
|
||||
@@ -244,34 +270,43 @@
|
||||
|
||||
;; Wallet transactions
|
||||
|
||||
(fx/defn handle-preferences-load
|
||||
(rf/defn handle-preferences-load
|
||||
{:events [::preferences-loaded]}
|
||||
[{:keys [db]} preferences]
|
||||
{:db (assoc db :push-notifications/preferences preferences)})
|
||||
|
||||
(fx/defn load-notification-preferences
|
||||
(rf/defn load-notification-preferences
|
||||
{:events [::load-notification-preferences]}
|
||||
[cofx]
|
||||
{::json-rpc/call [{:method "localnotifications_notificationPreferences"
|
||||
:params []
|
||||
:on-success #(re-frame/dispatch [::preferences-loaded %])}]})
|
||||
|
||||
(defn preference= [x y]
|
||||
(defn preference=
|
||||
[x y]
|
||||
(and (= (:service x) (:service y))
|
||||
(= (:event x) (:event y))
|
||||
(= (:identifier x) (:identifier y))))
|
||||
|
||||
(defn- update-preference [all new]
|
||||
(defn- update-preference
|
||||
[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]}
|
||||
[{:keys [db] :as cofx} enabled?]
|
||||
{:db (update db :push-notifications/preferences update-preference {:enabled (not enabled?)
|
||||
:service "wallet"
|
||||
:event "transaction"
|
||||
:identifier "all"})
|
||||
{:db (update db
|
||||
:push-notifications/preferences
|
||||
update-preference
|
||||
{:enabled (not enabled?)
|
||||
:service "wallet"
|
||||
:event "transaction"
|
||||
:identifier "all"})
|
||||
::json-rpc/call [{:method "localnotifications_switchWalletNotifications"
|
||||
:params [(not enabled?)]
|
||||
:on-success #(log/info "[push-notifications] switch-transaction-notifications successful" %)
|
||||
:on-error #(log/error "[push-notifications] switch-transaction-notifications error" %)}]})
|
||||
:on-success #(log/info
|
||||
"[push-notifications] switch-transaction-notifications successful"
|
||||
%)
|
||||
:on-error #(log/error
|
||||
"[push-notifications] switch-transaction-notifications error"
|
||||
%)}]})
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
(ns status-im.notifications.local
|
||||
(:require [status-im.async-storage.core :as async-storage]
|
||||
[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]
|
||||
(:require ["@react-native-community/push-notification-ios" :default pn-ios]
|
||||
[cljs-bean.core :as bean]
|
||||
[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.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
|
||||
{:symbol :ERC20
|
||||
@@ -24,34 +24,39 @@
|
||||
(def notification-event-ios "localNotification")
|
||||
(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")
|
||||
(.presentLocalNotification
|
||||
pn-ios
|
||||
#js {:alertBody message
|
||||
:alertTitle title
|
||||
;; NOTE: Use a special type to hide in Obj-C code other notifications
|
||||
:userInfo (bean/->js (merge user-info
|
||||
{:notificationType "local-notification"}))})))
|
||||
#js
|
||||
{:alertBody message
|
||||
:alertTitle title
|
||||
;; NOTE: Use a special type to hide in Obj-C code other notifications
|
||||
:userInfo (bean/->js (merge user-info
|
||||
{:notificationType "local-notification"}))})))
|
||||
|
||||
(defn local-push-android
|
||||
[notification]
|
||||
(pn-android/present-local-notification notification))
|
||||
|
||||
(defn handle-notification-press [{{deep-link :deepLink} :userInfo
|
||||
interaction :userInteraction}]
|
||||
(defn handle-notification-press
|
||||
[{{deep-link :deepLink} :userInfo
|
||||
interaction :userInteraction}]
|
||||
(async-storage/set-item! (str :chat-id) nil)
|
||||
(when (and deep-link
|
||||
(or platform/ios?
|
||||
(and platform/android? interaction)))
|
||||
(re-frame/dispatch [:universal-links/handle-url deep-link])))
|
||||
|
||||
(defn listen-notifications []
|
||||
(defn listen-notifications
|
||||
[]
|
||||
(if platform/ios?
|
||||
(.addEventListener ^js pn-ios
|
||||
notification-event-ios
|
||||
(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
|
||||
notification-event-android
|
||||
(fn [^js data]
|
||||
@@ -72,21 +77,27 @@
|
||||
to (or (:name toAccount) (utils/get-shortened-address to))
|
||||
from (or (:name fromAccount) (utils/get-shortened-address from))
|
||||
title (case state
|
||||
"inbound" (i18n/label :t/push-inbound-transaction {:value amount
|
||||
:currency (:symbol token)})
|
||||
"outbound" (i18n/label :t/push-outbound-transaction {:value amount
|
||||
:currency (:symbol token)})
|
||||
"failed" (i18n/label :t/push-failed-transaction {:value amount
|
||||
:currency (:symbol token)})
|
||||
"inbound" (i18n/label :t/push-inbound-transaction
|
||||
{:value amount
|
||||
:currency (:symbol token)})
|
||||
"outbound" (i18n/label :t/push-outbound-transaction
|
||||
{:value amount
|
||||
:currency (:symbol token)})
|
||||
"failed" (i18n/label :t/push-failed-transaction
|
||||
{:value amount
|
||||
:currency (:symbol token)})
|
||||
nil)
|
||||
description (case state
|
||||
"inbound" (i18n/label :t/push-inbound-transaction-body {:from from
|
||||
:to to})
|
||||
"outbound" (i18n/label :t/push-outbound-transaction-body {:from from
|
||||
:to to})
|
||||
"failed" (i18n/label :t/push-failed-transaction-body {:value amount
|
||||
:currency (:symbol token)
|
||||
:to to})
|
||||
"inbound" (i18n/label :t/push-inbound-transaction-body
|
||||
{:from from
|
||||
:to to})
|
||||
"outbound" (i18n/label :t/push-outbound-transaction-body
|
||||
{:from from
|
||||
:to to})
|
||||
"failed" (i18n/label :t/push-failed-transaction-body
|
||||
{:value amount
|
||||
:currency (:symbol token)
|
||||
:to to})
|
||||
nil)]
|
||||
{:title title
|
||||
:icon (get-in token [:icon :source])
|
||||
@@ -113,21 +124,22 @@
|
||||
"message" (when (show-message-pn? cofx notification) notification)
|
||||
"transaction" (create-transfer-notification cofx notification)
|
||||
nil)
|
||||
:body-type bodyType)))
|
||||
:body-type
|
||||
bodyType)))
|
||||
|
||||
(re-frame/reg-fx
|
||||
::local-push-ios
|
||||
(fn [evt]
|
||||
(-> evt create-notification local-push-ios)))
|
||||
|
||||
(fx/defn local-notification-android
|
||||
(rf/defn local-notification-android
|
||||
{:events [::local-notification-android]}
|
||||
[cofx event]
|
||||
(some->> event
|
||||
(create-notification cofx)
|
||||
local-push-android))
|
||||
|
||||
(fx/defn process
|
||||
(rf/defn process
|
||||
[cofx evt]
|
||||
(if platform/ios?
|
||||
{::local-push-ios evt}
|
||||
|
||||
@@ -2,36 +2,40 @@
|
||||
(:require [re-frame.core :as re-frame]
|
||||
[status-im.ethereum.json-rpc :as json-rpc]
|
||||
[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.fx :as fx]
|
||||
[status-im.utils.platform :as utils.platform]
|
||||
[status-im2.navigation.events :as navigation]
|
||||
[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"
|
||||
:params [installation-id]
|
||||
: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"
|
||||
:params [installation-id]
|
||||
: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"
|
||||
:params [installation-id metadata]
|
||||
: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"
|
||||
:params []
|
||||
:on-success on-success
|
||||
:on-error on-error}))
|
||||
:on-error on-error}))
|
||||
|
||||
(defn compare-installation
|
||||
"Sort installations, first by our installation-id, then on whether is
|
||||
@@ -52,56 +56,61 @@
|
||||
[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]}
|
||||
[_]
|
||||
{::json-rpc/call [{:method "wakuext_sendPairInstallation"
|
||||
:params []
|
||||
:on-success #(log/info "sent pair installation message")}]})
|
||||
|
||||
(fx/defn prompt-dismissed
|
||||
(rf/defn prompt-dismissed
|
||||
{:events [:pairing.ui/prompt-dismissed]}
|
||||
[{:keys [db]}]
|
||||
{:db (assoc-in db [:pairing/prompt-user-pop-up] false)})
|
||||
|
||||
(fx/defn prompt-accepted
|
||||
(rf/defn prompt-accepted
|
||||
{:events [:pairing.ui/prompt-accepted]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{:db (assoc-in db [:pairing/prompt-user-pop-up] false)}
|
||||
(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?
|
||||
{:db (assoc-in db [:pairing/prompt-user-pop-up] true)
|
||||
: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)
|
||||
:cancel-button-text (i18n/label :t/cancel)
|
||||
:on-cancel #(re-frame/dispatch [:pairing.ui/prompt-dismissed])
|
||||
:on-accept #(re-frame/dispatch [:pairing.ui/prompt-accepted])}}))
|
||||
|
||||
(fx/defn set-name
|
||||
(rf/defn set-name
|
||||
"Set the name of the device"
|
||||
{:events [:pairing.ui/set-name-pressed]}
|
||||
[{:keys [db]} installation-name]
|
||||
(let [our-installation-id (get-in db [:multiaccount :installation-id])]
|
||||
{:pairing/set-installation-metadata [our-installation-id
|
||||
{:name installation-name
|
||||
{:name installation-name
|
||||
:deviceType utils.platform/os}]}))
|
||||
|
||||
(fx/defn init [cofx]
|
||||
(rf/defn init
|
||||
[cofx]
|
||||
{:pairing/get-our-installations nil})
|
||||
|
||||
(fx/defn enable [{:keys [db]} installation-id]
|
||||
(rf/defn enable
|
||||
[{:keys [db]} installation-id]
|
||||
{:db (assoc-in db
|
||||
[:pairing/installations installation-id :enabled?]
|
||||
true)})
|
||||
[:pairing/installations installation-id :enabled?]
|
||||
true)})
|
||||
|
||||
(fx/defn disable [{:keys [db]} installation-id]
|
||||
(rf/defn disable
|
||||
[{:keys [db]} installation-id]
|
||||
{:db (assoc-in db
|
||||
[:pairing/installations installation-id :enabled?]
|
||||
false)})
|
||||
[:pairing/installations installation-id :enabled?]
|
||||
false)})
|
||||
|
||||
(defn handle-enable-installation-response-success
|
||||
"Callback to dispatch on enable signature response"
|
||||
@@ -123,38 +132,43 @@
|
||||
[result]
|
||||
(re-frame/dispatch [:pairing.callback/get-our-installations-success result]))
|
||||
|
||||
(defn enable-installation! [installation-id]
|
||||
(defn enable-installation!
|
||||
[installation-id]
|
||||
(enable-installation-rpc
|
||||
installation-id
|
||||
(partial handle-enable-installation-response-success installation-id)
|
||||
nil))
|
||||
|
||||
(defn disable-installation! [installation-id]
|
||||
(defn disable-installation!
|
||||
[installation-id]
|
||||
(disable-installation-rpc
|
||||
installation-id
|
||||
(partial handle-disable-installation-response-success installation-id)
|
||||
nil))
|
||||
|
||||
(defn set-installation-metadata! [installation-id metadata]
|
||||
(defn set-installation-metadata!
|
||||
[installation-id metadata]
|
||||
(set-installation-metadata-rpc
|
||||
installation-id
|
||||
metadata
|
||||
(partial handle-set-installation-metadata-response-success installation-id metadata)
|
||||
nil))
|
||||
|
||||
(defn get-our-installations []
|
||||
(defn get-our-installations
|
||||
[]
|
||||
(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]}
|
||||
[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]}
|
||||
{: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)}}))
|
||||
|
||||
(fx/defn disable-fx
|
||||
(rf/defn disable-fx
|
||||
{:events [:pairing.ui/disable-installation-pressed]}
|
||||
[_ installation-id]
|
||||
{:pairing/disable-installation [installation-id]})
|
||||
@@ -178,58 +192,66 @@
|
||||
:pairing/get-our-installations
|
||||
get-our-installations)
|
||||
|
||||
(fx/defn send-installation-messages
|
||||
(rf/defn send-installation-messages
|
||||
{:events [:pairing.ui/synchronize-installation-pressed]}
|
||||
[{:keys [db]}]
|
||||
(let [multiaccount (:multiaccount db)
|
||||
(let [multiaccount (:multiaccount db)
|
||||
{:keys [name preferred-name identicon]} multiaccount]
|
||||
{::json-rpc/call [{:method "wakuext_syncDevices"
|
||||
:params [(or preferred-name name) identicon]
|
||||
: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
|
||||
:name (:name metadata)
|
||||
:timestamp timestamp
|
||||
:device-type (:deviceType metadata)
|
||||
:enabled? enabled})
|
||||
:name (:name metadata)
|
||||
:timestamp timestamp
|
||||
:device-type (:deviceType metadata)
|
||||
:enabled? enabled})
|
||||
|
||||
(fx/defn update-installation
|
||||
(rf/defn update-installation
|
||||
{:events [:pairing.callback/set-installation-metadata-success]}
|
||||
[{:keys [db]} installation-id metadata]
|
||||
{:db (update-in db [:pairing/installations installation-id]
|
||||
{:db (update-in db
|
||||
[:pairing/installations installation-id]
|
||||
assoc
|
||||
:installation-id installation-id
|
||||
:name (:name metadata)
|
||||
:device-type (:deviceType metadata))})
|
||||
:name (:name metadata)
|
||||
:device-type (:deviceType metadata))})
|
||||
|
||||
(fx/defn handle-installations [{:keys [db]} installations]
|
||||
{:db (update db :pairing/installations #(reduce
|
||||
(fn [acc {:keys [id] :as i}]
|
||||
(update acc id merge (installation<-rpc i)))
|
||||
%
|
||||
installations))})
|
||||
(rf/defn handle-installations
|
||||
[{:keys [db]} installations]
|
||||
{:db (update db
|
||||
:pairing/installations
|
||||
#(reduce
|
||||
(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]}
|
||||
[{:keys [db]} installations]
|
||||
{:db (assoc db :pairing/installations (reduce
|
||||
(fn [acc {:keys [id] :as i}]
|
||||
(assoc acc id
|
||||
(installation<-rpc i)))
|
||||
{}
|
||||
installations))})
|
||||
{:db (assoc db
|
||||
:pairing/installations
|
||||
(reduce
|
||||
(fn [acc {:keys [id] :as i}]
|
||||
(assoc acc
|
||||
id
|
||||
(installation<-rpc i)))
|
||||
{}
|
||||
installations))})
|
||||
|
||||
(fx/defn enable-installation-success
|
||||
(rf/defn enable-installation-success
|
||||
{:events [:pairing.callback/enable-installation-success]}
|
||||
[cofx installation-id]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(enable installation-id)
|
||||
(multiaccounts.update/send-multiaccount-update)))
|
||||
|
||||
(fx/defn disable-installation-success
|
||||
(rf/defn disable-installation-success
|
||||
{:events [:pairing.callback/disable-installation-success]}
|
||||
[cofx installation-id]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(disable installation-id)
|
||||
(multiaccounts.update/send-multiaccount-update)))
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
(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]}
|
||||
[_ value]
|
||||
{:show-popover nil
|
||||
{:show-popover nil
|
||||
;;TODO refactor popover just start animation on mount
|
||||
:dispatch-later [{:ms 250 :dispatch [:show-popover-db value]}]
|
||||
:dismiss-keyboard nil})
|
||||
|
||||
(fx/defn show-popover-db
|
||||
(rf/defn show-popover-db
|
||||
{:events [:show-popover-db]}
|
||||
[{:keys [db]} value]
|
||||
{:db (assoc db :popover/popover value)})
|
||||
|
||||
(fx/defn hide-popover
|
||||
(rf/defn hide-popover
|
||||
{:events [:hide-popover]}
|
||||
[{:keys [db]}]
|
||||
{:db (dissoc db :popover/popover)
|
||||
{:db (dissoc db :popover/popover)
|
||||
:hide-popover nil})
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
(ns status-im.profile.core
|
||||
(:require [re-frame.core :as re-frame]
|
||||
[status-im.utils.fx :as fx]
|
||||
[status-im.ui.components.list-selection :as list-selection]
|
||||
[status-im.utils.universal-links.utils :as universal-links]
|
||||
(:require [clojure.string :as string]
|
||||
[re-frame.core :as re-frame]
|
||||
[status-im.multiaccounts.update.core :as multiaccounts.update]
|
||||
[status-im.ui.components.list-selection :as list-selection]
|
||||
[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
|
||||
:copy-to-clipboard
|
||||
@@ -30,7 +30,9 @@
|
||||
(swap! tooltips dissoc interval-id))
|
||||
(do (re-frame/dispatch [:set-in [:tooltips tooltip-id] opacity])
|
||||
(when (< 10 cnt)
|
||||
(swap! tooltips assoc-in [tooltip-id :opacity] (- opacity 0.05)))))))
|
||||
(swap! tooltips assoc-in
|
||||
[tooltip-id :opacity]
|
||||
(- opacity 0.05)))))))
|
||||
100)]
|
||||
(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)]
|
||||
(list-selection/open-share {:message link}))))
|
||||
|
||||
(fx/defn finish-success
|
||||
(rf/defn finish-success
|
||||
{:events [:my-profile/finish-success]}
|
||||
[{:keys [db] :as cofx}]
|
||||
{:db (update db :my-profile/seed assoc :step :finish :error nil :word nil)})
|
||||
|
||||
(fx/defn finish
|
||||
(rf/defn finish
|
||||
{:events [:my-profile/finish]}
|
||||
[cofx]
|
||||
(multiaccounts.update/clean-seed-phrase
|
||||
cofx
|
||||
{: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]}
|
||||
[{:keys [db]}]
|
||||
(let [{:keys [mnemonic]} (:multiaccount db)
|
||||
shuffled-mnemonic (shuffle (map-indexed vector (string/split mnemonic #" ")))]
|
||||
{:db (assoc db :my-profile/seed {:step :first-word
|
||||
:first-word (first shuffled-mnemonic)
|
||||
:second-word (second shuffled-mnemonic)})}))
|
||||
shuffled-mnemonic (shuffle (map-indexed vector (string/split mnemonic #" ")))]
|
||||
{:db (assoc db
|
||||
:my-profile/seed
|
||||
{: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]}
|
||||
[{:keys [db]} step]
|
||||
{: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]}
|
||||
[_ value]
|
||||
{:copy-to-clipboard value})
|
||||
|
||||
(fx/defn show-tooltip
|
||||
(rf/defn show-tooltip
|
||||
{:events [:show-tooltip]}
|
||||
[_ tooltip-id]
|
||||
{:show-tooltip tooltip-id})
|
||||
|
||||
(fx/defn share-profile-link
|
||||
(rf/defn share-profile-link
|
||||
{:events [:profile/share-profile-link]}
|
||||
[_ value]
|
||||
{:profile/share-profile-link value})
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
(ns status-im.qr-scanner.core
|
||||
(:require [re-frame.core :as re-frame]
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
[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]
|
||||
(:require [clojure.string :as string]
|
||||
[re-frame.core :as re-frame]
|
||||
[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]
|
||||
[clojure.string :as string]
|
||||
[taoensso.timbre :as log]))
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
[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]}
|
||||
[_ opts]
|
||||
{:request-permissions-fx
|
||||
@@ -24,48 +24,52 @@
|
||||
(i18n/label :t/camera-access-error))
|
||||
50))}})
|
||||
|
||||
(fx/defn set-qr-code
|
||||
(rf/defn set-qr-code
|
||||
{:events [:qr-scanner.callback/scan-qr-code-success]}
|
||||
[{:keys [db]} opts data]
|
||||
(when-let [handler (:handler 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]}
|
||||
[cofx opts]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(navigation/navigate-back)
|
||||
(when-let [handler (:cancel-handler opts)]
|
||||
(fn [] {:dispatch [handler opts]}))))
|
||||
|
||||
(fx/defn handle-browse [cofx {:keys [url]}]
|
||||
(fx/merge cofx
|
||||
(rf/defn handle-browse
|
||||
[cofx {:keys [url]}]
|
||||
(rf/merge cofx
|
||||
{:browser/show-browser-selection url}
|
||||
(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)
|
||||
(chat/start-chat cofx chat-id nil)
|
||||
{:utils/show-popup {:title (i18n/label :t/unable-to-read-this-code)
|
||||
: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)
|
||||
(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))
|
||||
|
||||
(fx/defn handle-view-profile
|
||||
(rf/defn handle-view-profile
|
||||
[{:keys [db] :as cofx} {:keys [public-key ens-name]}]
|
||||
(let [own (new-chat.db/own-public-key? db public-key)]
|
||||
(cond
|
||||
(and public-key own)
|
||||
{:change-tab-fx :profile
|
||||
{:change-tab-fx :profile
|
||||
:pop-to-root-tab-fx :profile-stack}
|
||||
|
||||
(and public-key (not own))
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{:dispatch [:chat.ui/show-profile public-key ens-name]}
|
||||
(navigation/navigate-back))
|
||||
|
||||
@@ -74,13 +78,14 @@
|
||||
:content (i18n/label :t/ens-name-not-found)
|
||||
:on-dismiss #(re-frame/dispatch [:pop-to-root-tab :chat-stack])}})))
|
||||
|
||||
(fx/defn handle-eip681 [cofx data]
|
||||
(fx/merge cofx
|
||||
(rf/defn handle-eip681
|
||||
[cofx data]
|
||||
(rf/merge cofx
|
||||
{:dispatch [:wallet/parse-eip681-uri-and-resolve-ens data]}
|
||||
(navigation/change-tab :wallet)
|
||||
(navigation/pop-to-root-tab :wallet-stack)))
|
||||
|
||||
(fx/defn handle-wallet-connect
|
||||
(rf/defn handle-wallet-connect
|
||||
{:events [::handle-wallet-connect-uri]}
|
||||
[cofx data]
|
||||
(let [wc-version (last (string/split (first (string/split data "?")) "@"))]
|
||||
@@ -88,12 +93,12 @@
|
||||
{:dispatch [:wallet-connect-legacy/pair data]}
|
||||
{:dispatch [:wallet-connect/pair data]})))
|
||||
|
||||
(fx/defn handle-local-pairing
|
||||
(rf/defn handle-local-pairing
|
||||
{:events [::handle-local-pairing-uri]}
|
||||
[_ data]
|
||||
{:dispatch [:syncing/input-connection-string-for-bootstrapping data]})
|
||||
|
||||
(fx/defn match-scan
|
||||
(rf/defn match-scan
|
||||
{:events [::match-scanned-value]}
|
||||
[cofx {:keys [type] :as data}]
|
||||
(case type
|
||||
@@ -109,11 +114,11 @@
|
||||
(log/info "Unable to find matcher for scanned value"
|
||||
{:type type
|
||||
:event ::match-scanned-value})
|
||||
{:dispatch [:navigate-back]
|
||||
{:dispatch [:navigate-back]
|
||||
:utils/show-popup {:title (i18n/label :t/unable-to-read-this-code)
|
||||
:on-dismiss #(re-frame/dispatch [:pop-to-root-tab :chat-stack])}})))
|
||||
|
||||
(fx/defn on-scan
|
||||
(rf/defn on-scan
|
||||
{:events [::on-scan-success]}
|
||||
[{:keys [db]} uri]
|
||||
{::router/handle-uri {:chain (ethereum/chain-keyword db)
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
(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]}
|
||||
[cofx 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]}
|
||||
[cofx 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]}
|
||||
[cofx 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]}
|
||||
[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)})
|
||||
|
||||
@@ -1,85 +1,114 @@
|
||||
(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.mailserver.core :as mailserver]
|
||||
[status-im.multiaccounts.login.core :as login]
|
||||
[status-im.transport.message.core :as transport.message]
|
||||
[status-im.notifications.local :as local-notifications]
|
||||
[status-im.chat.models.message :as models.message]
|
||||
[status-im.chat.models.link-preview :as link.preview]
|
||||
[status-im.transport.message.core :as transport.message]
|
||||
[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]}]
|
||||
(log/debug "[signals] status-node-started"
|
||||
"error" error)
|
||||
"error"
|
||||
error)
|
||||
(if error
|
||||
(cond-> {:db (-> db
|
||||
(update :multiaccounts/login dissoc :processing)
|
||||
(assoc-in [:multiaccounts/login :error]
|
||||
;; NOTE: the only currently known error is
|
||||
;; "file is not a database" which occurs
|
||||
;; when the user inputs the wrong password
|
||||
;; if that is the error that is found
|
||||
;; we show the i18n label for wrong password
|
||||
;; to the user
|
||||
;; in case of an unknown error we show the
|
||||
;; error
|
||||
(if (= error "file is not a database")
|
||||
(i18n/label :t/wrong-password)
|
||||
error)))}
|
||||
(cond->
|
||||
{:db (-> db
|
||||
(update :multiaccounts/login dissoc :processing)
|
||||
(assoc-in [:multiaccounts/login :error]
|
||||
;; NOTE: the only currently known error is
|
||||
;; "file is not a database" which occurs
|
||||
;; when the user inputs the wrong password
|
||||
;; if that is the error that is found
|
||||
;; we show the i18n label for wrong password
|
||||
;; to the user
|
||||
;; in case of an unknown error we show the
|
||||
;; error
|
||||
(if (= error "file is not a database")
|
||||
(i18n/label :t/wrong-password)
|
||||
error)))}
|
||||
(= (:view-id db) :progress)
|
||||
(assoc :dispatch [:navigate-to :login]))
|
||||
(login/multiaccount-login-success cofx)))
|
||||
|
||||
(fx/defn summary
|
||||
(rf/defn summary
|
||||
[{:keys [db] :as cofx} peers-summary]
|
||||
(let [previous-summary (:peers-summary db)
|
||||
peers-count (count peers-summary)]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{:db (assoc db
|
||||
:peers-summary peers-summary
|
||||
:peers-count peers-count)}
|
||||
:peers-count peers-count)}
|
||||
(visibility-status-updates/peers-summary-change peers-count))))
|
||||
|
||||
(fx/defn wakuv2-peer-stats
|
||||
(rf/defn wakuv2-peer-stats
|
||||
[{:keys [db]} peer-stats]
|
||||
(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)))}))
|
||||
|
||||
(defn handle-local-pairing-signals [signal-type]
|
||||
(defn handle-local-pairing-signals
|
||||
[signal-type]
|
||||
(log/info "local pairing signal received"
|
||||
{:signal-type signal-type}))
|
||||
|
||||
(fx/defn process
|
||||
(rf/defn process
|
||||
{:events [:signals/signal-received]}
|
||||
[{:keys [db] :as cofx} event-str]
|
||||
;; 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
|
||||
;; data structures
|
||||
(let [^js data (.parse js/JSON event-str)
|
||||
(let [^js data (.parse js/JSON event-str)
|
||||
^js event-js (.-event data)
|
||||
type (.-type data)]
|
||||
type (.-type data)]
|
||||
(case type
|
||||
"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))}
|
||||
"envelope.sent" (transport.message/update-envelopes-status cofx (:ids (js->clj event-js :keywordize-keys true)) :sent)
|
||||
"envelope.expired" (transport.message/update-envelopes-status cofx (:ids (js->clj event-js :keywordize-keys true)) :not-sent)
|
||||
"message.delivered" (let [{:keys [chatID messageID]} (js->clj event-js :keywordize-keys true)]
|
||||
(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)
|
||||
"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))}
|
||||
"envelope.sent" (transport.message/update-envelopes-status cofx
|
||||
(:ids
|
||||
(js->clj event-js
|
||||
:keywordize-keys
|
||||
true))
|
||||
:sent)
|
||||
"envelope.expired" (transport.message/update-envelopes-status cofx
|
||||
(:ids
|
||||
(js->clj event-js
|
||||
:keywordize-keys
|
||||
true))
|
||||
:not-sent)
|
||||
"message.delivered" (let [{:keys [chatID messageID]} (js->clj event-js
|
||||
:keywordize-keys
|
||||
true)]
|
||||
(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"))))
|
||||
|
||||
+221
-192
@@ -1,27 +1,27 @@
|
||||
(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]
|
||||
[status-im.constants :as constants]
|
||||
[status-im.ethereum.core :as ethereum]
|
||||
[status-im.ethereum.eip55 :as eip55]
|
||||
[status-im.ethereum.json-rpc :as json-rpc]
|
||||
[status-im.ethereum.tokens :as tokens]
|
||||
[status-im.keycard.common :as keycard.common]
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
[status-im.keycard.card :as keycard.card]
|
||||
[status-im.keycard.common :as keycard.common]
|
||||
[status-im.native-module.core :as status]
|
||||
[status-im.signing.eip1559 :as eip1559]
|
||||
[status-im.signing.keycard :as signing.keycard]
|
||||
[status-im.utils.fx :as fx]
|
||||
[status-im.utils.hex :as utils.hex]
|
||||
[status-im.utils.money :as money]
|
||||
[utils.security.core :as security]
|
||||
[status-im.utils.types :as types]
|
||||
[status-im.utils.utils :as utils]
|
||||
[status-im.wallet.prices :as prices]
|
||||
[status-im.wallet.core :as wallet]
|
||||
[status-im.wallet.prices :as prices]
|
||||
[taoensso.timbre :as log]
|
||||
[clojure.set :as clojure.set]
|
||||
[status-im.signing.eip1559 :as eip1559]))
|
||||
[utils.re-frame :as rf]
|
||||
[utils.security.core :as security]))
|
||||
|
||||
(re-frame/reg-fx
|
||||
:signing/send-transaction-fx
|
||||
@@ -59,42 +59,46 @@
|
||||
(status/sign-typed-data-v4 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)]
|
||||
(or
|
||||
(get-in db [:contacts/contacts to])
|
||||
{:address (ethereum/normalized-hex to)})))
|
||||
|
||||
(fx/defn change-password
|
||||
(rf/defn change-password
|
||||
{:events [:signing.ui/password-is-changed]}
|
||||
[{db :db} password]
|
||||
(let [unmasked-pass (security/safe-unmask-data password)]
|
||||
{:db (update db :signing/sign assoc
|
||||
:password password
|
||||
:error nil
|
||||
:enabled? (and unmasked-pass (> (count unmasked-pass) 5)))}))
|
||||
{:db (update db
|
||||
:signing/sign assoc
|
||||
:password password
|
||||
: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}]
|
||||
(let [{{:keys [data typed? from v4]} :message} tx
|
||||
{:keys [in-progress? password]} sign
|
||||
from (or from (ethereum/default-address db))
|
||||
hashed-password (ethereum/sha3 (security/safe-unmask-data password))]
|
||||
{:keys [in-progress? password]} sign
|
||||
from (or from (ethereum/default-address db))
|
||||
hashed-password (ethereum/sha3 (security/safe-unmask-data password))]
|
||||
(when-not in-progress?
|
||||
(merge
|
||||
{:db (update db :signing/sign assoc :error nil :in-progress? true)}
|
||||
(if typed?
|
||||
{:signing.fx/sign-typed-data {:v4 v4
|
||||
:data data
|
||||
:account from
|
||||
{:signing.fx/sign-typed-data {:v4 v4
|
||||
:data data
|
||||
:account from
|
||||
: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
|
||||
:password hashed-password
|
||||
: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]}
|
||||
[{{:signing/keys [sign tx] :as db} :db :as cofx}]
|
||||
(let [{:keys [in-progress? password]} sign
|
||||
@@ -110,57 +114,62 @@
|
||||
(when nonce
|
||||
{:nonce (str "0x" (status/number-to-hex nonce))})
|
||||
(when maxPriorityFeePerGas
|
||||
{:maxPriorityFeePerGas (str "0x" (status/number-to-hex
|
||||
(js/parseInt maxPriorityFeePerGas)))})
|
||||
{:maxPriorityFeePerGas (str "0x"
|
||||
(status/number-to-hex
|
||||
(js/parseInt maxPriorityFeePerGas)))})
|
||||
(when maxFeePerGas
|
||||
{:maxFeePerGas (str "0x" (status/number-to-hex
|
||||
(js/parseInt maxFeePerGas)))}))]
|
||||
{:maxFeePerGas (str "0x"
|
||||
(status/number-to-hex
|
||||
(js/parseInt maxFeePerGas)))}))]
|
||||
(when-not in-progress?
|
||||
{: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
|
||||
: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 [value gasPrice maxFeePerGas maxPriorityFeePerGas gas data to from hash]} symbol amount]
|
||||
(let [token (tokens/symbol->token (:wallet/all-tokens db) symbol)
|
||||
from (eip55/address->checksum from)
|
||||
(let [token (tokens/symbol->token (:wallet/all-tokens db) symbol)
|
||||
from (eip55/address->checksum from)
|
||||
;;if there is a hash in the tx object that means we resending transaction
|
||||
old-tx-hash hash
|
||||
gas-price (money/to-fixed (money/bignumber gasPrice))
|
||||
gas-limit (money/to-fixed (money/bignumber gas))
|
||||
tx {:timestamp now
|
||||
:to to
|
||||
:from from
|
||||
:type :pending
|
||||
:hash new-tx-hash
|
||||
:data data
|
||||
:token token
|
||||
:symbol symbol
|
||||
:value (if token
|
||||
(money/to-fixed (money/unit->token amount (:decimals token)))
|
||||
(money/to-fixed (money/bignumber value)))
|
||||
:gas-price gas-price
|
||||
:fee-cap maxFeePerGas
|
||||
:tip-cap maxPriorityFeePerGas
|
||||
:gas-limit gas-limit}]
|
||||
gas-price (money/to-fixed (money/bignumber gasPrice))
|
||||
gas-limit (money/to-fixed (money/bignumber gas))
|
||||
tx {:timestamp now
|
||||
:to to
|
||||
:from from
|
||||
:type :pending
|
||||
:hash new-tx-hash
|
||||
:data data
|
||||
:token token
|
||||
:symbol symbol
|
||||
:value (if token
|
||||
(money/to-fixed (money/unit->token amount (:decimals token)))
|
||||
(money/to-fixed (money/bignumber value)))
|
||||
:gas-price gas-price
|
||||
:fee-cap maxFeePerGas
|
||||
:tip-cap maxPriorityFeePerGas
|
||||
:gas-limit gas-limit}]
|
||||
(log/info "[signing] prepare-unconfirmed-transaction" tx)
|
||||
{:db (-> db
|
||||
;;remove old transaction, because we replace it with the new one
|
||||
(update-in [:wallet :accounts from :transactions] dissoc old-tx-hash)
|
||||
(assoc-in [:wallet :accounts from :transactions new-tx-hash] tx))
|
||||
::json-rpc/call [{:method "wallet_storePendingTransaction"
|
||||
:params [(-> tx
|
||||
(dissoc :gas-price :gas-limit)
|
||||
(assoc :gasPrice
|
||||
(money/to-fixed (money/bignumber gasPrice))
|
||||
:gasLimit (money/to-fixed (money/bignumber gas)))
|
||||
clj->js)]
|
||||
{:db (-> db
|
||||
;;remove old transaction, because we replace it with the new one
|
||||
(update-in [:wallet :accounts from :transactions] dissoc old-tx-hash)
|
||||
(assoc-in [:wallet :accounts from :transactions new-tx-hash] tx))
|
||||
::json-rpc/call [{:method "wallet_storePendingTransaction"
|
||||
:params [(-> tx
|
||||
(dissoc :gas-price :gas-limit)
|
||||
(assoc :gasPrice
|
||||
(money/to-fixed (money/bignumber gasPrice))
|
||||
:gasLimit (money/to-fixed (money/bignumber gas)))
|
||||
clj->js)]
|
||||
: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
|
||||
(string/starts-with? data constants/method-id-transfer)
|
||||
:transfer
|
||||
@@ -169,13 +178,16 @@
|
||||
(string/starts-with? data constants/method-id-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)]
|
||||
(when (and token data (string? data))
|
||||
(when-let [type (get-method-type data)]
|
||||
(let [[address value _] (status/decode-parameters
|
||||
(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)
|
||||
{:to address
|
||||
:contact (get-contact db address)
|
||||
@@ -186,7 +198,8 @@
|
||||
:token token
|
||||
: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}
|
||||
:cancel? cancel?
|
||||
:hash hash}
|
||||
@@ -196,20 +209,23 @@
|
||||
eth-amount (when eth-value (money/to-fixed (money/wei->ether eth-value)))
|
||||
token (get-transfer-token db to data)]
|
||||
(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
|
||||
:contact (get-contact db to)
|
||||
:symbol :ETH
|
||||
:value value
|
||||
: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))
|
||||
token
|
||||
:else
|
||||
{:to 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
|
||||
tx
|
||||
(parse-tx-obj db tx-obj)
|
||||
@@ -221,45 +237,52 @@
|
||||
:maxPriorityFeePerGas (when 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
|
||||
{{: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]))
|
||||
wallet-set-up-passed? (get-in db [:multiaccount :wallet-set-up-passed?])]
|
||||
(if message
|
||||
(fx/merge
|
||||
(rf/merge
|
||||
cofx
|
||||
{:db (assoc db
|
||||
:signing/queue (drop-last queue)
|
||||
:signing/tx tx
|
||||
:signing/sign {:type (cond pinless? :pinless
|
||||
keycard-multiaccount? :keycard
|
||||
:else :password)
|
||||
:formatted-data (if typed?
|
||||
(types/js->pretty-json (types/json->js data))
|
||||
(ethereum/hex->text data))
|
||||
:keycard-step (when pinless? :connect)})
|
||||
{:db (assoc db
|
||||
:signing/queue (drop-last queue)
|
||||
:signing/tx tx
|
||||
:signing/sign {:type (cond pinless? :pinless
|
||||
keycard-multiaccount? :keycard
|
||||
:else :password)
|
||||
:formatted-data (if typed?
|
||||
(types/js->pretty-json
|
||||
(types/json->js data))
|
||||
(ethereum/hex->text data))
|
||||
:keycard-step (when pinless? :connect)})
|
||||
:show-signing-sheet nil}
|
||||
#(when-not wallet-set-up-passed?
|
||||
{:dispatch-later [{:dispatch [:show-popover {:view :signing-phrase}] :ms 200}]})
|
||||
(when pinless?
|
||||
(keycard.card/start-nfc {:on-success #(re-frame/dispatch [:keycard.callback/start-nfc-success])
|
||||
:on-failure #(re-frame/dispatch [:keycard.callback/start-nfc-failure])})
|
||||
(signing.keycard/hash-message {:data data
|
||||
:typed? true
|
||||
:on-completed #(re-frame/dispatch [:keycard/store-hash-and-sign-typed %])})))
|
||||
(fx/merge
|
||||
:on-failure #(re-frame/dispatch
|
||||
[:keycard.callback/start-nfc-failure])})
|
||||
(signing.keycard/hash-message
|
||||
{:data data
|
||||
:typed? true
|
||||
:on-completed #(re-frame/dispatch [:keycard/store-hash-and-sign-typed %])})))
|
||||
(rf/merge
|
||||
cofx
|
||||
{:db (assoc db
|
||||
:signing/queue (drop-last queue)
|
||||
:signing/tx (prepare-tx db tx))
|
||||
{:db (assoc db
|
||||
:signing/queue (drop-last queue)
|
||||
:signing/tx (prepare-tx db tx))
|
||||
:show-signing-sheet nil
|
||||
:dismiss-keyboard nil}
|
||||
:dismiss-keyboard nil}
|
||||
#(when-not wallet-set-up-passed?
|
||||
{:dispatch-later [{:dispatch [:show-popover {:view :signing-phrase}] :ms 200}]})
|
||||
(prices/update-prices)
|
||||
#(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
|
||||
(dissoc :gasPrice)
|
||||
(update :maxFeePerGas
|
||||
@@ -269,9 +292,9 @@
|
||||
(money/mul 2)
|
||||
money/to-hex))))
|
||||
:success-event :signing/update-estimated-gas-success
|
||||
:error-event :signing/update-estimated-gas-error}})
|
||||
:error-event :signing/update-estimated-gas-error}})
|
||||
(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
|
||||
{:success-callback #(re-frame/dispatch
|
||||
[:wallet.send/update-gas-price-success :signing/tx % tx-obj])
|
||||
@@ -279,40 +302,41 @@
|
||||
:network-id (get-in (ethereum/current-network db)
|
||||
[:config :NetworkId])}})))))
|
||||
|
||||
(fx/defn check-queue [{:keys [db] :as cofx}]
|
||||
(rf/defn check-queue
|
||||
[{:keys [db] :as cofx}]
|
||||
(let [{:signing/keys [tx queue]} db]
|
||||
(when (and (not tx) (seq queue))
|
||||
(show-sign cofx))))
|
||||
|
||||
(fx/defn send-transaction-message
|
||||
(rf/defn send-transaction-message
|
||||
{:events [:sign/send-transaction-message]}
|
||||
[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
|
||||
;; as an integer or big-int
|
||||
:params [chat-id (str value) contract transaction-hash
|
||||
(or (:result (types/json->clj signature))
|
||||
(ethereum/normalized-hex signature))]
|
||||
:params [chat-id (str value) contract transaction-hash
|
||||
(or (:result (types/json->clj signature))
|
||||
(ethereum/normalized-hex signature))]
|
||||
:js-response true
|
||||
:on-success
|
||||
#(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]}
|
||||
[cofx message-id transaction-hash signature]
|
||||
{::json-rpc/call [{:method "wakuext_acceptRequestTransaction"
|
||||
:params [transaction-hash message-id
|
||||
(or (:result (types/json->clj signature))
|
||||
(ethereum/normalized-hex signature))]
|
||||
{::json-rpc/call [{:method "wakuext_acceptRequestTransaction"
|
||||
:params [transaction-hash message-id
|
||||
(or (:result (types/json->clj signature))
|
||||
(ethereum/normalized-hex signature))]
|
||||
:js-response true
|
||||
:on-success
|
||||
#(re-frame/dispatch [:transport/message-sent %])}]})
|
||||
|
||||
(fx/defn transaction-result
|
||||
(rf/defn transaction-result
|
||||
[{:keys [db] :as cofx} result tx-obj]
|
||||
(let [{:keys [on-result symbol amount from]} (get db :signing/tx)]
|
||||
(fx/merge cofx
|
||||
{:db (dissoc db :signing/tx :signing/sign)
|
||||
(rf/merge cofx
|
||||
{:db (dissoc db :signing/tx :signing/sign)
|
||||
:signing/show-transaction-result nil}
|
||||
(prepare-unconfirmed-transaction result tx-obj symbol amount)
|
||||
(check-queue)
|
||||
@@ -320,35 +344,35 @@
|
||||
#(when on-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 [message-id chat-id from] :as tx-obj}]
|
||||
(let [{:keys [on-result symbol amount contract value]} (get db :signing/tx)
|
||||
data (str (get-in db [:multiaccount :public-key])
|
||||
(subs transaction-hash 2))]
|
||||
(fx/merge
|
||||
data (str (get-in db [:multiaccount :public-key])
|
||||
(subs transaction-hash 2))]
|
||||
(rf/merge
|
||||
cofx
|
||||
{:db (dissoc db :signing/tx :signing/sign)}
|
||||
(wallet/watch-tx (get from :address) transaction-hash)
|
||||
(if (keycard.common/keycard-multiaccount? db)
|
||||
(signing.keycard/hash-message
|
||||
{:data data
|
||||
{:data data
|
||||
:on-completed
|
||||
(fn [hash]
|
||||
(re-frame/dispatch
|
||||
[:keycard/sign-message
|
||||
{:tx-hash transaction-hash
|
||||
{:tx-hash transaction-hash
|
||||
:message-id message-id
|
||||
:chat-id chat-id
|
||||
:value value
|
||||
:contract contract
|
||||
:data data}
|
||||
:chat-id chat-id
|
||||
:value value
|
||||
:contract contract
|
||||
:data data}
|
||||
hash]))})
|
||||
(fn [_]
|
||||
{:signing.fx/sign-message
|
||||
{:params {:data data
|
||||
:password hashed-password
|
||||
:account from}
|
||||
{:params {:data data
|
||||
:password hashed-password
|
||||
:account from}
|
||||
:on-completed
|
||||
(fn [res]
|
||||
(re-frame/dispatch
|
||||
@@ -362,7 +386,7 @@
|
||||
#(when on-result
|
||||
{:dispatch (conj on-result transaction-hash)}))))
|
||||
|
||||
(fx/defn transaction-error
|
||||
(rf/defn transaction-error
|
||||
[{:keys [db]} {:keys [code message]}]
|
||||
(let [on-error (get-in db [:signing/tx :on-error])]
|
||||
(if (= code constants/send-transaction-err-decrypt)
|
||||
@@ -373,34 +397,36 @@
|
||||
(when on-error
|
||||
{: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]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{:db (dissoc db :signing/tx :signing/sign)}
|
||||
check-queue))
|
||||
|
||||
(fx/defn sign-message-completed
|
||||
(rf/defn sign-message-completed
|
||||
{:events [:signing/sign-message-completed]}
|
||||
[{:keys [db] :as cofx} 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
|
||||
{:db (update db :signing/sign
|
||||
assoc :error (if (= 5 (:code error))
|
||||
{:db (update db
|
||||
:signing/sign
|
||||
assoc
|
||||
:error (if (= 5 (:code error))
|
||||
(i18n/label :t/wrong-password)
|
||||
(:message error))
|
||||
:in-progress? false)}
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(when-not (= (-> db :signing/sign :type) :pinless)
|
||||
(dissoc-signing-db-entries-and-check-queue))
|
||||
#(when (= (-> db :signing/sign :type) :pinless)
|
||||
{:dispatch-later [{:ms 3000
|
||||
{:dispatch-later [{:ms 3000
|
||||
:dispatch [:signing/dissoc-entries-and-check-queue]}]})
|
||||
#(when on-result
|
||||
{:dispatch (conj on-result result)})))))
|
||||
|
||||
(fx/defn transaction-completed
|
||||
(rf/defn transaction-completed
|
||||
{:events [:signing/transaction-completed]
|
||||
:interceptors [(re-frame/inject-cofx :random-id-generator)]}
|
||||
[cofx response tx-obj hashed-password]
|
||||
@@ -412,15 +438,15 @@
|
||||
(command-transaction-result cofx-in-progress-false result hashed-password tx-obj)
|
||||
(transaction-result cofx-in-progress-false result tx-obj)))))
|
||||
|
||||
(fx/defn discard
|
||||
(rf/defn discard
|
||||
"Discrad transaction signing"
|
||||
{:events [:signing.ui/cancel-is-pressed]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(let [{:keys [on-error]} (get-in db [:signing/tx])]
|
||||
(fx/merge cofx
|
||||
{:db (-> db
|
||||
(assoc-in [:keycard :pin :status] nil)
|
||||
(dissoc :signing/tx :signing/sign))
|
||||
(rf/merge cofx
|
||||
{:db (-> db
|
||||
(assoc-in [:keycard :pin :status] nil)
|
||||
(dissoc :signing/tx :signing/sign))
|
||||
:hide-signing-sheet nil}
|
||||
(check-queue)
|
||||
(keycard.common/hide-connection-sheet)
|
||||
@@ -428,10 +454,11 @@
|
||||
#(when on-error
|
||||
{: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)))))
|
||||
|
||||
(fx/defn sign
|
||||
(rf/defn sign
|
||||
"Signing transaction or message, shows signing sheet
|
||||
tx
|
||||
{: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}"
|
||||
{:events [:signing.ui/sign]}
|
||||
[{:keys [db] :as cofx} tx]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{:db (update db :signing/queue conj (normalize-tx-obj db tx))}
|
||||
(check-queue)))
|
||||
|
||||
(fx/defn sign-transaction-button-clicked-from-chat
|
||||
{:events [:wallet.ui/sign-transaction-button-clicked-from-chat]}
|
||||
(rf/defn sign-transaction-button-clicked-from-chat
|
||||
{:events [:wallet.ui/sign-transaction-button-clicked-from-chat]}
|
||||
[{:keys [db] :as cofx} {:keys [to amount from token]}]
|
||||
(let [{:keys [symbol address]} token
|
||||
amount-hex (str "0x" (status/number-to-hex amount))
|
||||
to-norm (ethereum/normalized-hex (if (string? to) to (:address to)))
|
||||
from-address (:address from)
|
||||
identity (:current-chat-id db)
|
||||
db (dissoc db :wallet/prepare-transaction :signing/edit-fee)]
|
||||
amount-hex (str "0x" (status/number-to-hex amount))
|
||||
to-norm (ethereum/normalized-hex (if (string? to) to (:address to)))
|
||||
from-address (:address from)
|
||||
identity (:current-chat-id db)
|
||||
db (dissoc db :wallet/prepare-transaction :signing/edit-fee)]
|
||||
(if to-norm
|
||||
(fx/merge
|
||||
(rf/merge
|
||||
cofx
|
||||
{:db db}
|
||||
(sign {:tx-obj (if (= symbol :ETH)
|
||||
{:to to-norm
|
||||
:from from-address
|
||||
{:to to-norm
|
||||
:from from-address
|
||||
:chat-id identity
|
||||
:command? true
|
||||
:value amount-hex}
|
||||
:value amount-hex}
|
||||
{:to (ethereum/normalized-hex address)
|
||||
:from from-address
|
||||
:chat-id identity
|
||||
:command? true
|
||||
:data (status/encode-transfer to-norm amount-hex)})}))
|
||||
{:db db
|
||||
{:db db
|
||||
::json-rpc/call
|
||||
[{:method "wakuext_requestAddressForTransaction"
|
||||
:params [(:current-chat-id db)
|
||||
from-address
|
||||
amount
|
||||
(when-not (= symbol :ETH)
|
||||
address)]
|
||||
[{:method "wakuext_requestAddressForTransaction"
|
||||
:params [(:current-chat-id db)
|
||||
from-address
|
||||
amount
|
||||
(when-not (= symbol :ETH)
|
||||
address)]
|
||||
: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
|
||||
{:events [:wallet.ui/sign-transaction-button-clicked-from-request]}
|
||||
(rf/defn sign-transaction-button-clicked-from-request
|
||||
{:events [:wallet.ui/sign-transaction-button-clicked-from-request]}
|
||||
[{:keys [db] :as cofx} {:keys [amount from token]}]
|
||||
(let [{:keys [request-parameters chat-id]} (:wallet/prepare-transaction db)
|
||||
{:keys [symbol address]} token
|
||||
amount-hex (str "0x" (status/number-to-hex amount))
|
||||
to-norm (:address request-parameters)
|
||||
from-address (:address from)]
|
||||
(fx/merge cofx
|
||||
{:keys [symbol address]} token
|
||||
amount-hex (str "0x" (status/number-to-hex amount))
|
||||
to-norm (:address request-parameters)
|
||||
from-address (:address from)]
|
||||
(rf/merge cofx
|
||||
{:db (dissoc db :wallet/prepare-transaction :signing/edit-fee)}
|
||||
(fn [cofx]
|
||||
(sign
|
||||
cofx
|
||||
{:tx-obj (if (= symbol :ETH)
|
||||
{:to to-norm
|
||||
:from from-address
|
||||
{:to to-norm
|
||||
:from from-address
|
||||
:message-id (:id request-parameters)
|
||||
:chat-id chat-id
|
||||
:command? true
|
||||
:value amount-hex}
|
||||
{:to (ethereum/normalized-hex address)
|
||||
:from from-address
|
||||
:command? true
|
||||
:chat-id chat-id
|
||||
:command? true
|
||||
:value amount-hex}
|
||||
{:to (ethereum/normalized-hex address)
|
||||
:from from-address
|
||||
:command? true
|
||||
:message-id (:id request-parameters)
|
||||
:chat-id chat-id
|
||||
:data (status/encode-transfer to-norm amount-hex)})})))))
|
||||
:chat-id chat-id
|
||||
: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]}
|
||||
[{:keys [db] :as cofx} {:keys [to amount from token gas gasPrice maxFeePerGas maxPriorityFeePerGas]}]
|
||||
(let [{:keys [symbol address]} token
|
||||
amount-hex (str "0x" (status/number-to-hex amount))
|
||||
to-norm (ethereum/normalized-hex (if (string? to) to (:address to)))
|
||||
from-address (:address from)]
|
||||
(fx/merge cofx
|
||||
amount-hex (str "0x" (status/number-to-hex amount))
|
||||
to-norm (ethereum/normalized-hex (if (string? to) to (:address to)))
|
||||
from-address (:address from)]
|
||||
(rf/merge cofx
|
||||
{:db (dissoc db :wallet/prepare-transaction :signing/edit-fee)}
|
||||
(sign
|
||||
{:tx-obj (merge (if (eip1559/sync-enabled?)
|
||||
@@ -540,31 +567,33 @@
|
||||
:params [hash]
|
||||
:on-success handler})))
|
||||
|
||||
(fx/defn cancel-transaction-pressed
|
||||
(rf/defn cancel-transaction-pressed
|
||||
{:events [:signing.ui/cancel-transaction-pressed]}
|
||||
[_ hash]
|
||||
{: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]}
|
||||
[_ hash]
|
||||
{: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]}
|
||||
[cofx {:keys [from nonce hash]}]
|
||||
(when (and from nonce hash)
|
||||
(sign cofx {:tx-obj {:from from
|
||||
:to from
|
||||
:nonce nonce
|
||||
:value "0x0"
|
||||
:cancel? true
|
||||
:hash hash}})))
|
||||
(sign cofx
|
||||
{:tx-obj {:from from
|
||||
:to from
|
||||
:nonce nonce
|
||||
:value "0x0"
|
||||
:cancel? true
|
||||
:hash hash}})))
|
||||
|
||||
(fx/defn increase-gas
|
||||
(rf/defn increase-gas
|
||||
{:events [:signing/increase-gas]}
|
||||
[cofx {:keys [from nonce] :as tx}]
|
||||
(when (and from nonce)
|
||||
(sign cofx {:tx-obj (-> tx
|
||||
(select-keys [:from :to :value :input :gas :nonce :hash])
|
||||
(clojure.set/rename-keys {:input :data}))})))
|
||||
(sign cofx
|
||||
{:tx-obj (-> tx
|
||||
(select-keys [:from :to :value :input :gas :nonce :hash])
|
||||
(clojure.set/rename-keys {:input :data}))})))
|
||||
|
||||
+130
-96
@@ -1,15 +1,15 @@
|
||||
(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.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.ethereum.core :as ethereum]
|
||||
[clojure.string :as string]))
|
||||
[status-im.signing.eip1559 :as eip1559]
|
||||
[status-im.utils.money :as money]
|
||||
[taoensso.timbre :as log]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(def min-gas-price-wei ^js (money/bignumber 1))
|
||||
|
||||
@@ -17,19 +17,22 @@
|
||||
|
||||
(defmulti get-error-label-key (fn [type _] type))
|
||||
|
||||
(defmethod get-error-label-key :gasPrice [_ value]
|
||||
(defmethod get-error-label-key :gasPrice
|
||||
[_ value]
|
||||
(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
|
||||
(-> (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
|
||||
(not value) :t/invalid-number
|
||||
(.lt value min-gas-units) :t/wallet-send-min-units
|
||||
(not value) :t/invalid-number
|
||||
(.lt value min-gas-units) :t/wallet-send-min-units
|
||||
(-> value .decimalPlaces pos?) :t/invalid-number))
|
||||
|
||||
(defmethod get-error-label-key :default [_ value]
|
||||
(defmethod get-error-label-key :default
|
||||
[_ value]
|
||||
(when (or (not value)
|
||||
(<= value 0))
|
||||
:t/invalid-number))
|
||||
@@ -40,7 +43,8 @@
|
||||
(money/to-fixed (money/wei->ether (.times gas gasPrice)))
|
||||
"0"))
|
||||
|
||||
(defn edit-max-fee [edit]
|
||||
(defn edit-max-fee
|
||||
[edit]
|
||||
(let [gasPrice (get-in edit [:gasPrice :value-number])
|
||||
gas (get-in edit [:gas :value-number])]
|
||||
(assoc edit :max-fee (calculate-max-fee gas gasPrice))))
|
||||
@@ -50,7 +54,7 @@
|
||||
Wei for gas, and gwei for gas price.
|
||||
Validates them and sets max fee"
|
||||
[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)
|
||||
data (if error-label-key
|
||||
{:value value
|
||||
@@ -69,17 +73,20 @@
|
||||
(def minimum-priority-fee-gwei
|
||||
(money/bignumber 0.3))
|
||||
|
||||
(defn get-suggested-tip [latest-priority-fee]
|
||||
(defn get-suggested-tip
|
||||
[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)
|
||||
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)
|
||||
(money/div suggested-tip-gwei 2)
|
||||
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)]
|
||||
[(if (money/greater-than minimum-priority-fee-gwei current-minimum-fee)
|
||||
current-minimum-fee
|
||||
@@ -89,47 +96,52 @@
|
||||
(def average-priority-fee
|
||||
(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)
|
||||
latest-base-fee (money/wei->gwei
|
||||
(money/bignumber
|
||||
(get db :wallet/current-base-fee)))
|
||||
fee-error (cond
|
||||
(or (:error maxFeePerGas)
|
||||
(:error maxPriorityFeePerGas))
|
||||
nil
|
||||
latest-base-fee (money/wei->gwei
|
||||
(money/bignumber
|
||||
(get db :wallet/current-base-fee)))
|
||||
fee-error (cond
|
||||
(or (:error maxFeePerGas)
|
||||
(:error maxPriorityFeePerGas))
|
||||
nil
|
||||
|
||||
(money/greater-than latest-base-fee
|
||||
(:value-number maxFeePerGas))
|
||||
{:label (i18n/label :t/below-base-fee)
|
||||
:severity :error})]
|
||||
(money/greater-than latest-base-fee
|
||||
(:value-number maxFeePerGas))
|
||||
{:label (i18n/label :t/below-base-fee)
|
||||
:severity :error})]
|
||||
(if fee-error
|
||||
(assoc-in db [:signing/edit-fee :maxFeePerGas :fee-error] 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)
|
||||
latest-priority-fee (get db :wallet/current-priority-fee)
|
||||
fee-error (cond
|
||||
(:error maxPriorityFeePerGas)
|
||||
nil
|
||||
latest-priority-fee (get db :wallet/current-priority-fee)
|
||||
fee-error (cond
|
||||
(:error maxPriorityFeePerGas)
|
||||
nil
|
||||
|
||||
(money/greater-than (get-minimum-priority-fee
|
||||
(money/div
|
||||
(money/wei->gwei (money/bignumber latest-priority-fee)) 2))
|
||||
(:value-number maxPriorityFeePerGas))
|
||||
{:label (i18n/label :t/low-tip)
|
||||
:severity :error}
|
||||
(money/greater-than (get-minimum-priority-fee
|
||||
(money/div
|
||||
(money/wei->gwei (money/bignumber
|
||||
latest-priority-fee))
|
||||
2))
|
||||
(:value-number maxPriorityFeePerGas))
|
||||
{:label (i18n/label :t/low-tip)
|
||||
:severity :error}
|
||||
|
||||
#_(money/greater-than average-priority-fee
|
||||
(:value-number maxPriorityFeePerGas))
|
||||
#_{:label (i18n/label :t/lower-than-average-tip)
|
||||
:severity :error})]
|
||||
#_(money/greater-than average-priority-fee
|
||||
(:value-number maxPriorityFeePerGas))
|
||||
#_{:label (i18n/label :t/lower-than-average-tip)
|
||||
:severity :error})]
|
||||
(if fee-error
|
||||
(assoc-in db [:signing/edit-fee :maxPriorityFeePerGas :fee-error] 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?)
|
||||
(reduce
|
||||
(fn [acc f]
|
||||
@@ -139,7 +151,7 @@
|
||||
validate-max-priority-fee])
|
||||
db))
|
||||
|
||||
(fx/defn edit-value
|
||||
(rf/defn edit-value
|
||||
{:events [:signing.edit-fee.ui/edit-value]}
|
||||
[{:keys [db]} key value]
|
||||
{:db (-> db
|
||||
@@ -147,7 +159,8 @@
|
||||
(update :signing/edit-fee build-edit key value)
|
||||
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)
|
||||
normal-bn (money/bignumber normal)
|
||||
slow-bn (money/bignumber slow)
|
||||
@@ -165,7 +178,7 @@
|
||||
:tip 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]}
|
||||
[{:keys [db] :as cofx} option]
|
||||
(let [tip (get db :wallet/current-priority-fee)
|
||||
@@ -176,21 +189,23 @@
|
||||
{:db (-> db
|
||||
(assoc-in [:signing/edit-fee :selected-fee-option] option)
|
||||
(update :signing/edit-fee
|
||||
build-edit :maxFeePerGas (money/wei->gwei fee))
|
||||
build-edit
|
||||
:maxFeePerGas (money/wei->gwei 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]}
|
||||
[{:keys [db]} value]
|
||||
(let [{:keys [maxFeePerGas maxPriorityFeePerGas]}
|
||||
(get db :signing/edit-fee)
|
||||
latest-base-fee (get db :wallet/current-base-fee)
|
||||
max-fee-value (:value-number maxFeePerGas)
|
||||
max-priority-fee-value (:value-number maxPriorityFeePerGas)
|
||||
new-value (money/bignumber value)
|
||||
fee-without-tip (money/sub max-fee-value max-priority-fee-value)
|
||||
base-fee (money/wei->gwei (money/bignumber latest-base-fee))
|
||||
latest-base-fee (get db :wallet/current-base-fee)
|
||||
max-fee-value (:value-number maxFeePerGas)
|
||||
max-priority-fee-value (:value-number maxPriorityFeePerGas)
|
||||
new-value (money/bignumber value)
|
||||
fee-without-tip (money/sub max-fee-value max-priority-fee-value)
|
||||
base-fee (money/wei->gwei (money/bignumber latest-base-fee))
|
||||
new-max-fee-value
|
||||
(money/to-fixed
|
||||
(if (money/greater-than base-fee fee-without-tip)
|
||||
@@ -201,19 +216,21 @@
|
||||
(update :signing/edit-fee build-edit :maxFeePerGas new-max-fee-value)
|
||||
validate-eip1559-fees)}))
|
||||
|
||||
(fx/defn update-estimated-gas-success
|
||||
(rf/defn update-estimated-gas-success
|
||||
{:events [:signing/update-estimated-gas-success]}
|
||||
[{db :db} gas]
|
||||
{:db (-> db
|
||||
(assoc-in [:signing/tx :gas] gas)
|
||||
(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]}
|
||||
[{db :db} price]
|
||||
(if (eip1559/sync-enabled?)
|
||||
(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
|
||||
(assoc-in [:signing/tx :maxFeePerGas]
|
||||
(money/to-hex (money/add max-priority-fee-bn
|
||||
@@ -225,7 +242,7 @@
|
||||
(assoc-in [:signing/tx :gasPrice] price)
|
||||
(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]}
|
||||
[{db :db} {:keys [message]}]
|
||||
(log/warn "signing/update-estimated-gas-error" message)
|
||||
@@ -233,28 +250,36 @@
|
||||
(assoc-in [:signing/edit-fee :gas-loading?] false)
|
||||
(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]}
|
||||
[{db :db}]
|
||||
{: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]}
|
||||
[{{:signing/keys [tx] :as db} :db :as cofx} sheet-opts]
|
||||
(let [{:keys [gas gasPrice maxFeePerGas maxPriorityFeePerGas]} tx
|
||||
max-fee (money/to-fixed (money/wei->gwei maxFeePerGas))
|
||||
max-priority-fee (money/to-fixed (money/wei->gwei maxPriorityFeePerGas))
|
||||
edit-fee (reduce (partial apply build-edit)
|
||||
{:selected-fee-option (get-in db [: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})]
|
||||
(fx/merge cofx
|
||||
max-fee (money/to-fixed (money/wei->gwei
|
||||
maxFeePerGas))
|
||||
max-priority-fee (money/to-fixed (money/wei->gwei
|
||||
maxPriorityFeePerGas))
|
||||
edit-fee (reduce
|
||||
(partial apply build-edit)
|
||||
{:selected-fee-option
|
||||
(get-in db
|
||||
[: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)}
|
||||
(bottom-sheet/show-bottom-sheet {:view sheet-opts}))))
|
||||
|
||||
(fx/defn submit-fee
|
||||
(rf/defn submit-fee
|
||||
{:events [:signing.edit-fee.ui/submit]}
|
||||
[{{:signing/keys [edit-fee] :as db} :db :as cofx} force?]
|
||||
(let [{:keys [gas gasPrice maxFeePerGas maxPriorityFeePerGas]} edit-fee
|
||||
@@ -267,18 +292,20 @@
|
||||
(if (and (seq errors?)
|
||||
(not force?))
|
||||
(popover/show-popover cofx {:view :fees-warning})
|
||||
(fx/merge cofx
|
||||
{:db (update db :signing/tx assoc
|
||||
:gas (:value-number gas)
|
||||
:gasPrice (:value-number gasPrice)
|
||||
:maxFeePerGas (money/->wei :gwei (:value-number maxFeePerGas))
|
||||
:maxPriorityFeePerGas (money/->wei :gwei (:value-number maxPriorityFeePerGas)))}
|
||||
(rf/merge cofx
|
||||
{:db (update db
|
||||
:signing/tx assoc
|
||||
:gas (:value-number gas)
|
||||
:gasPrice (:value-number gasPrice)
|
||||
:maxFeePerGas (money/->wei :gwei (:value-number maxFeePerGas))
|
||||
:maxPriorityFeePerGas (money/->wei :gwei
|
||||
(:value-number maxPriorityFeePerGas)))}
|
||||
(bottom-sheet/hide-bottom-sheet)))))
|
||||
|
||||
(fx/defn submit-nonce
|
||||
(rf/defn submit-nonce
|
||||
{:events [:signing.nonce/submit]}
|
||||
[{db :db :as cofx} nonce]
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{:db (assoc-in db [:signing/tx :nonce] (if (string/blank? nonce) nil nonce))}
|
||||
(bottom-sheet/hide-bottom-sheet)))
|
||||
|
||||
@@ -289,10 +316,10 @@
|
||||
network-id
|
||||
(fn []
|
||||
(json-rpc/call
|
||||
{:method "eth_feeHistory"
|
||||
{:method "eth_feeHistory"
|
||||
;; NOTE(rasom): We don't need `reward` atm but if the last parameter is
|
||||
;; `nil` request fails on some chains (particularly on xDai).
|
||||
:params [101 "latest" [100]]
|
||||
:params [101 "latest" [100]]
|
||||
:on-success #(re-frame/dispatch [::header-fetched
|
||||
(assoc params :fee-history %)])}))
|
||||
(fn []
|
||||
@@ -303,7 +330,8 @@
|
||||
|
||||
(def london-block-gas-limit (money/bignumber 30000000))
|
||||
|
||||
(defn calc-percentiles [v ps]
|
||||
(defn calc-percentiles
|
||||
[v ps]
|
||||
(let [sorted-v (sort-by
|
||||
identity
|
||||
(fn [a b]
|
||||
@@ -319,7 +347,8 @@
|
||||
;; fee might be very small and using this value might slow transaction
|
||||
(def minimum-base-fee (money/->wei :gwei (money/bignumber 1)))
|
||||
|
||||
(defn recommended-base-fee [current perc20]
|
||||
(defn recommended-base-fee
|
||||
[current perc20]
|
||||
(let [fee
|
||||
(cond (money/greater-than-or-equals current perc20)
|
||||
current
|
||||
@@ -332,13 +361,15 @@
|
||||
|
||||
(defn slow-base-fee [_ perc10] perc10)
|
||||
|
||||
(defn fast-base-fee [current]
|
||||
(defn fast-base-fee
|
||||
[current]
|
||||
(let [fee (money/mul current 2)]
|
||||
(if (money/greater-than minimum-base-fee fee)
|
||||
(money/mul minimum-base-fee 2)
|
||||
fee)))
|
||||
|
||||
(defn check-base-fee [{:keys [baseFeePerGas testnet?]}]
|
||||
(defn check-base-fee
|
||||
[{:keys [baseFeePerGas testnet?]}]
|
||||
(let [all-base-fees (mapv money/bignumber baseFeePerGas)
|
||||
next-base-fee (peek all-base-fees)
|
||||
previous-fees (subvec all-base-fees 0 101)
|
||||
@@ -357,12 +388,13 @@
|
||||
:fast-base-fee (money/to-hex (fast-base-fee next-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/sub (money/bignumber gas-price)
|
||||
(money/bignumber base-fee))))
|
||||
|
||||
(fx/defn header-fetched
|
||||
(rf/defn header-fetched
|
||||
{:events [::header-fetched]}
|
||||
[{{:networks/keys [current-network networks]} :db}
|
||||
{:keys [error-callback success-callback fee-history] :as params}]
|
||||
@@ -375,8 +407,9 @@
|
||||
(let [{:keys [current-base-fee] :as base-fees}
|
||||
(check-base-fee
|
||||
(assoc fee-history
|
||||
:testnet? (ethereum/testnet?
|
||||
(get-in networks [current-network :config :NetworkId]))))]
|
||||
:testnet?
|
||||
(ethereum/testnet?
|
||||
(get-in networks [current-network :config :NetworkId]))))]
|
||||
(merge {:max-priority-fee
|
||||
(max-priority-fee-hex (money/bignumber %) current-base-fee)}
|
||||
base-fees)))
|
||||
@@ -390,5 +423,6 @@
|
||||
(json-rpc/call
|
||||
{:method "eth_estimateGas"
|
||||
: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 %])})))
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
(ns status-im.signing.keycard
|
||||
(: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.native-module.core :as status]
|
||||
[status-im.utils.types :as types]
|
||||
[taoensso.timbre :as log]))
|
||||
[taoensso.timbre :as log]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(re-frame/reg-fx
|
||||
::hash-transaction
|
||||
@@ -27,7 +27,7 @@
|
||||
[{:keys [gas gasPrice data nonce tx-obj] :as params}]
|
||||
(let [{:keys [from to value chat-id message-id command? maxPriorityFeePerGas maxFeePerGas]} tx-obj
|
||||
maxPriorityFeePerGas (or maxPriorityFeePerGas (get params :maxPriorityFeePerGas))
|
||||
maxFeePerGas (or maxFeePerGas (get params :maxFeePerGas))]
|
||||
maxFeePerGas (or maxFeePerGas (get params :maxFeePerGas))]
|
||||
(cond-> {:from from
|
||||
:to to
|
||||
:value value
|
||||
@@ -35,11 +35,15 @@
|
||||
:message-id message-id
|
||||
:command? command?}
|
||||
maxPriorityFeePerGas
|
||||
(assoc :maxPriorityFeePerGas (str "0x" (status/number-to-hex
|
||||
(js/parseInt maxPriorityFeePerGas))))
|
||||
(assoc :maxPriorityFeePerGas
|
||||
(str "0x"
|
||||
(status/number-to-hex
|
||||
(js/parseInt maxPriorityFeePerGas))))
|
||||
maxFeePerGas
|
||||
(assoc :maxFeePerGas (str "0x" (status/number-to-hex
|
||||
(js/parseInt maxFeePerGas))))
|
||||
(assoc :maxFeePerGas
|
||||
(str "0x"
|
||||
(status/number-to-hex
|
||||
(js/parseInt maxFeePerGas))))
|
||||
gas
|
||||
(assoc :gas (str "0x" (status/number-to-hex gas)))
|
||||
gasPrice
|
||||
@@ -49,7 +53,7 @@
|
||||
nonce
|
||||
(assoc :nonce nonce))))
|
||||
|
||||
(fx/defn hash-message
|
||||
(rf/defn hash-message
|
||||
[_ {:keys [v4 data typed? on-completed]}]
|
||||
(if typed?
|
||||
{::hash-typed-data
|
||||
@@ -68,7 +72,7 @@
|
||||
[:signing.keycard.callback/hash-message-completed
|
||||
data typed? %]))}}))
|
||||
|
||||
(fx/defn hash-message-completed
|
||||
(rf/defn hash-message-completed
|
||||
{:events [:signing.keycard.callback/hash-message-completed]}
|
||||
[{:keys [db]} data typed? result]
|
||||
(let [{:keys [result error]} (types/json->clj result)]
|
||||
@@ -76,12 +80,13 @@
|
||||
{:dispatch [:signing.ui/cancel-is-pressed]
|
||||
:utils/show-popup {:title (i18n/label :t/sign-request-failed)
|
||||
:content (:message error)}}
|
||||
{:db (update db :keycard assoc
|
||||
:hash result
|
||||
:typed? typed?
|
||||
:data data)})))
|
||||
{:db (update db
|
||||
:keycard assoc
|
||||
:hash result
|
||||
:typed? typed?
|
||||
:data data)})))
|
||||
|
||||
(fx/defn hash-transaction
|
||||
(rf/defn hash-transaction
|
||||
[{:keys [db]}]
|
||||
(let [tx (prepare-transaction (:signing/tx db))]
|
||||
(log/debug "hash-transaction" tx)
|
||||
@@ -90,7 +95,7 @@
|
||||
:on-completed #(re-frame/dispatch
|
||||
[:signing.keycard.callback/hash-transaction-completed tx %])}}))
|
||||
|
||||
(fx/defn hash-transaction-completed
|
||||
(rf/defn hash-transaction-completed
|
||||
{:events [:signing.keycard.callback/hash-transaction-completed]}
|
||||
[{:keys [db]} original-tx result]
|
||||
(let [{:keys [transaction hash]} (:result (types/json->clj result))]
|
||||
@@ -99,11 +104,11 @@
|
||||
(merge original-tx transaction))
|
||||
(assoc-in [:keycard :hash] hash))}))
|
||||
|
||||
(fx/defn sign-with-keycard
|
||||
(rf/defn sign-with-keycard
|
||||
{:events [:signing.ui/sign-with-keycard-pressed]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(let [{:keys [message maxPriorityFeePerGas maxFeePerGas]} (get db :signing/tx)]
|
||||
(fx/merge
|
||||
(rf/merge
|
||||
cofx
|
||||
{:db (-> db
|
||||
(assoc-in [:keycard :pin :enter-step] :sign)
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
(ns status-im.stickers.core
|
||||
(:require [clojure.string :as string]
|
||||
[re-frame.core :as re-frame]
|
||||
[status-im.constants :as constants]
|
||||
[status-im.ethereum.core :as ethereum]
|
||||
[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.constants :as constants]))
|
||||
[status-im.utils.utils :as utils]
|
||||
[status-im2.navigation.events :as navigation]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(re-frame/reg-fx
|
||||
:stickers/set-pending-timeout-fx
|
||||
@@ -15,17 +15,17 @@
|
||||
(utils/set-timeout #(re-frame/dispatch [:stickers/pending-timeout])
|
||||
10000)))
|
||||
|
||||
(fx/defn install-stickers-pack
|
||||
(rf/defn install-stickers-pack
|
||||
{:events [:stickers/install-pack]}
|
||||
[{db :db :as cofx} id]
|
||||
(fx/merge
|
||||
(rf/merge
|
||||
cofx
|
||||
{:db (assoc-in db [:stickers/packs id :status] constants/sticker-pack-status-installed)
|
||||
::json-rpc/call [{:method "stickers_install"
|
||||
:params [(ethereum/chain-id db) id]
|
||||
:on-success #()}]}))
|
||||
|
||||
(fx/defn load-packs
|
||||
(rf/defn load-packs
|
||||
{:events [:stickers/load-packs]}
|
||||
[{:keys [db]}]
|
||||
{::json-rpc/call [{:method "stickers_market"
|
||||
@@ -41,7 +41,7 @@
|
||||
:params []
|
||||
:on-success #(re-frame/dispatch [:stickers/stickers-recent-success %])}]})
|
||||
|
||||
(fx/defn buy-pack
|
||||
(rf/defn buy-pack
|
||||
{:events [:stickers/buy-pack]}
|
||||
[{db :db} pack-id]
|
||||
{::json-rpc/call [{:method "stickers_buyPrepareTx"
|
||||
@@ -50,34 +50,36 @@
|
||||
{:tx-obj %
|
||||
:on-result [:stickers/pending-pack pack-id]}])}]})
|
||||
|
||||
(fx/defn pending-pack
|
||||
(rf/defn pending-pack
|
||||
{:events [:stickers/pending-pack]}
|
||||
[{db :db} id]
|
||||
{: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))
|
||||
:stickers/set-pending-timeout-fx nil
|
||||
::json-rpc/call [{:method "stickers_addPending"
|
||||
:params [(ethereum/chain-id db) (int id)]
|
||||
:on-success #()}]})
|
||||
|
||||
(fx/defn pending-timeout
|
||||
(rf/defn pending-timeout
|
||||
{:events [:stickers/pending-timeout]}
|
||||
[{{:stickers/keys [packs-pending] :as db} :db}]
|
||||
(when (seq packs-pending)
|
||||
{::json-rpc/call [{:method "stickers_processPending"
|
||||
: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]}
|
||||
[{{:stickers/keys [packs-pending packs] :as db} :db} purchased]
|
||||
(let [purchased-ids (map :id (vals purchased))
|
||||
packs-pending (apply disj packs-pending purchased-ids)
|
||||
packs (reduce (fn [packs id]
|
||||
(assoc-in packs [id :status] constants/sticker-pack-status-owned))
|
||||
packs
|
||||
purchased-ids)]
|
||||
packs (reduce (fn [packs id]
|
||||
(assoc-in packs [id :status] constants/sticker-pack-status-owned))
|
||||
packs
|
||||
purchased-ids)]
|
||||
(merge
|
||||
{:db (-> db
|
||||
(assoc :stickers/packs packs)
|
||||
@@ -85,19 +87,19 @@
|
||||
(when (seq packs-pending)
|
||||
{:stickers/set-pending-timeout-fx nil}))))
|
||||
|
||||
(fx/defn stickers-market-success
|
||||
(rf/defn stickers-market-success
|
||||
{:events [:stickers/stickers-market-success]}
|
||||
[{:keys [db]} packs]
|
||||
(let [packs (reduce (fn [acc pack] (assoc acc (:id pack) pack)) {} packs)]
|
||||
{:db (update db :stickers/packs merge packs)}))
|
||||
|
||||
(fx/defn stickers-installed-success
|
||||
(rf/defn stickers-installed-success
|
||||
{:events [:stickers/stickers-installed-success]}
|
||||
[{:keys [db]} packs]
|
||||
(let [packs (reduce (fn [acc [_ pack]] (assoc acc (:id pack) pack)) {} packs)]
|
||||
{:db (update db :stickers/packs merge packs)}))
|
||||
|
||||
(fx/defn stickers-pending-success
|
||||
(rf/defn stickers-pending-success
|
||||
{:events [:stickers/stickers-pending-success]}
|
||||
[{:keys [db]} packs]
|
||||
(let [packs (reduce (fn [acc [_ pack]] (assoc acc (:id pack) pack)) {} packs)]
|
||||
@@ -108,18 +110,18 @@
|
||||
(when (seq packs)
|
||||
{:stickers/set-pending-timeout-fx nil}))))
|
||||
|
||||
(fx/defn stickers-recent-success
|
||||
(rf/defn stickers-recent-success
|
||||
{:events [:stickers/stickers-recent-success]}
|
||||
[{:keys [db]} packs]
|
||||
{:db (assoc db :stickers/recent-stickers packs)})
|
||||
|
||||
(fx/defn open-sticker-pack
|
||||
(rf/defn open-sticker-pack
|
||||
{:events [:stickers/open-sticker-pack]}
|
||||
[{{:networks/keys [current-network]} :db :as cofx} id]
|
||||
(when (and id (or config/stickers-test-enabled? (string/starts-with? current-network "mainnet")))
|
||||
(navigation/open-modal cofx :stickers-pack {:id id})))
|
||||
|
||||
(fx/defn select-pack
|
||||
(rf/defn select-pack
|
||||
{:events [:stickers/select-pack]}
|
||||
[{:keys [db]} id]
|
||||
{:db (assoc db :stickers/selected-pack id)})
|
||||
|
||||
@@ -1,25 +1,30 @@
|
||||
(ns status-im.test-runner
|
||||
{:dev/always true}
|
||||
(:require
|
||||
[cljs.test :as ct]
|
||||
[clojure.string :as string]
|
||||
[shadow.test :as st]
|
||||
[shadow.test.env :as env]))
|
||||
(:require [cljs.test :as ct]
|
||||
[clojure.string :as string]
|
||||
[shadow.test :as st]
|
||||
[shadow.test.env :as env]
|
||||
[status-im.ethereum.json-rpc :as json-rpc]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(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?
|
||||
(if (ct/successful? m)
|
||||
(js/process.exit 0)
|
||||
(js/process.exit 1))))
|
||||
|
||||
;; get-test-data is a macro so this namespace REQUIRES :dev/always hint ns so that it is always recompiled
|
||||
(defn ^:dev/after-load reset-test-data! []
|
||||
;; get-test-data is a macro so this namespace REQUIRES :dev/always hint ns so that it is always
|
||||
;; recompiled
|
||||
(defn ^:dev/after-load reset-test-data!
|
||||
[]
|
||||
(-> (env/get-test-data)
|
||||
(env/reset-test-data!)))
|
||||
|
||||
(defn parse-args [args]
|
||||
(defn parse-args
|
||||
[args]
|
||||
(reduce
|
||||
(fn [opts arg]
|
||||
(cond
|
||||
@@ -33,7 +38,7 @@
|
||||
(assoc opts :repl true)
|
||||
|
||||
(string/starts-with? arg "--test=")
|
||||
(let [test-arg (subs arg 7)
|
||||
(let [test-arg (subs arg 7)
|
||||
test-syms
|
||||
(->> (string/split test-arg ",")
|
||||
(map symbol))]
|
||||
@@ -45,12 +50,17 @@
|
||||
{:test-syms []}
|
||||
args))
|
||||
|
||||
(defn find-matching-test-vars [test-syms]
|
||||
(defn find-matching-test-vars
|
||||
[test-syms]
|
||||
;; FIXME: should have some kind of wildcard support
|
||||
(let [test-namespaces
|
||||
(->> test-syms (filter simple-symbol?) (set))
|
||||
(->> test-syms
|
||||
(filter simple-symbol?)
|
||||
(set))
|
||||
test-var-syms
|
||||
(->> test-syms (filter qualified-symbol?) (set))]
|
||||
(->> test-syms
|
||||
(filter qualified-symbol?)
|
||||
(set))]
|
||||
|
||||
(->> (env/get-test-vars)
|
||||
(filter (fn [the-var]
|
||||
@@ -58,7 +68,8 @@
|
||||
(or (contains? test-namespaces ns)
|
||||
(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
|
||||
(-> (ct/empty-env)
|
||||
;; can't think of a proper way to let CLI specify custom reporter?
|
||||
@@ -70,17 +81,19 @@
|
||||
|
||||
(cond
|
||||
help
|
||||
(do (println "Usage:")
|
||||
(println " --list (list known test names)")
|
||||
(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)"))
|
||||
(do
|
||||
(println "Usage:")
|
||||
(println " --list (list known test names)")
|
||||
(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
|
||||
(doseq [[ns ns-info]
|
||||
(->> (env/get-tests)
|
||||
(sort-by first))]
|
||||
(println "Namespace:" ns)
|
||||
(doseq [var (:vars ns-info)
|
||||
(doseq [var (:vars ns-info)
|
||||
:let [m (meta var)]]
|
||||
(println (str " " (:ns m) "/" (:name m))))
|
||||
(println "---------------------------------"))
|
||||
@@ -97,8 +110,20 @@
|
||||
:else
|
||||
(st/run-all-tests test-env nil))))
|
||||
|
||||
(defn ^:export main [& args]
|
||||
(defn ^:export main
|
||||
[& args]
|
||||
(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)]
|
||||
(execute-cli opts)))
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
(ns ^{:doc "API to init and stop whisper messaging"}
|
||||
status-im.transport.core
|
||||
(ns ^{:doc "API to init and stop whisper messaging"} status-im.transport.core
|
||||
(:require [re-frame.core :as re-frame]
|
||||
[status-im.ethereum.json-rpc :as json-rpc]
|
||||
[status-im.pairing.core :as pairing]
|
||||
[status-im.utils.fx :as fx]
|
||||
[status-im.stickers.core :as stickers]
|
||||
status-im.transport.shh
|
||||
[taoensso.timbre :as log]
|
||||
[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]}
|
||||
[{:keys [db]} node-info]
|
||||
{:db (assoc db :node-info node-info)})
|
||||
|
||||
(fx/defn fetch-node-info-fx [_]
|
||||
{::json-rpc/call [{:method "admin_nodeInfo"
|
||||
(rf/defn fetch-node-info-fx
|
||||
[_]
|
||||
{::json-rpc/call [{:method "admin_nodeInfo"
|
||||
: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
|
||||
[db mailservers]
|
||||
@@ -28,25 +28,25 @@
|
||||
(assoc :name (if (seq name) name id))
|
||||
(dissoc :fleet))]
|
||||
(assoc-in db
|
||||
[:mailserver/mailservers (keyword fleet) (keyword id)]
|
||||
updated-mailserver)))
|
||||
[:mailserver/mailservers (keyword fleet) (keyword id)]
|
||||
updated-mailserver)))
|
||||
db
|
||||
mailservers))
|
||||
|
||||
(fx/defn start-messenger
|
||||
(rf/defn start-messenger
|
||||
"We should only start receiving messages/processing topics once all the
|
||||
initializiation is completed, otherwise we might receive messages/topics
|
||||
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-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]}
|
||||
[{:keys [db] :as cofx} {:keys [mailservers] :as response}]
|
||||
(log/info "Messenger started")
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{:db (-> db
|
||||
(assoc :messenger/started? true)
|
||||
(add-mailservers mailservers))}
|
||||
|
||||
@@ -1,37 +1,36 @@
|
||||
(ns ^{:doc "Definition of the StatusMessage protocol"}
|
||||
status-im.transport.message.core
|
||||
(: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]
|
||||
(ns ^{:doc "Definition of the StatusMessage protocol"} status-im.transport.message.core
|
||||
(:require [clojure.string :as string]
|
||||
[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]
|
||||
(if sync-handler
|
||||
(sync-handler cofx response-js true)
|
||||
{:utils/dispatch-later [{:ms 20 :dispatch [:process-response response-js]}]}))
|
||||
|
||||
(fx/defn process-response
|
||||
(rf/defn process-response
|
||||
{:events [:process-response]}
|
||||
[{:keys [db] :as cofx} ^js response-js process-async]
|
||||
(let [^js communities (.-communities response-js)
|
||||
@@ -59,7 +58,7 @@
|
||||
(seq chats)
|
||||
(do
|
||||
(js-delete response-js "chats")
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(process-next response-js sync-handler)
|
||||
(models.chat/ensure-chats (map data-store.chats/<-rpc (types/js->clj chats)))))
|
||||
|
||||
@@ -69,7 +68,7 @@
|
||||
(seq activity-notifications)
|
||||
(do
|
||||
(js-delete response-js "activityCenterNotifications")
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(->> activity-notifications
|
||||
types/js->clj
|
||||
(map data-store.activities/<-rpc)
|
||||
@@ -79,86 +78,87 @@
|
||||
(seq installations)
|
||||
(let [installations-clj (types/js->clj installations)]
|
||||
(js-delete response-js "installations")
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(process-next response-js sync-handler)
|
||||
(models.pairing/handle-installations installations-clj)))
|
||||
|
||||
(seq 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 "chatsForContacts")
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(process-next response-js sync-handler)
|
||||
(models.contact/ensure-contacts
|
||||
(map data-store.contacts/<-rpc contacts-clj) chats)))
|
||||
(map data-store.contacts/<-rpc contacts-clj)
|
||||
chats)))
|
||||
|
||||
(seq communities)
|
||||
(let [communities-clj (types/js->clj communities)]
|
||||
(js-delete response-js "communities")
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(process-next response-js sync-handler)
|
||||
(models.communities/handle-communities communities-clj)))
|
||||
|
||||
(seq bookmarks)
|
||||
(let [bookmarks-clj (types/js->clj bookmarks)]
|
||||
(js-delete response-js "bookmarks")
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(process-next response-js sync-handler)
|
||||
(browser/handle-bookmarks bookmarks-clj)))
|
||||
|
||||
(seq pin-messages)
|
||||
(let [pin-messages (types/js->clj pin-messages)]
|
||||
(js-delete response-js "pinMessages")
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(process-next response-js sync-handler)
|
||||
(messages.pin/receive-signal (map data-store.messages/<-rpc pin-messages))))
|
||||
|
||||
(seq removed-chats)
|
||||
(let [removed-chats-clj (types/js->clj removed-chats)]
|
||||
(js-delete response-js "removedChats")
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(process-next response-js sync-handler)
|
||||
(models.communities/handle-removed-chats removed-chats-clj)))
|
||||
|
||||
(seq 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)
|
||||
(models.communities/handle-request-to-join request-js)))
|
||||
|
||||
(seq emoji-reactions)
|
||||
(let [reactions (types/js->clj emoji-reactions)]
|
||||
(js-delete response-js "emojiReactions")
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(process-next response-js sync-handler)
|
||||
(models.reactions/receive-signal (map data-store.reactions/<-rpc reactions))))
|
||||
|
||||
(seq invitations)
|
||||
(let [invitations (types/js->clj invitations)]
|
||||
(js-delete response-js "invitations")
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(process-next response-js sync-handler)
|
||||
(models.group/handle-invitations (map data-store.invitations/<-rpc invitations))))
|
||||
|
||||
(seq removed-messages)
|
||||
(let [removed-messages-clj (types/js->clj removed-messages)]
|
||||
(js-delete response-js "removedMessages")
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(process-next response-js sync-handler)
|
||||
(models.message/handle-removed-messages removed-messages-clj)))
|
||||
|
||||
(seq cleared-histories)
|
||||
(let [cleared-histories-clj (types/js->clj cleared-histories)]
|
||||
(js-delete response-js "clearedHistories")
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(process-next response-js sync-handler)
|
||||
(models.message/handle-cleared-histories-messages cleared-histories-clj)))
|
||||
|
||||
(seq visibility-status-updates)
|
||||
(let [visibility-status-updates-clj (types/js->clj visibility-status-updates)]
|
||||
(js-delete response-js "statusUpdates")
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(process-next response-js sync-handler)
|
||||
(models.visibility-status-updates/handle-visibility-status-updates
|
||||
visibility-status-updates-clj)))
|
||||
@@ -166,28 +166,28 @@
|
||||
(seq accounts)
|
||||
(do
|
||||
(js-delete response-js "accounts")
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(process-next response-js sync-handler)
|
||||
(multiaccounts.login/update-wallet-accounts (types/js->clj accounts))))
|
||||
|
||||
(seq settings)
|
||||
(do
|
||||
(js-delete response-js "settings")
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(process-next response-js sync-handler)
|
||||
(update.core/set-many-js settings)))
|
||||
|
||||
(seq identity-images)
|
||||
(let [images-clj (map types/js->clj identity-images)]
|
||||
(js-delete response-js "identityImages")
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(process-next response-js sync-handler)
|
||||
(update.core/optimistic :images images-clj)))
|
||||
|
||||
(some? current-visibility-status)
|
||||
(let [current-visibility-status-clj (types/js->clj current-visibility-status)]
|
||||
(js-delete response-js "currentStatus")
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
(process-next response-js sync-handler)
|
||||
(models.visibility-status-updates/sync-visibility-status-update
|
||||
current-visibility-status-clj))))))
|
||||
@@ -195,19 +195,21 @@
|
||||
(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"
|
||||
[{:keys [current-chat-id db] :as acc} ^js message-js]
|
||||
(let [chat-id (.-localChatId message-js)
|
||||
message-type (.-messageType message-js)
|
||||
from (.-from message-js)
|
||||
mentioned (.-mentioned message-js)
|
||||
profile (models.chat/profile-chat? {:db db} chat-id)
|
||||
new (.-new message-js)
|
||||
current (= current-chat-id chat-id)
|
||||
(let [chat-id (.-localChatId message-js)
|
||||
message-type (.-messageType message-js)
|
||||
from (.-from message-js)
|
||||
mentioned (.-mentioned message-js)
|
||||
profile (models.chat/profile-chat? {:db db} chat-id)
|
||||
new (.-new message-js)
|
||||
current (= current-chat-id chat-id)
|
||||
should-update-unviewed? (and (not current)
|
||||
new
|
||||
(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}))))
|
||||
tx-hash (and (.-commandParameters message-js) (.-commandParameters.transactionHash message-js))]
|
||||
tx-hash (and (.-commandParameters message-js)
|
||||
(.-commandParameters.transactionHash message-js))]
|
||||
(cond-> acc
|
||||
current
|
||||
(update :messages conj message-js)
|
||||
@@ -235,46 +237,52 @@
|
||||
[response-js messages]
|
||||
(if (seq messages)
|
||||
(set! (.-messages response-js)
|
||||
(.sort (to-array messages)
|
||||
(fn [a b]
|
||||
(- (.-clock b) (.-clock a)))))
|
||||
(.sort (to-array messages)
|
||||
(fn [a b]
|
||||
(- (.-clock b) (.-clock a)))))
|
||||
(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"
|
||||
{:events [:sanitize-messages-and-process-response]}
|
||||
[{:keys [db] :as cofx} ^js response-js process-async]
|
||||
(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]}
|
||||
(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}
|
||||
(.-messages response-js))]
|
||||
(sort-js-messages! response-js messages)
|
||||
(fx/merge cofx
|
||||
{:db db
|
||||
(rf/merge cofx
|
||||
{:db db
|
||||
:utils/dispatch-later (concat []
|
||||
(when (seq statuses)
|
||||
[{:ms 100 :dispatch [:process-statuses statuses]}])
|
||||
(when (seq 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)))))
|
||||
|
||||
(fx/defn remove-hash
|
||||
(rf/defn remove-hash
|
||||
[{:keys [db]} 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]
|
||||
(when-let [{:keys [pending-confirmations not-sent]}
|
||||
(get-in db [:transport/message-ids->confirmations message-id])]
|
||||
(if (zero? (dec pending-confirmations))
|
||||
(fx/merge cofx
|
||||
(rf/merge cofx
|
||||
{:db (update db
|
||||
:transport/message-ids->confirmations
|
||||
dissoc message-id)}
|
||||
dissoc
|
||||
message-id)}
|
||||
(models.message/update-message-status chat-id
|
||||
message-id
|
||||
(if not-sent
|
||||
@@ -282,13 +290,13 @@
|
||||
status))
|
||||
(remove-hash message-id))
|
||||
(let [confirmations {:pending-confirmations (dec pending-confirmations)
|
||||
:not-sent (or not-sent
|
||||
(= :not-sent status))}]
|
||||
:not-sent (or not-sent
|
||||
(= :not-sent status))}]
|
||||
{:db (assoc-in db
|
||||
[:transport/message-ids->confirmations message-id]
|
||||
confirmations)}))))
|
||||
[:transport/message-ids->confirmations message-id]
|
||||
confirmations)}))))
|
||||
|
||||
(fx/defn update-envelope-status
|
||||
(rf/defn update-envelope-status
|
||||
[{:keys [db] :as cofx} message-id status]
|
||||
(if-let [{:keys [chat-id]}
|
||||
(get-in db [:transport/message-envelopes message-id])]
|
||||
@@ -298,37 +306,38 @@
|
||||
;; came too early
|
||||
{: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]
|
||||
(when (or (not= status :not-sent)
|
||||
(= :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"
|
||||
[{:keys [db] :as cofx} chat-id message-id message-type]
|
||||
;; 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 % chat-id message-id)
|
||||
statuses)
|
||||
|
||||
add-envelope-data (fn [{:keys [db]}]
|
||||
{:db (-> db
|
||||
(update :transport/message-confirmations dissoc message-id)
|
||||
(assoc-in [:transport/message-envelopes message-id]
|
||||
{:chat-id chat-id
|
||||
:message-type message-type})
|
||||
(update-in [:transport/message-ids->confirmations message-id]
|
||||
#(or % {:pending-confirmations 1})))})]
|
||||
(apply fx/merge cofx (conj check-confirmations-fx add-envelope-data))))
|
||||
add-envelope-data (fn [{:keys [db]}]
|
||||
{:db (-> db
|
||||
(update :transport/message-confirmations dissoc message-id)
|
||||
(assoc-in [:transport/message-envelopes message-id]
|
||||
{:chat-id chat-id
|
||||
:message-type message-type})
|
||||
(update-in [:transport/message-ids->confirmations message-id]
|
||||
#(or % {:pending-confirmations 1})))})]
|
||||
(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]}
|
||||
[cofx response-js]
|
||||
(let [set-hash-fxs (map (fn [{:keys [localChatId id messageType]}]
|
||||
(set-message-envelope-hash localChatId id messageType))
|
||||
(types/js->clj (.-messages response-js)))]
|
||||
(apply fx/merge cofx
|
||||
(apply rf/merge
|
||||
cofx
|
||||
(conj set-hash-fxs
|
||||
#(sanitize-messages-and-process-response % response-js false)))))
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
(ns ^{:doc "Protocol API and protocol utils"}
|
||||
status-im.transport.message.protocol
|
||||
(ns ^{:doc "Protocol API and protocol utils"} status-im.transport.message.protocol
|
||||
(:require [re-frame.core :as re-frame]
|
||||
[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
|
||||
text
|
||||
response-to
|
||||
ens-name
|
||||
community-id
|
||||
image-path
|
||||
audio-path
|
||||
audio-duration-ms
|
||||
sticker
|
||||
content-type]}]
|
||||
(defn build-message
|
||||
[{:keys [chat-id
|
||||
text
|
||||
response-to
|
||||
ens-name
|
||||
community-id
|
||||
image-path
|
||||
audio-path
|
||||
audio-duration-ms
|
||||
sticker
|
||||
content-type]}]
|
||||
{:chatId chat-id
|
||||
:text text
|
||||
:responseTo response-to
|
||||
@@ -26,25 +26,28 @@
|
||||
:sticker sticker
|
||||
:contentType content-type})
|
||||
|
||||
(fx/defn send-chat-messages [_ messages]
|
||||
{::json-rpc/call [{:method "wakuext_sendChatMessages"
|
||||
:params [(mapv build-message messages)]
|
||||
(rf/defn send-chat-messages
|
||||
[_ messages]
|
||||
{::json-rpc/call [{:method "wakuext_sendChatMessages"
|
||||
:params [(mapv build-message messages)]
|
||||
:js-response true
|
||||
:on-success #(re-frame/dispatch [:transport/message-sent %])
|
||||
:on-error #(do
|
||||
(log/warn "failed to send a message" %)
|
||||
(js/alert (str "failed to send a message: " %)))}]})
|
||||
:on-success #(re-frame/dispatch [:transport/message-sent %])
|
||||
:on-error #(do
|
||||
(log/warn "failed to send a message" %)
|
||||
(js/alert (str "failed to send a message: " %)))}]})
|
||||
|
||||
(fx/defn send-reaction [_ {:keys [message-id chat-id emoji-id]}]
|
||||
{::json-rpc/call [{:method "wakuext_sendEmojiReaction"
|
||||
:params [chat-id message-id emoji-id]
|
||||
(rf/defn send-reaction
|
||||
[_ {:keys [message-id chat-id emoji-id]}]
|
||||
{::json-rpc/call [{:method "wakuext_sendEmojiReaction"
|
||||
:params [chat-id message-id emoji-id]
|
||||
:js-response true
|
||||
:on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %])
|
||||
:on-error #(log/error "failed to send a reaction" %)}]})
|
||||
:on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %])
|
||||
:on-error #(log/error "failed to send a reaction" %)}]})
|
||||
|
||||
(fx/defn send-retract-reaction [_ {:keys [emoji-reaction-id]}]
|
||||
{::json-rpc/call [{:method "wakuext_sendEmojiReactionRetraction"
|
||||
:params [emoji-reaction-id]
|
||||
(rf/defn send-retract-reaction
|
||||
[_ {:keys [emoji-reaction-id]}]
|
||||
{::json-rpc/call [{:method "wakuext_sendEmojiReactionRetraction"
|
||||
:params [emoji-reaction-id]
|
||||
:js-response true
|
||||
:on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %])
|
||||
:on-error #(log/error "failed to send a reaction retraction" %)}]})
|
||||
:on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %])
|
||||
:on-error #(log/error "failed to send a reaction retraction" %)}]})
|
||||
|
||||
@@ -1,20 +1,22 @@
|
||||
(ns status-im.ui.components.invite.events
|
||||
(:require [re-frame.core :as re-frame]
|
||||
[status-im.utils.fx :as fx]
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
[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
|
||||
::share
|
||||
(fn [content]
|
||||
(.share ^js react/sharing (clj->js content))))
|
||||
|
||||
(fx/defn share-link
|
||||
(rf/defn share-link
|
||||
{:events [::share-link]}
|
||||
[{:keys [db]}]
|
||||
(let [{:keys [public-key preferred-name]} (get db :multiaccount)
|
||||
profile-link (universal-links/generate-link :user :external
|
||||
(or preferred-name public-key))
|
||||
profile-link (universal-links/generate-link :user
|
||||
:external
|
||||
(or preferred-name
|
||||
public-key))
|
||||
message (i18n/label :t/join-me {:url profile-link})]
|
||||
{::share {:message message}}))
|
||||
|
||||
@@ -1,23 +1,22 @@
|
||||
(ns status-im.ui.screens.chat.audio-message.views
|
||||
(:require-macros [status-im.utils.views :refer [defview letsubs]])
|
||||
(:require
|
||||
[goog.string :as gstring]
|
||||
[reagent.core :as reagent]
|
||||
[status-im.audio.core :as audio]
|
||||
[status-im.ui.components.react :as react]
|
||||
[re-frame.core :as re-frame]
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
[quo.components.animated.pressable :as pressable]
|
||||
[status-im.native-module.core :as status]
|
||||
[status-im.ui.screens.chat.components.input :as input]
|
||||
[status-im.ui.screens.chat.components.style :as input.style]
|
||||
[status-im.ui.screens.chat.audio-message.styles :as styles]
|
||||
[quo.design-system.colors :as colors]
|
||||
[status-im.ui.components.animation :as anim]
|
||||
[status-im.ui.components.icons.icons :as icons]
|
||||
[status-im.utils.utils :as utils.utils]
|
||||
[status-im.utils.fs :as fs]
|
||||
[status-im.utils.fx :as fx]))
|
||||
(:require [goog.string :as gstring]
|
||||
[quo.components.animated.pressable :as pressable]
|
||||
[quo.design-system.colors :as colors]
|
||||
[re-frame.core :as re-frame]
|
||||
[reagent.core :as reagent]
|
||||
[status-im.audio.core :as audio]
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
[status-im.native-module.core :as status]
|
||||
[status-im.ui.components.animation :as anim]
|
||||
[status-im.ui.components.icons.icons :as icons]
|
||||
[status-im.ui.components.react :as react]
|
||||
[status-im.ui.screens.chat.audio-message.styles :as styles]
|
||||
[status-im.ui.screens.chat.components.input :as input]
|
||||
[status-im.ui.screens.chat.components.style :as input.style]
|
||||
[status-im.utils.fs :as fs]
|
||||
[status-im.utils.utils :as utils.utils]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
;; reference db levels
|
||||
(def total-silence-db -160)
|
||||
@@ -31,20 +30,23 @@
|
||||
;;ensure animation finishes before next meter update
|
||||
(defonce metering-anim-duration (int (* metering-interval 0.9)))
|
||||
|
||||
(defn update-meter [meter-data]
|
||||
(defn update-meter
|
||||
[meter-data]
|
||||
(let [value (if meter-data
|
||||
(.-value ^js meter-data)
|
||||
total-silence-db)]
|
||||
(anim/start (anim/timing visual-target-value {:toValue value
|
||||
:duration metering-anim-duration
|
||||
:useNativeDriver true}))))
|
||||
(anim/start (anim/timing visual-target-value
|
||||
{:toValue value
|
||||
:duration metering-anim-duration
|
||||
:useNativeDriver true}))))
|
||||
|
||||
(def base-filename "am.")
|
||||
(def default-format "aac")
|
||||
(def rec-options (merge
|
||||
audio/default-recorder-options
|
||||
{:filename (str base-filename default-format)
|
||||
:meteringInterval metering-interval}))
|
||||
(def rec-options
|
||||
(merge
|
||||
audio/default-recorder-options
|
||||
{:filename (str base-filename default-format)
|
||||
:meteringInterval metering-interval}))
|
||||
|
||||
;; maximum 1 minute of recordings time to keep data at certain size
|
||||
(def max-recording-ms (* 1 60 1000))
|
||||
@@ -53,11 +55,13 @@
|
||||
(defonce recorder-ref (atom nil))
|
||||
(defonce player-ref (atom nil))
|
||||
|
||||
(defn destroy-recorder []
|
||||
(defn destroy-recorder
|
||||
[]
|
||||
(audio/destroy-recorder @recorder-ref)
|
||||
(reset! recorder-ref nil))
|
||||
|
||||
(defn destroy-player []
|
||||
(defn destroy-player
|
||||
[]
|
||||
(audio/destroy-player @player-ref)
|
||||
(reset! player-ref nil))
|
||||
|
||||
@@ -70,7 +74,7 @@
|
||||
;; to be called when app goes in background
|
||||
(defonce on-background-cb (atom #()))
|
||||
|
||||
(fx/defn on-background
|
||||
(rf/defn on-background
|
||||
{:events [:audio-recorder/on-background]}
|
||||
[_]
|
||||
(when @on-background-cb
|
||||
@@ -83,32 +87,38 @@
|
||||
(defonce recording-backlog-ms (atom 0))
|
||||
|
||||
;; updates timer UI
|
||||
(defn update-timer [timer]
|
||||
(defn update-timer
|
||||
[timer]
|
||||
(let [ms (if @recording-start-ts
|
||||
(+
|
||||
(- (js/Date.now) @recording-start-ts)
|
||||
@recording-backlog-ms)
|
||||
@recording-backlog-ms)
|
||||
s (quot ms 1000)]
|
||||
s (quot ms 1000)]
|
||||
(if (> ms max-recording-ms)
|
||||
(@max-recording-reached-cb)
|
||||
(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! 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/parallel
|
||||
[(anim/timing rec-button-anim-value {:toValue (if rec? 1 0)
|
||||
:duration 100
|
||||
:useNativeDriver true})
|
||||
(anim/timing ctrl-buttons-anim-value {:toValue (if show-ctrl? 1 0)
|
||||
:duration 100
|
||||
:useNativeDriver true})])))
|
||||
[(anim/timing rec-button-anim-value
|
||||
{:toValue (if rec? 1 0)
|
||||
:duration 100
|
||||
:useNativeDriver true})
|
||||
(anim/timing ctrl-buttons-anim-value
|
||||
{: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)
|
||||
(@max-recording-reached-cb)
|
||||
(do
|
||||
@@ -120,7 +130,8 @@
|
||||
@state-cb
|
||||
#(utils.utils/show-popup (i18n/label :t/audio-recorder-error) (:message %))))))
|
||||
|
||||
(defn reload-recorder []
|
||||
(defn reload-recorder
|
||||
[]
|
||||
(when @recorder-ref
|
||||
(destroy-recorder))
|
||||
(reset! recorder-ref (audio/new-recorder rec-options #(update-meter %) @state-cb))
|
||||
@@ -134,7 +145,7 @@
|
||||
(destroy-player))
|
||||
(reset! player-ref (audio/new-player
|
||||
(:filename rec-options)
|
||||
{:autoDestroy false
|
||||
{:autoDestroy false
|
||||
:continuesToPlayInBackground false}
|
||||
@state-cb))
|
||||
(audio/prepare-player
|
||||
@@ -142,7 +153,8 @@
|
||||
#(do (@state-cb) (when on-success (on-success)))
|
||||
#(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
|
||||
(utils.utils/clear-interval @recording-timer)
|
||||
(reset! recording-timer nil))
|
||||
@@ -158,7 +170,8 @@
|
||||
#(utils.utils/show-popup (i18n/label :t/audio-recorder-error) (:message %)))
|
||||
(animate-buttons false max-recording-reached? params))
|
||||
|
||||
(defn pause-recording [{:keys [timer] :as params}]
|
||||
(defn pause-recording
|
||||
[{:keys [timer] :as params}]
|
||||
(when @recording-timer
|
||||
(utils.utils/clear-interval @recording-timer)
|
||||
(reset! recording-backlog-ms (+ @recording-backlog-ms (- (js/Date.now) @recording-start-ts)))
|
||||
@@ -181,28 +194,29 @@
|
||||
- :recording-paused
|
||||
- :ready-to-record"
|
||||
[state-ref]
|
||||
(let [player-state (audio/get-state @player-ref)
|
||||
recorder-state (audio/get-state @recorder-ref)
|
||||
output-file (or
|
||||
(audio/get-recorder-file-path @recorder-ref)
|
||||
(:output-file @state-ref))
|
||||
general (cond
|
||||
(= recorder-state audio/RECORDING) :recording
|
||||
(= player-state audio/PLAYING) :playing
|
||||
(= player-state audio/PREPARED) :ready-to-send
|
||||
(= recorder-state audio/PAUSED) :recording-paused
|
||||
:else :ready-to-record)
|
||||
new-state {:general general
|
||||
:cancel-disabled? (nil? (#{:recording :recording-paused :ready-to-send} general))
|
||||
:output-file output-file
|
||||
:duration (audio/get-player-duration @player-ref)}]
|
||||
(let [player-state (audio/get-state @player-ref)
|
||||
recorder-state (audio/get-state @recorder-ref)
|
||||
output-file (or
|
||||
(audio/get-recorder-file-path @recorder-ref)
|
||||
(:output-file @state-ref))
|
||||
general (cond
|
||||
(= recorder-state audio/RECORDING) :recording
|
||||
(= player-state audio/PLAYING) :playing
|
||||
(= player-state audio/PREPARED) :ready-to-send
|
||||
(= recorder-state audio/PAUSED) :recording-paused
|
||||
:else :ready-to-record)
|
||||
new-state {:general general
|
||||
:cancel-disabled? (nil? (#{:recording :recording-paused :ready-to-send} general))
|
||||
:output-file output-file
|
||||
:duration (audio/get-player-duration @player-ref)}]
|
||||
(if (#{:recording :recording-paused} general)
|
||||
(status/activate-keep-awake)
|
||||
(status/deactivate-keep-awake))
|
||||
(when (not= @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
|
||||
(:output-file @state-ref)
|
||||
(int (:duration @state-ref))])
|
||||
@@ -210,52 +224,65 @@
|
||||
(@state-cb))
|
||||
|
||||
;; rec-button-anim-value 0 => stopped, 1 => recording
|
||||
(defview rec-button-view [{:keys [rec-button-anim-value state] :as params}]
|
||||
(letsubs [outer-scale (anim/interpolate visual-target-value {:inputRange [total-silence-db silence-db 0]
|
||||
:outputRange [1 0.8 1.2]})
|
||||
inner-scale (anim/interpolate rec-button-anim-value {:inputRange [0 1]
|
||||
:outputRange [1 0.5]})
|
||||
inner-border-radius (anim/interpolate rec-button-anim-value {:inputRange [0 1]
|
||||
: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}
|
||||
(defview rec-button-view
|
||||
[{:keys [rec-button-anim-value state] :as params}]
|
||||
(letsubs [outer-scale (anim/interpolate visual-target-value
|
||||
{:inputRange [total-silence-db silence-db 0]
|
||||
:outputRange [1 0.8 1.2]})
|
||||
inner-scale (anim/interpolate rec-button-anim-value
|
||||
{:inputRange [0 1]
|
||||
:outputRange [1 0.5]})
|
||||
inner-border-radius (anim/interpolate rec-button-anim-value
|
||||
{:inputRange [0 1]
|
||||
: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/animated-view {:style (styles/rec-outer-circle outer-scale)}]
|
||||
[react/animated-view {:style (styles/rec-inner-circle inner-scale inner-border-radius)}]]]))
|
||||
|
||||
(defn- cancel-button [disabled? on-press contact-request]
|
||||
[pressable/pressable {:type :scale
|
||||
:disabled disabled?
|
||||
:on-press on-press}
|
||||
(defn- cancel-button
|
||||
[disabled? on-press contact-request]
|
||||
[pressable/pressable
|
||||
{:type :scale
|
||||
:disabled disabled?
|
||||
:on-press on-press}
|
||||
[react/view {:style (input.style/send-message-button)}
|
||||
[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
|
||||
:color colors/white-persist}]]])
|
||||
|
||||
(defview audio-message-view []
|
||||
(letsubs [rec-button-anim-value (anim/create-value 0)
|
||||
(defview audio-message-view
|
||||
[]
|
||||
(letsubs [rec-button-anim-value (anim/create-value 0)
|
||||
ctrl-buttons-anim-value (anim/create-value 0)
|
||||
timer (reagent/atom "")
|
||||
state (reagent/atom nil)]
|
||||
{:component-did-mount (fn []
|
||||
(reset-timer timer)
|
||||
(reset! state-cb #(update-state state))
|
||||
(reset! max-recording-reached-cb #(do
|
||||
(when (= (:general @state) :recording)
|
||||
(stop-recording {:rec-button-anim-value rec-button-anim-value
|
||||
:ctrl-buttons-anim-value ctrl-buttons-anim-value
|
||||
:timer timer
|
||||
:max-recording-reached? true}))
|
||||
(utils.utils/show-popup (i18n/label :t/audio-recorder)
|
||||
(i18n/label :t/audio-recorder-max-ms-reached))))
|
||||
(reset! on-background-cb #(when (= (:general @state) :recording)
|
||||
(pause-recording {:rec-button-anim-value rec-button-anim-value
|
||||
:ctrl-buttons-anim-value ctrl-buttons-anim-value
|
||||
:timer timer})))
|
||||
(reload-recorder))
|
||||
timer (reagent/atom "")
|
||||
state (reagent/atom nil)]
|
||||
{:component-did-mount (fn []
|
||||
(reset-timer timer)
|
||||
(reset! state-cb #(update-state state))
|
||||
(reset! max-recording-reached-cb
|
||||
#(do
|
||||
(when (= (:general @state) :recording)
|
||||
(stop-recording {:rec-button-anim-value rec-button-anim-value
|
||||
:ctrl-buttons-anim-value ctrl-buttons-anim-value
|
||||
:timer timer
|
||||
:max-recording-reached? true}))
|
||||
(utils.utils/show-popup (i18n/label :t/audio-recorder)
|
||||
(i18n/label
|
||||
:t/audio-recorder-max-ms-reached))))
|
||||
(reset! on-background-cb #(when (= (:general @state) :recording)
|
||||
(pause-recording
|
||||
{:rec-button-anim-value rec-button-anim-value
|
||||
:ctrl-buttons-anim-value
|
||||
ctrl-buttons-anim-value
|
||||
:timer timer})))
|
||||
(reload-recorder))
|
||||
:component-will-unmount (fn []
|
||||
(when @recording-timer
|
||||
(utils.utils/clear-interval @recording-timer)
|
||||
@@ -268,26 +295,29 @@
|
||||
(reset! state-cb nil)
|
||||
(reset! max-recording-reached-cb nil)
|
||||
(reset! on-background-cb nil))}
|
||||
(let [base-params {:rec-button-anim-value rec-button-anim-value
|
||||
:ctrl-buttons-anim-value ctrl-buttons-anim-value
|
||||
:timer timer}
|
||||
(let [base-params {:rec-button-anim-value rec-button-anim-value
|
||||
:ctrl-buttons-anim-value ctrl-buttons-anim-value
|
||||
:timer timer}
|
||||
contact-request @(re-frame/subscribe [:chats/sending-contact-request])]
|
||||
[react/view {:style styles/container}
|
||||
[react/text {:style styles/timer
|
||||
:accessibility-label :audio-message-recorded-time} @timer]
|
||||
[react/text
|
||||
{:style styles/timer
|
||||
:accessibility-label :audio-message-recorded-time} @timer]
|
||||
[react/view {:style styles/buttons-container}
|
||||
[react/animated-view {:style {:opacity ctrl-buttons-anim-value}}
|
||||
[cancel-button (:cancel-disabled? @state) #(stop-recording base-params) contact-request]]
|
||||
[rec-button-view (merge base-params {:state state})]
|
||||
[react/animated-view {:style {:opacity ctrl-buttons-anim-value}}
|
||||
[input/send-button (fn [] (cond
|
||||
(= :ready-to-send (:general @state))
|
||||
(do
|
||||
(reset-timer timer)
|
||||
(animate-buttons false false base-params)
|
||||
(send-audio-msessage state))
|
||||
[input/send-button
|
||||
(fn []
|
||||
(cond
|
||||
(= :ready-to-send (:general @state))
|
||||
(do
|
||||
(reset-timer timer)
|
||||
(animate-buttons false false base-params)
|
||||
(send-audio-msessage state))
|
||||
|
||||
(#{:recording :recording-paused} (:general @state))
|
||||
(stop-recording (merge base-params
|
||||
{:on-success
|
||||
#(send-audio-msessage state)}))))]]]])))
|
||||
(#{:recording :recording-paused} (:general @state))
|
||||
(stop-recording (merge base-params
|
||||
{:on-success
|
||||
#(send-audio-msessage state)}))))]]]])))
|
||||
|
||||
@@ -1,47 +1,54 @@
|
||||
(ns status-im.ui.screens.chat.components.input
|
||||
(:require [status-im.ui.components.icons.icons :as icons]
|
||||
[quo.react-native :as rn]
|
||||
[quo.react :as quo.react]
|
||||
[quo.platform :as platform]
|
||||
(:require [clojure.string :as string]
|
||||
[quo.components.animated.pressable :as pressable]
|
||||
[quo.components.list.item :as list-item]
|
||||
[quo.components.text :as text]
|
||||
[quo.design-system.colors :as colors]
|
||||
[status-im.ui.screens.chat.components.style :as styles]
|
||||
[status-im.utils.fx :as fx]
|
||||
[status-im.ui.screens.chat.components.reply :as reply]
|
||||
[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]
|
||||
[quo.platform :as platform]
|
||||
[quo.react :as quo.react]
|
||||
[quo.react-native :as rn]
|
||||
[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]
|
||||
[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]
|
||||
(some-> ^js (quo.react/current-ref text-input-ref) .focus))
|
||||
(defn input-focus
|
||||
[text-input-ref]
|
||||
(some-> ^js (quo.react/current-ref text-input-ref)
|
||||
.focus))
|
||||
|
||||
(def panel->icons {:extensions :main-icons/commands
|
||||
:images :main-icons/photo})
|
||||
(def panel->icons
|
||||
{:extensions :main-icons/commands
|
||||
:images :main-icons/photo})
|
||||
|
||||
(defn touchable-icon [{:keys [panel active set-active accessibility-label]}]
|
||||
[pressable/pressable {:type :scale
|
||||
:accessibility-label accessibility-label
|
||||
:on-press #(set-active (when-not (= active panel) panel))}
|
||||
(defn touchable-icon
|
||||
[{:keys [panel active set-active accessibility-label]}]
|
||||
[pressable/pressable
|
||||
{:type :scale
|
||||
:accessibility-label accessibility-label
|
||||
:on-press #(set-active (when-not (= active panel) panel))}
|
||||
[rn/view {:style (styles/touchable-icon)}
|
||||
[icons/icon
|
||||
(panel->icons panel)
|
||||
(styles/icon (= active panel))]]])
|
||||
|
||||
(defn touchable-stickers-icon [{:keys [panel active set-active accessibility-label input-focus]}]
|
||||
[pressable/pressable {:type :scale
|
||||
:accessibility-label accessibility-label
|
||||
:on-press #(if (= active panel)
|
||||
(input-focus)
|
||||
(set-active panel))}
|
||||
(defn touchable-stickers-icon
|
||||
[{:keys [panel active set-active accessibility-label input-focus]}]
|
||||
[pressable/pressable
|
||||
{:type :scale
|
||||
:accessibility-label accessibility-label
|
||||
:on-press #(if (= active panel)
|
||||
(input-focus)
|
||||
(set-active panel))}
|
||||
[rn/view {:style (styles/in-input-touchable-icon)}
|
||||
(if (= active panel)
|
||||
[icons/icon :main-icons/keyboard (styles/icon false)]
|
||||
@@ -49,7 +56,8 @@
|
||||
|
||||
;; TODO(Ferossgp): Move this into audio panel.
|
||||
;; 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
|
||||
[:request-permissions
|
||||
{:permissions [:record-audio]
|
||||
@@ -63,18 +71,21 @@
|
||||
(i18n/label :t/audio-recorder-permissions-error)))
|
||||
50)}]))
|
||||
|
||||
(defn touchable-audio-icon [{:keys [panel active set-active accessibility-label input-focus]}]
|
||||
[pressable/pressable {:type :scale
|
||||
:accessibility-label accessibility-label
|
||||
:on-press #(if (= active panel)
|
||||
(input-focus)
|
||||
(request-record-audio-permission set-active panel))}
|
||||
(defn touchable-audio-icon
|
||||
[{:keys [panel active set-active accessibility-label input-focus]}]
|
||||
[pressable/pressable
|
||||
{:type :scale
|
||||
:accessibility-label accessibility-label
|
||||
:on-press #(if (= active panel)
|
||||
(input-focus)
|
||||
(request-record-audio-permission set-active panel))}
|
||||
[rn/view {:style (styles/in-input-touchable-icon)}
|
||||
(if (= active panel)
|
||||
[icons/icon :main-icons/keyboard (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/view {:style (styles/send-message-button)}
|
||||
(when-not contact-request
|
||||
@@ -83,22 +94,23 @@
|
||||
:accessibility-label :send-message-button
|
||||
: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))
|
||||
start (.-start selection)
|
||||
end (.-end selection)]
|
||||
start (.-start selection)
|
||||
end (.-end selection)]
|
||||
;; NOTE(rasom): on iOS we do not dispatch this event immediately
|
||||
;; because it is needed only in case if selection is changed without
|
||||
;; typing. Timeout might be canceled on `on-change`.
|
||||
(when platform/ios?
|
||||
(reset!
|
||||
timeout-id
|
||||
(utils.utils/set-timeout
|
||||
#(re-frame/dispatch [::mentions/on-selection-change
|
||||
{:start start
|
||||
:end end}
|
||||
mentionable-users])
|
||||
50)))
|
||||
timeout-id
|
||||
(utils.utils/set-timeout
|
||||
#(re-frame/dispatch [::mentions/on-selection-change
|
||||
{:start start
|
||||
:end end}
|
||||
mentionable-users])
|
||||
50)))
|
||||
;; NOTE(rasom): on Android we dispatch event only in case if there
|
||||
;; was no text changes during last 50ms. `on-selection-change` is
|
||||
;; dispatched after `on-change`, that's why there is no another way
|
||||
@@ -128,42 +140,51 @@
|
||||
[]
|
||||
(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
|
||||
(quo.react/set-native-props actions-ref #js {:width 0 :left -88}))
|
||||
(quo.react/set-native-props send-ref #js {:width nil :right nil})
|
||||
(when sticker-ref
|
||||
(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
|
||||
(quo.react/set-native-props actions-ref #js {:width nil :left nil}))
|
||||
(quo.react/set-native-props send-ref #js {:width 0 :right -100})
|
||||
(when sticker-ref
|
||||
(quo.react/set-native-props sticker-ref #js {:width nil :right nil})))
|
||||
|
||||
(defn reset-input [refs chat-id]
|
||||
(some-> ^js (quo.react/current-ref (:text-input-ref refs)) .clear)
|
||||
(defn reset-input
|
||||
[refs chat-id]
|
||||
(some-> ^js (quo.react/current-ref (:text-input-ref refs))
|
||||
.clear)
|
||||
(swap! mentions-enabled update :render not)
|
||||
(swap! input-texts dissoc chat-id))
|
||||
|
||||
(defn clear-input [chat-id refs]
|
||||
(defn clear-input
|
||||
[chat-id refs]
|
||||
(hide-send refs)
|
||||
(if (get @mentions-enabled chat-id)
|
||||
(do
|
||||
(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,
|
||||
;;after clearing text will be typed with this blue color, so we render white text first and then clear it
|
||||
;;we need this timeout, because if we clear text input and first index was a mention object with
|
||||
;;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))
|
||||
(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)
|
||||
;;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]))
|
||||
|
||||
(defn on-change [last-text-change timeout-id mentionable-users refs chat-id sending-image args]
|
||||
(let [text (.-text ^js (.-nativeEvent ^js args))
|
||||
(defn on-change
|
||||
[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)]
|
||||
(when (and (seq prev-text) (empty? text) (not sending-image))
|
||||
(hide-send refs))
|
||||
@@ -187,7 +208,7 @@
|
||||
(when platform/ios?
|
||||
(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
|
||||
as arguments and returns new fx. Always clear all validation messages."
|
||||
{:events [:chat.ui.input/set-chat-input-text]}
|
||||
@@ -197,15 +218,21 @@
|
||||
chat (get-in db [:chats chat-id])
|
||||
current-multiaccount (:multiaccount db)
|
||||
mentionable-users (mentions/get-mentionable-users
|
||||
chat all-contacts current-multiaccount nil)
|
||||
chat
|
||||
all-contacts
|
||||
current-multiaccount
|
||||
nil)
|
||||
hydrated-mentions (map
|
||||
(fn [[t mention :as e]]
|
||||
(if (= t :mention)
|
||||
(let [mention (multiaccounts/displayed-name
|
||||
(get mentionable-users mention))]
|
||||
[:mention (if (string/starts-with? mention "@")
|
||||
mention (str "@" mention))])
|
||||
e)) text-with-mentions)
|
||||
[:mention
|
||||
(if (string/starts-with? mention "@")
|
||||
mention
|
||||
(str "@" mention))])
|
||||
e))
|
||||
text-with-mentions)
|
||||
info (mentions/->info hydrated-mentions)
|
||||
new-text (string/join (map second hydrated-mentions))]
|
||||
{:set-text-input-value [chat-id new-text]
|
||||
@@ -215,13 +242,14 @@
|
||||
(assoc-in [:chat/inputs-with-mentions chat-id] hydrated-mentions)
|
||||
(assoc-in [:chats/mentions chat-id :mentions] info))}))
|
||||
|
||||
(defn on-text-input [mentionable-users chat-id args]
|
||||
(let [native-event (.-nativeEvent ^js args)
|
||||
text (.-text ^js native-event)
|
||||
(defn on-text-input
|
||||
[mentionable-users chat-id args]
|
||||
(let [native-event (.-nativeEvent ^js args)
|
||||
text (.-text ^js native-event)
|
||||
previous-text (.-previousText ^js native-event)
|
||||
range (.-range ^js native-event)
|
||||
start (.-start ^js range)
|
||||
end (.-end ^js range)]
|
||||
range (.-range ^js native-event)
|
||||
start (.-start ^js range)
|
||||
end (.-end ^js range)]
|
||||
(when (and (not (get @mentions-enabled chat-id)) (string/index-of text "@"))
|
||||
(swap! mentions-enabled assoc chat-id true))
|
||||
|
||||
@@ -237,13 +265,14 @@
|
||||
(when platform/android?
|
||||
(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?])
|
||||
mentionable-users @(re-frame/subscribe [:chats/mentionable-users])
|
||||
timeout-id (atom nil)
|
||||
last-text-change (atom nil)
|
||||
mentions-enabled (get @mentions-enabled chat-id)
|
||||
contact-request @(re-frame/subscribe [:chats/sending-contact-request])]
|
||||
timeout-id (atom nil)
|
||||
last-text-change (atom nil)
|
||||
mentions-enabled (get @mentions-enabled chat-id)
|
||||
contact-request @(re-frame/subscribe [:chats/sending-contact-request])]
|
||||
|
||||
[rn/text-input
|
||||
{:style (styles/text-input contact-request)
|
||||
@@ -263,8 +292,12 @@
|
||||
(i18n/label :t/type-a-message))
|
||||
:underline-color-android :transparent
|
||||
:auto-capitalize :sentences
|
||||
:on-selection-change (partial on-selection-change 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-selection-change (partial on-selection-change
|
||||
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)}
|
||||
(if mentions-enabled
|
||||
(for [[idx [type text]] (map-indexed
|
||||
@@ -280,39 +313,41 @@
|
||||
[[public-key {:keys [alias name nickname] :as user}] _ _ text-input-ref]
|
||||
(let [ens-name? (not= alias name)]
|
||||
[list-item/list-item
|
||||
(cond-> {:icon [photos/member-photo public-key]
|
||||
:size :small
|
||||
:text-size :small
|
||||
:title
|
||||
[text/text
|
||||
{:weight :medium
|
||||
:ellipsize-mode :tail
|
||||
:number-of-lines 1
|
||||
:size :small}
|
||||
(if nickname
|
||||
nickname
|
||||
name)
|
||||
(when nickname
|
||||
[text/text
|
||||
{:weight :regular
|
||||
:color :secondary
|
||||
:ellipsize-mode :tail
|
||||
:size :small}
|
||||
" "
|
||||
(when ens-name?
|
||||
"@")
|
||||
name])]
|
||||
:title-text-weight :medium
|
||||
:on-press
|
||||
(fn []
|
||||
(re-frame/dispatch [:chat.ui/select-mention text-input-ref user]))}
|
||||
(cond->
|
||||
{:icon [photos/member-photo public-key]
|
||||
:size :small
|
||||
:text-size :small
|
||||
:title
|
||||
[text/text
|
||||
{:weight :medium
|
||||
:ellipsize-mode :tail
|
||||
:number-of-lines 1
|
||||
:size :small}
|
||||
(if nickname
|
||||
nickname
|
||||
name)
|
||||
(when nickname
|
||||
[text/text
|
||||
{:weight :regular
|
||||
:color :secondary
|
||||
:ellipsize-mode :tail
|
||||
:size :small}
|
||||
" "
|
||||
(when ens-name?
|
||||
"@")
|
||||
name])]
|
||||
:title-text-weight :medium
|
||||
:on-press
|
||||
(fn []
|
||||
(re-frame/dispatch [:chat.ui/select-mention text-input-ref user]))}
|
||||
|
||||
ens-name?
|
||||
(assoc :subtitle alias))]))
|
||||
|
||||
(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])]
|
||||
(when (seq suggestions)
|
||||
(let [height (+ 16 (* 52 (min 4.5 (count suggestions))))]
|
||||
@@ -330,36 +365,44 @@
|
||||
:render-data text-input-ref
|
||||
: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)))
|
||||
|
||||
(defn send-image []
|
||||
(defn send-image
|
||||
[]
|
||||
(let [sending-image @(re-frame/subscribe [:chats/sending-image])]
|
||||
(when (seq sending-image)
|
||||
[reply/send-image sending-image])))
|
||||
|
||||
(defn actions [extensions image show-send actions-ref active-panel set-active-panel contact-request]
|
||||
[rn/view {:style (styles/actions-wrapper (and (not contact-request) show-send))
|
||||
:ref actions-ref}
|
||||
(defn actions
|
||||
[extensions image show-send actions-ref active-panel set-active-panel contact-request]
|
||||
[rn/view
|
||||
{:style (styles/actions-wrapper (and (not contact-request) show-send))
|
||||
:ref actions-ref}
|
||||
(when extensions
|
||||
[touchable-icon {:panel :extensions
|
||||
:accessibility-label :show-extensions-icon
|
||||
:active active-panel
|
||||
:set-active set-active-panel}])
|
||||
[touchable-icon
|
||||
{:panel :extensions
|
||||
:accessibility-label :show-extensions-icon
|
||||
:active active-panel
|
||||
:set-active set-active-panel}])
|
||||
(when image
|
||||
[touchable-icon {:panel :images
|
||||
:accessibility-label :show-photo-icon
|
||||
:active active-panel
|
||||
:set-active set-active-panel}])])
|
||||
[touchable-icon
|
||||
{:panel :images
|
||||
:accessibility-label :show-photo-icon
|
||||
:active active-panel
|
||||
:set-active set-active-panel}])])
|
||||
|
||||
(defn chat-toolbar [{:keys [chat-id]}]
|
||||
(let [actions-ref (quo.react/create-ref)
|
||||
send-ref (quo.react/create-ref)
|
||||
sticker-ref (quo.react/create-ref)
|
||||
(defn chat-toolbar
|
||||
[{:keys [chat-id]}]
|
||||
(let [actions-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])
|
||||
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]}]
|
||||
(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
|
||||
refs {:actions-ref actions-ref
|
||||
:send-ref send-ref
|
||||
@@ -368,37 +411,43 @@
|
||||
{:keys [send stickers image extensions audio sending-image]} @toolbar-options
|
||||
show-send (or show-send sending-image)
|
||||
contact-request @(re-frame/subscribe [:chats/sending-contact-request])]
|
||||
[rn/view {:style (styles/toolbar)
|
||||
:on-layout on-chat-toolbar-layout}
|
||||
[rn/view
|
||||
{:style (styles/toolbar)
|
||||
:on-layout on-chat-toolbar-layout}
|
||||
;; EXTENSIONS and IMAGE buttons
|
||||
[actions extensions image show-send actions-ref active-panel set-active-panel contact-request]
|
||||
[rn/view {:style (styles/input-container contact-request)}
|
||||
[send-image]
|
||||
[rn/view {:style styles/input-row}
|
||||
[text-input {:chat-id chat-id
|
||||
:sending-image sending-image
|
||||
:refs refs
|
||||
:set-active-panel set-active-panel}]
|
||||
[text-input
|
||||
{:chat-id chat-id
|
||||
:sending-image sending-image
|
||||
:refs refs
|
||||
:set-active-panel set-active-panel}]
|
||||
;; SEND button
|
||||
[rn/view {:ref send-ref :style (when-not show-send {:width 0 :right -100})}
|
||||
(when send
|
||||
[send-button #(do (clear-input chat-id refs)
|
||||
(re-frame/dispatch [:chat.ui/send-current-message]))
|
||||
[send-button
|
||||
#(do (clear-input chat-id refs)
|
||||
(re-frame/dispatch [:chat.ui/send-current-message]))
|
||||
contact-request])]
|
||||
|
||||
;; STICKERS and AUDIO buttons
|
||||
(when-not @(re-frame/subscribe [:chats/edit-message])
|
||||
[rn/view {:style (merge {:flex-direction :row} (when show-send {:width 0 :right -100}))
|
||||
:ref sticker-ref}
|
||||
[rn/view
|
||||
{:style (merge {:flex-direction :row} (when show-send {:width 0 :right -100}))
|
||||
:ref sticker-ref}
|
||||
(when stickers
|
||||
[touchable-stickers-icon {:panel :stickers
|
||||
:accessibility-label :show-stickers-icon
|
||||
:active active-panel
|
||||
:input-focus #(input-focus text-input-ref)
|
||||
:set-active set-active-panel}])
|
||||
[touchable-stickers-icon
|
||||
{:panel :stickers
|
||||
:accessibility-label :show-stickers-icon
|
||||
:active active-panel
|
||||
:input-focus #(input-focus text-input-ref)
|
||||
:set-active set-active-panel}])
|
||||
(when audio
|
||||
[touchable-audio-icon {:panel :audio
|
||||
:accessibility-label :show-audio-message-icon
|
||||
:active active-panel
|
||||
:input-focus #(input-focus text-input-ref)
|
||||
:set-active set-active-panel}])])]]]))))
|
||||
[touchable-audio-icon
|
||||
{:panel :audio
|
||||
:accessibility-label :show-audio-message-icon
|
||||
:active 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
|
||||
(:require-macros [status-im.utils.views :refer [defview letsubs]])
|
||||
(:require [status-im.utils.utils :as utils]
|
||||
[reagent.core :as reagent]
|
||||
(:require ["react-native-blob-util" :default ReactNativeBlobUtil]
|
||||
[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]
|
||||
[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.react :as react]
|
||||
[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]
|
||||
[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 current-player-message-id (atom nil))
|
||||
(defonce current-active-state-ref-ref (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
|
||||
(utils/clear-interval @progress-timer)
|
||||
(when-not start?
|
||||
@@ -28,48 +29,61 @@
|
||||
(when start?
|
||||
(when @progress-timer
|
||||
(utils/clear-interval @progress-timer))
|
||||
(reset! progress-timer (utils/set-interval
|
||||
#(when (and @state-ref (not (:slider-seeking @state-ref)))
|
||||
(let [ct (audio/get-player-current-time @player-ref)]
|
||||
(reset! progress-ref ct)
|
||||
(when ct
|
||||
(anim/start (anim/timing progress-anim {:toValue @progress-ref
|
||||
:duration 100
|
||||
:easing (.-linear ^js anim/easing)
|
||||
:useNativeDriver true})))))
|
||||
100))))
|
||||
(reset! progress-timer
|
||||
(utils/set-interval
|
||||
#(when (and @state-ref (not (:slider-seeking @state-ref)))
|
||||
(let [ct (audio/get-player-current-time @player-ref)]
|
||||
(reset! progress-ref ct)
|
||||
(when ct
|
||||
(anim/start (anim/timing progress-anim
|
||||
{:toValue @progress-ref
|
||||
:duration 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]}]
|
||||
(let [player-state (audio/get-state @player-ref)
|
||||
(defn update-state
|
||||
[{: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-new-state-seeking?
|
||||
(:slider-seeking @state-ref))
|
||||
general (cond
|
||||
(some? error) :error
|
||||
(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
|
||||
(= 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)
|
||||
general (cond
|
||||
(some? error) :error
|
||||
(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
|
||||
(= 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))
|
||||
:progress-ref (or progress-ref (:progress-ref @state-ref))
|
||||
:progress-anim (or progress-anim (:progress-anim @state-ref))
|
||||
:slider-seeking slider-seeking
|
||||
:else (:duration @state-ref))
|
||||
:progress-ref (or progress-ref (:progress-ref @state-ref))
|
||||
:progress-anim (or progress-anim (:progress-anim @state-ref))
|
||||
:slider-seeking slider-seeking
|
||||
|
||||
; persist seek-to-ms while seeking or audio is not loaded
|
||||
:seek-to-ms (when (or
|
||||
slider-seeking
|
||||
(#{:preparing :not-loaded :error} general))
|
||||
(or seek-to-ms (:seek-to-ms @state-ref)))}]
|
||||
; persist seek-to-ms while seeking or audio is not loaded
|
||||
:seek-to-ms (when (or
|
||||
slider-seeking
|
||||
(#{:preparing :not-loaded :error} general))
|
||||
(or seek-to-ms (:seek-to-ms @state-ref)))}]
|
||||
; update state if needed
|
||||
(when (not= @state-ref new-state)
|
||||
(reset! state-ref new-state))
|
||||
@@ -87,9 +101,11 @@
|
||||
(reset! (:progress-ref new-state) 0)
|
||||
(anim/set-value (:progress-anim new-state) 0))))
|
||||
|
||||
(defn destroy-player [{:keys [message-id reloading?]}]
|
||||
(when (and @player-ref (or reloading?
|
||||
(= message-id @current-player-message-id)))
|
||||
(defn destroy-player
|
||||
[{:keys [message-id reloading?]}]
|
||||
(when (and @player-ref
|
||||
(or reloading?
|
||||
(= message-id @current-player-message-id)))
|
||||
(audio/destroy-player @player-ref)
|
||||
(reset! player-ref nil)
|
||||
(when @current-active-state-ref-ref
|
||||
@@ -99,7 +115,8 @@
|
||||
|
||||
(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))
|
||||
(let [now (js/Date.now)]
|
||||
(when (or immediate? (> (- now @last-seek) 200))
|
||||
@@ -113,13 +130,15 @@
|
||||
#(update-state (merge params {:error (:message %)}))))))
|
||||
(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?}))
|
||||
(.fetch "GET" (str base64-uri))
|
||||
(.then #(on-success (.base64 ^js %)))
|
||||
(.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,
|
||||
;; we go ahead only if there is no player or
|
||||
;; if it is already prepared
|
||||
@@ -131,7 +150,7 @@
|
||||
(fn [base64-data]
|
||||
(reset! player-ref (audio/new-player
|
||||
(str "data:audio/acc;base64," base64-data)
|
||||
{:autoDestroy false
|
||||
{:autoDestroy false
|
||||
:continuesToPlayInBackground false}
|
||||
#(seek params 0 true nil)))
|
||||
(audio/prepare-player
|
||||
@@ -142,7 +161,8 @@
|
||||
(reset! current-active-state-ref-ref state-ref)
|
||||
(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)
|
||||
;; player has audio from another message, we need to reload
|
||||
(reload-player params
|
||||
@@ -171,40 +191,43 @@
|
||||
(update-state params))
|
||||
#(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)]
|
||||
(if (= (:general @state-ref) :preparing)
|
||||
[react/view {:style (style/play-pause-container outgoing true)}
|
||||
(if (= (:general @state-ref) :preparing)
|
||||
[react/view {:style (style/play-pause-container outgoing true)}
|
||||
[react/small-loading-indicator color]]
|
||||
[react/touchable-highlight {:on-press on-press}
|
||||
[icons/icon (case (:general @state-ref)
|
||||
:playing :main-icons/pause
|
||||
:main-icons/play)
|
||||
[icons/icon
|
||||
(case (:general @state-ref)
|
||||
:playing :main-icons/pause
|
||||
:main-icons/play)
|
||||
{:container-style (style/play-pause-container outgoing false)
|
||||
:accessibility-label :play-pause-audio-message-button
|
||||
:color color}]])))
|
||||
|
||||
(fx/defn on-background
|
||||
(rf/defn on-background
|
||||
{:events [:audio-message/on-background]}
|
||||
[_]
|
||||
(when (and @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}))
|
||||
nil)
|
||||
|
||||
(defview message-content [{:keys [audio audio-duration-ms message-id outgoing]}]
|
||||
(letsubs [state (reagent/atom nil)
|
||||
progress (reagent/atom 0)
|
||||
(defview message-content
|
||||
[{:keys [audio audio-duration-ms message-id outgoing]}]
|
||||
(letsubs [state (reagent/atom nil)
|
||||
progress (reagent/atom 0)
|
||||
progress-anim (anim/create-value 0)
|
||||
width [:dimensions/window-width]]
|
||||
{:component-did-mount (fn []
|
||||
(update-state {:state-ref state
|
||||
:audio-duration-ms audio-duration-ms
|
||||
:message-id message-id
|
||||
:unloaded? true
|
||||
:progress-ref progress
|
||||
:progress-anim progress-anim}))
|
||||
width [:dimensions/window-width]]
|
||||
{:component-did-mount (fn []
|
||||
(update-state {:state-ref state
|
||||
:audio-duration-ms audio-duration-ms
|
||||
:message-id message-id
|
||||
:unloaded? true
|
||||
:progress-ref progress
|
||||
:progress-anim progress-anim}))
|
||||
:component-will-unmount (fn []
|
||||
(destroy-player {:state-ref state :message-id message-id})
|
||||
(when (= @current-player-message-id message-id)
|
||||
@@ -212,27 +235,38 @@
|
||||
(reset! current-player-message-id 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)
|
||||
[react/text {:style {:typography :main-medium
|
||||
:margin-bottom 16}} (:error-msg @state)]
|
||||
[react/text
|
||||
{:style {:typography :main-medium
|
||||
:margin-bottom 16}} (:error-msg @state)]
|
||||
[react/view (style/container width)
|
||||
[react/view style/play-pause-slider-container
|
||||
[play-pause-button state outgoing #(play-pause base-params audio)]
|
||||
[react/view style/slider-container
|
||||
[slider/animated-slider (merge (style/slider outgoing)
|
||||
{:minimum-value 0
|
||||
:maximum-value (:duration @state)
|
||||
:value progress-anim
|
||||
:on-value-change #(seek base-params % false 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)})]]]
|
||||
[slider/animated-slider
|
||||
(merge (style/slider outgoing)
|
||||
{:minimum-value 0
|
||||
:maximum-value (:duration @state)
|
||||
:value progress-anim
|
||||
:on-value-change #(seek base-params % false 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/text {:style (style/timestamp outgoing)}
|
||||
[react/text {:style (style/timestamp outgoing)}
|
||||
(let [time (cond
|
||||
(or (:slider-seeking @state) (> (:seek-to-ms @state) 0)) (:seek-to-ms @state)
|
||||
(#{:playing :paused :seeking} (:general @state)) @progress
|
||||
:else (:duration @state))
|
||||
s (quot time 1000)]
|
||||
(#{:playing :paused :seeking} (:general @state)) @progress
|
||||
:else (:duration @state))
|
||||
s (quot time 1000)]
|
||||
(gstring/format "%02d:%02d" (quot s 60) (mod s 60)))]]]))))
|
||||
|
||||
@@ -1,87 +1,101 @@
|
||||
(ns status-im.ui.screens.chat.message.pinned-message
|
||||
(:require [re-frame.core :as re-frame]
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
(:require [quo.core :as quo]
|
||||
[quo.design-system.colors :as colors]
|
||||
[status-im.ui.components.react :as react]
|
||||
[quo.core :as quo]
|
||||
[re-frame.core :as re-frame]
|
||||
[reagent.core :as reagent]
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
[status-im.ui.components.list.views :as list]
|
||||
[status-im.utils.handlers :refer [<sub]]
|
||||
[status-im.ui.screens.chat.message.message :as message]))
|
||||
[status-im.ui.components.react :as react]
|
||||
[status-im.ui.screens.chat.message.message :as message]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(def selected-unpin (reagent/atom nil))
|
||||
|
||||
(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%"}
|
||||
:on-press #(reset! selected-unpin message-id)}
|
||||
[react/view {:style {:flex-direction :row
|
||||
:align-items :center
|
||||
:justify-content :space-between
|
||||
:flex 1
|
||||
:padding-right 20}}
|
||||
(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%"}
|
||||
:on-press #(reset! selected-unpin message-id)}
|
||||
[react/view
|
||||
{:style {:flex-direction :row
|
||||
:align-items :center
|
||||
:justify-content :space-between
|
||||
:flex 1
|
||||
:padding-right 20}}
|
||||
[message/chat-message
|
||||
(assoc message
|
||||
:group-chat group-chat
|
||||
:public? public?
|
||||
:group-chat group-chat
|
||||
:public? public?
|
||||
:current-public-key current-public-key
|
||||
:show-input? false
|
||||
:pinned false
|
||||
:display-username? (not outgoing)
|
||||
:display-photo? false
|
||||
:last-in-group? false
|
||||
:in-popover? true)
|
||||
:show-input? false
|
||||
:pinned false
|
||||
:display-username? (not outgoing)
|
||||
:display-photo? false
|
||||
:last-in-group? false
|
||||
:in-popover? true)
|
||||
space-keeper]
|
||||
[react/view {:style {:position :absolute
|
||||
:right 18
|
||||
:padding-top 4}}
|
||||
[react/view
|
||||
{:style {:position :absolute
|
||||
:right 18
|
||||
:padding-top 4}}
|
||||
[quo/radio {:value (= @selected-unpin message-id)}]]]])
|
||||
|
||||
(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])]
|
||||
[list/flat-list
|
||||
{:key-fn list-key-fn
|
||||
:data (reverse pinned-messages)
|
||||
:render-data {:chat-id chat-id}
|
||||
:render-fn render-pin-fn
|
||||
:on-scroll-to-index-failed identity
|
||||
:style {:flex-grow 0
|
||||
:border-top-width 1
|
||||
:border-bottom-width 1
|
||||
:border-top-color colors/gray-lighter
|
||||
:border-bottom-color colors/gray-lighter}
|
||||
:content-container-style {:padding-bottom 10
|
||||
:padding-top 10}}]))
|
||||
{:key-fn list-key-fn
|
||||
:data (reverse pinned-messages)
|
||||
:render-data {:chat-id chat-id}
|
||||
:render-fn render-pin-fn
|
||||
:on-scroll-to-index-failed identity
|
||||
:style {:flex-grow 0
|
||||
:border-top-width 1
|
||||
:border-bottom-width 1
|
||||
:border-top-color colors/gray-lighter
|
||||
:border-bottom-color colors/gray-lighter}
|
||||
:content-container-style {:padding-bottom 10
|
||||
:padding-top 10}}]))
|
||||
|
||||
(defn pin-limit-popover []
|
||||
(let [{:keys [message]} (<sub [:popover/popover])]
|
||||
(defn pin-limit-popover
|
||||
[]
|
||||
(let [{:keys [message]} (rf/sub [:popover/popover])]
|
||||
[react/view {:style {:flex-shrink 1}}
|
||||
[react/view {:style {:height 60
|
||||
:justify-content :center}}
|
||||
[react/text {:style {:padding-horizontal 40
|
||||
:text-align :center}}
|
||||
[react/view
|
||||
{:style {:height 60
|
||||
:justify-content :center}}
|
||||
[react/text
|
||||
{:style {:padding-horizontal 40
|
||||
:text-align :center}}
|
||||
(i18n/label :t/pin-limit-reached)]]
|
||||
[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
|
||||
{:on-press #(do
|
||||
(reset! selected-unpin nil)
|
||||
(re-frame/dispatch [:hide-popover]))
|
||||
:type :secondary}
|
||||
:type :secondary}
|
||||
(i18n/label :t/cancel)]
|
||||
[quo/button
|
||||
{:on-press #(do
|
||||
(re-frame/dispatch [:pin-message/send-pin-message {:chat-id (message :chat-id)
|
||||
:message-id @selected-unpin
|
||||
:pinned false}])
|
||||
(re-frame/dispatch [:pin-message/send-pin-message
|
||||
{:chat-id (message :chat-id)
|
||||
:message-id @selected-unpin
|
||||
:pinned false}])
|
||||
(re-frame/dispatch [:pin-message/send-pin-message (assoc message :pinned true)])
|
||||
(re-frame/dispatch [:hide-popover])
|
||||
(reset! selected-unpin nil))
|
||||
:type :secondary
|
||||
:type :secondary
|
||||
:disabled (nil? @selected-unpin)
|
||||
:theme (if (nil? @selected-unpin) :disabled :negative)}
|
||||
:theme (if (nil? @selected-unpin) :disabled :negative)}
|
||||
(i18n/label :t/unpin)]]]))
|
||||
|
||||
@@ -1,59 +1,67 @@
|
||||
(ns status-im.ui.screens.communities.channel-details
|
||||
(:require [quo.core :as quo]
|
||||
[status-im.utils.handlers :refer [>evt <sub]]
|
||||
[status-im.ui.components.profile-header.view :as profile-header]
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
[clojure.string :as string]
|
||||
(:require [clojure.string :as string]
|
||||
[quo.core :as quo]
|
||||
[re-frame.core :as re-frame]
|
||||
[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 []
|
||||
(let [{:keys [chat-id]} (<sub [:get-screen-params])]
|
||||
(defn view
|
||||
[]
|
||||
(let [{:keys [chat-id]} (rf/sub [:get-screen-params])]
|
||||
(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 [position]} (<sub [:chats/community-chat-by-id community-id chat-id])
|
||||
category (<sub [:chats/category-by-chat-id community-id chat-id])
|
||||
{:keys [admin]} (<sub [:communities/community community-id])
|
||||
pinned-messages (<sub [:chats/pinned chat-id])]
|
||||
[quo/animated-header {:left-accessories [{:icon :main-icons/arrow-left
|
||||
:accessibility-label :back-button
|
||||
:on-press #(>evt [:navigate-back])}]
|
||||
:right-accessories (when admin [{:icon :edit
|
||||
:accessibility-label :invite-button
|
||||
:on-press #(>evt [::communities/edit-channel-pressed
|
||||
community-id
|
||||
chat-name
|
||||
description
|
||||
color
|
||||
emoji
|
||||
chat-id
|
||||
(:id category)
|
||||
position])}])
|
||||
:extended-header (profile-header/extended-header
|
||||
{:title chat-name
|
||||
:color color
|
||||
:emoji emoji
|
||||
:subtitle (i18n/label :t/public-channel)})
|
||||
:use-insets true}
|
||||
{:keys [position]} (rf/sub [:chats/community-chat-by-id
|
||||
community-id chat-id])
|
||||
category (rf/sub [:chats/category-by-chat-id
|
||||
community-id chat-id])
|
||||
{:keys [admin]} (rf/sub [:communities/community
|
||||
community-id])
|
||||
pinned-messages (rf/sub [:chats/pinned chat-id])]
|
||||
[quo/animated-header
|
||||
{:left-accessories [{:icon :main-icons/arrow-left
|
||||
:accessibility-label :back-button
|
||||
:on-press #(rf/dispatch [:navigate-back])}]
|
||||
:right-accessories (when admin
|
||||
[{:icon :edit
|
||||
:accessibility-label :invite-button
|
||||
:on-press #(rf/dispatch [::communities/edit-channel-pressed
|
||||
community-id
|
||||
chat-name
|
||||
description
|
||||
color
|
||||
emoji
|
||||
chat-id
|
||||
(:id category)
|
||||
position])}])
|
||||
: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)
|
||||
[:<>
|
||||
[quo/list-footer {:color :main}
|
||||
description]
|
||||
[quo/separator {:style {:margin-vertical 8}}]
|
||||
(when admin
|
||||
[quo/list-item {:title (i18n/label :t/category)
|
||||
:on-press #(>evt [:open-modal :select-category {:chat current-chat
|
||||
:category category
|
||||
:community-id community-id}])
|
||||
:chevron true
|
||||
:accessory :text
|
||||
:accessory-text (if category
|
||||
(:name category)
|
||||
(i18n/label :t/none))}])
|
||||
[quo/list-item
|
||||
{:title (i18n/label :t/category)
|
||||
:on-press #(rf/dispatch [:open-modal :select-category
|
||||
{:chat current-chat
|
||||
:category category
|
||||
:community-id community-id}])
|
||||
:chevron true
|
||||
:accessory :text
|
||||
:accessory-text (if category
|
||||
(:name category)
|
||||
(i18n/label :t/none))}])
|
||||
[quo/list-item
|
||||
{:title (i18n/label :t/pinned-messages)
|
||||
:accessory :text
|
||||
:accessory-text (count pinned-messages)
|
||||
:chevron true
|
||||
:on-press #(re-frame/dispatch [:contact.ui/pinned-messages-pressed chat-id])}]])]))))
|
||||
{:title (i18n/label :t/pinned-messages)
|
||||
:accessory :text
|
||||
:accessory-text (count pinned-messages)
|
||||
:chevron true
|
||||
: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
|
||||
(:require [quo.react-native :as rn]
|
||||
(:require [clojure.string :as string]
|
||||
[quo.core :as quo]
|
||||
[clojure.string :as string]
|
||||
[status-im.ui.components.react :as react]
|
||||
[status-im.utils.handlers :refer [>evt <sub]]
|
||||
[quo.react-native :as rn]
|
||||
[status-im.communities.core :as communities]
|
||||
[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.color-picker :as color-picker]
|
||||
[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 []
|
||||
(let [{:keys [color emoji]} (<sub [:communities/create-channel])
|
||||
size styles/emoji-thumbnail-preview-size]
|
||||
(defn thumbnail-preview-section
|
||||
[]
|
||||
(let [{:keys [color emoji]} (rf/sub [:communities/create-channel])
|
||||
size styles/emoji-thumbnail-preview-size]
|
||||
[rn/view styles/emoji-thumbnail-preview
|
||||
[emoji-thumbnail-preview/emoji-thumbnail
|
||||
emoji color size]]))
|
||||
|
||||
(defn color-circle [item]
|
||||
(let [{:keys [color]} (<sub [:communities/create-channel])
|
||||
item-color (:color item)
|
||||
key (:key key)
|
||||
color-selected? (= (string/lower-case item-color) (string/lower-case color))]
|
||||
[react/touchable-opacity {:key key
|
||||
:accessibility-label :color-circle
|
||||
:on-press #(>evt [::communities/create-channel-field :color item-color])}
|
||||
(defn color-circle
|
||||
[item]
|
||||
(let [{:keys [color]} (rf/sub [:communities/create-channel])
|
||||
item-color (:color item)
|
||||
key (:key key)
|
||||
color-selected? (= (string/lower-case item-color) (string/lower-case color))]
|
||||
[react/touchable-opacity
|
||||
{: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 item-color)}]]]))
|
||||
|
||||
(defn update-emoji [emoji]
|
||||
(defn update-emoji
|
||||
[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 []
|
||||
(let [{:keys [width height]} (<sub [:dimensions/window])
|
||||
keyboard_height (if (> width height) 400 (- height styles/emoji-picker-upper-components-size))]
|
||||
(defn emoji-keyboard-section
|
||||
[]
|
||||
(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/emoji-keyboard (styles/emoji-keyboard #(update-emoji (.-emoji ^js %)))]]))
|
||||
|
||||
(defn view []
|
||||
(defn view
|
||||
[]
|
||||
[kb-presentation/keyboard-avoiding-view {:style {:flex 1}}
|
||||
[rn/scroll-view
|
||||
[quo/separator]
|
||||
|
||||
@@ -1,182 +1,214 @@
|
||||
(ns status-im.ui.screens.communities.create
|
||||
(:require [quo.react-native :as rn]
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
(:require [clojure.string :as string]
|
||||
[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]
|
||||
[status-im.ui.components.react :as react]
|
||||
[status-im.ui.screens.communities.membership :as memberships]
|
||||
[quo.react-native :as rn]
|
||||
[status-im.communities.core :as communities]
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
[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-description-length 140)
|
||||
|
||||
(defn valid? [community-name community-description]
|
||||
(defn valid?
|
||||
[community-name community-description]
|
||||
(and (not (string/blank? community-name))
|
||||
(not (string/blank? community-description))
|
||||
(<= (count community-name) max-name-length)
|
||||
(<= (count community-description) max-description-length)))
|
||||
|
||||
(def crop-size 1000)
|
||||
(def crop-opts {:cropping true
|
||||
:cropperCircleOverlay true
|
||||
:width crop-size
|
||||
:height crop-size})
|
||||
(def crop-opts
|
||||
{:cropping true
|
||||
:cropperCircleOverlay true
|
||||
: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
|
||||
(js/setTimeout
|
||||
(fn []
|
||||
(react/show-image-picker
|
||||
#(do (>evt [::communities/create-field :image (.-path ^js %)])
|
||||
(>evt [::communities/create-field :new-image (.-path ^js %)]))
|
||||
#(do (rf/dispatch [::communities/create-field :image (.-path ^js %)])
|
||||
(rf/dispatch [::communities/create-field :new-image (.-path ^js %)]))
|
||||
|
||||
crop-opts))
|
||||
300))
|
||||
|
||||
(defn take-pic []
|
||||
(defn take-pic
|
||||
[]
|
||||
;;we need timeout because first we need to close bottom sheet and then open picker
|
||||
(js/setTimeout
|
||||
(fn []
|
||||
(react/show-image-picker-camera
|
||||
#(do (>evt [::communities/create-field :image (.-path ^js %)])
|
||||
(>evt [::communities/create-field :new-image (.-path ^js %)]))
|
||||
#(do (rf/dispatch [::communities/create-field :image (.-path ^js %)])
|
||||
(rf/dispatch [::communities/create-field :new-image (.-path ^js %)]))
|
||||
crop-opts))
|
||||
300))
|
||||
|
||||
(defn bottom-sheet [has-picture editing?]
|
||||
(defn bottom-sheet
|
||||
[has-picture editing?]
|
||||
(fn []
|
||||
[:<>
|
||||
[quo/list-item {:accessibility-label :take-photo
|
||||
:theme :accent
|
||||
:icon :main-icons/camera
|
||||
:title (i18n/label :t/community-image-take)
|
||||
:on-press #(do
|
||||
(>evt [:bottom-sheet/hide])
|
||||
(take-pic))}]
|
||||
[quo/list-item {:accessibility-label :pick-photo
|
||||
:icon :main-icons/gallery
|
||||
:theme :accent
|
||||
:title (i18n/label :t/community-image-pick)
|
||||
:on-press #(do
|
||||
(>evt [:bottom-sheet/hide])
|
||||
(pick-pic))}]
|
||||
[quo/list-item
|
||||
{:accessibility-label :take-photo
|
||||
:theme :accent
|
||||
:icon :main-icons/camera
|
||||
:title (i18n/label :t/community-image-take)
|
||||
:on-press #(do
|
||||
(rf/dispatch [:bottom-sheet/hide])
|
||||
(take-pic))}]
|
||||
[quo/list-item
|
||||
{:accessibility-label :pick-photo
|
||||
:icon :main-icons/gallery
|
||||
:theme :accent
|
||||
:title (i18n/label :t/community-image-pick)
|
||||
:on-press #(do
|
||||
(rf/dispatch [:bottom-sheet/hide])
|
||||
(pick-pic))}]
|
||||
(when (and has-picture (not editing?))
|
||||
[quo/list-item {:accessibility-label :remove-photo
|
||||
:icon :main-icons/delete
|
||||
:theme :accent
|
||||
:title (i18n/label :t/community-image-remove)
|
||||
:on-press #(do
|
||||
(>evt [:bottom-sheet/hide])
|
||||
(>evt [::communities/remove-field :image]))}])]))
|
||||
[quo/list-item
|
||||
{:accessibility-label :remove-photo
|
||||
:icon :main-icons/delete
|
||||
:theme :accent
|
||||
:title (i18n/label :t/community-image-remove)
|
||||
:on-press #(do
|
||||
(rf/dispatch [:bottom-sheet/hide])
|
||||
(rf/dispatch [::communities/remove-field :image]))}])]))
|
||||
|
||||
(defn photo-picker []
|
||||
(let [{:keys [image editing?]} (<sub [:communities/create])]
|
||||
[rn/view {:style {:padding-top 16
|
||||
:align-items :center}}
|
||||
[rn/touchable-opacity {:on-press #(>evt [:bottom-sheet/show-sheet
|
||||
{:content (bottom-sheet (boolean image) editing?)}])}
|
||||
[rn/view {:style {:width 128
|
||||
:height 128}}
|
||||
[rn/view {:style {:flex 1
|
||||
:border-radius 64
|
||||
:background-color (colors/get-color :ui-01)
|
||||
:justify-content :center
|
||||
:align-items :center}}
|
||||
(defn photo-picker
|
||||
[]
|
||||
(let [{:keys [image editing?]} (rf/sub [:communities/create])]
|
||||
[rn/view
|
||||
{:style {:padding-top 16
|
||||
:align-items :center}}
|
||||
[rn/touchable-opacity
|
||||
{:on-press #(rf/dispatch [:bottom-sheet/show-sheet
|
||||
{:content (bottom-sheet (boolean image) editing?)}])}
|
||||
[rn/view
|
||||
{:style {:width 128
|
||||
:height 128}}
|
||||
[rn/view
|
||||
{:style {:flex 1
|
||||
:border-radius 64
|
||||
:background-color (colors/get-color :ui-01)
|
||||
:justify-content :center
|
||||
:align-items :center}}
|
||||
(if image
|
||||
[rn/image {:source (utils.image/source image)
|
||||
:style {:width 128
|
||||
:height 128
|
||||
:border-radius 64}
|
||||
:resize-mode :cover
|
||||
:accessibility-label :community-image}]
|
||||
[rn/image
|
||||
{:source (utils.image/source image)
|
||||
:style {:width 128
|
||||
:height 128
|
||||
:border-radius 64}
|
||||
:resize-mode :cover
|
||||
:accessibility-label :community-image}]
|
||||
[:<>
|
||||
[icons/icon :main-icons/photo {:color (colors/get-color :icon-02)}]
|
||||
[quo/text {:color :secondary}
|
||||
(i18n/label :t/community-thumbnail-upload)]])]
|
||||
[rn/view {:style {:position :absolute
|
||||
:top 0
|
||||
:right 7}}
|
||||
[rn/view {:style {:width 40
|
||||
:height 40
|
||||
:background-color (colors/get-color :interactive-01)
|
||||
:border-radius 20
|
||||
:align-items :center
|
||||
:justify-content :center
|
||||
:shadow-offset {:width 0 :height 1}
|
||||
:shadow-radius 6
|
||||
:shadow-opacity 1
|
||||
:shadow-color (colors/get-color :shadow-01)
|
||||
:elevation 2}}
|
||||
[rn/view
|
||||
{:style {:position :absolute
|
||||
:top 0
|
||||
:right 7}}
|
||||
[rn/view
|
||||
{:style {:width 40
|
||||
:height 40
|
||||
:background-color (colors/get-color :interactive-01)
|
||||
:border-radius 20
|
||||
:align-items :center
|
||||
:justify-content :center
|
||||
:shadow-offset {:width 0 :height 1}
|
||||
:shadow-radius 6
|
||||
:shadow-opacity 1
|
||||
:shadow-color (colors/get-color :shadow-01)
|
||||
:elevation 2}}
|
||||
[icons/icon :main-icons/add {:color colors/white}]]]]]]))
|
||||
|
||||
(defn countable-label [{:keys [label text max-length]}]
|
||||
[rn/view {:style {:padding-bottom 10
|
||||
:justify-content :space-between
|
||||
:align-items :flex-end
|
||||
:flex-direction :row
|
||||
:flex-wrap :nowrap}}
|
||||
(defn countable-label
|
||||
[{:keys [label text max-length]}]
|
||||
[rn/view
|
||||
{:style {:padding-bottom 10
|
||||
:justify-content :space-between
|
||||
:align-items :flex-end
|
||||
:flex-direction :row
|
||||
:flex-wrap :nowrap}}
|
||||
[quo/text label]
|
||||
[quo/text {:size :small
|
||||
:color (if (> (count text) max-length)
|
||||
:negative
|
||||
:secondary)}
|
||||
[quo/text
|
||||
{:size :small
|
||||
:color (if (> (count text) max-length)
|
||||
:negative
|
||||
:secondary)}
|
||||
(str (count text) "/" max-length)]])
|
||||
|
||||
(defn form []
|
||||
(let [{:keys [name description membership editing?]} (<sub [:communities/create])]
|
||||
[rn/scroll-view {:keyboard-should-persist-taps :handled
|
||||
:style {:flex 1}
|
||||
:content-container-style {:padding-vertical 16}}
|
||||
[rn/view {:style {:padding-bottom 16
|
||||
:padding-top 10
|
||||
:padding-horizontal 16}}
|
||||
[countable-label {:label (i18n/label :t/name-your-community)
|
||||
:text name
|
||||
:max-length max-name-length}]
|
||||
(defn form
|
||||
[]
|
||||
(let [{:keys [name description membership editing?]} (rf/sub [:communities/create])]
|
||||
[rn/scroll-view
|
||||
{:keyboard-should-persist-taps :handled
|
||||
:style {:flex 1}
|
||||
:content-container-style {:padding-vertical 16}}
|
||||
[rn/view
|
||||
{:style {:padding-bottom 16
|
||||
:padding-top 10
|
||||
:padding-horizontal 16}}
|
||||
[countable-label
|
||||
{:label (i18n/label :t/name-your-community)
|
||||
:text name
|
||||
:max-length max-name-length}]
|
||||
[quo/text-input
|
||||
{:placeholder (i18n/label :t/name-your-community-placeholder)
|
||||
:default-value name
|
||||
:on-change-text #(>evt [::communities/create-field :name %])
|
||||
:on-change-text #(rf/dispatch [::communities/create-field :name %])
|
||||
:auto-focus true}]]
|
||||
[rn/view {:style {:padding-bottom 16
|
||||
:padding-top 10
|
||||
:padding-horizontal 16}}
|
||||
[countable-label {:label (i18n/label :t/give-a-short-description-community)
|
||||
:text description
|
||||
:max-length max-description-length}]
|
||||
[rn/view
|
||||
{:style {:padding-bottom 16
|
||||
:padding-top 10
|
||||
:padding-horizontal 16}}
|
||||
[countable-label
|
||||
{:label (i18n/label :t/give-a-short-description-community)
|
||||
:text description
|
||||
:max-length max-description-length}]
|
||||
[quo/text-input
|
||||
{:placeholder (i18n/label :t/give-a-short-description-community)
|
||||
:multiline true
|
||||
: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}
|
||||
(i18n/label :t/community-thumbnail-image)]
|
||||
[photo-picker]
|
||||
(when-not editing? [:<>
|
||||
[quo/separator {:style {:margin-vertical 10}}]
|
||||
[quo/list-item {:title (i18n/label :t/membership-button)
|
||||
:accessory-text (i18n/label (get-in memberships/options [membership :title] :t/membership-none))
|
||||
:accessory :text
|
||||
:on-press #(>evt [:navigate-to :community-membership])
|
||||
:chevron true
|
||||
:size :small}]
|
||||
[quo/list-footer
|
||||
(i18n/label (get-in memberships/options [membership :description] :t/membership-none-placeholder))]])]))
|
||||
(when-not editing?
|
||||
[:<>
|
||||
[quo/separator {:style {:margin-vertical 10}}]
|
||||
[quo/list-item
|
||||
{:title (i18n/label :t/membership-button)
|
||||
:accessory-text (i18n/label (get-in memberships/options
|
||||
[membership :title]
|
||||
:t/membership-none))
|
||||
:accessory :text
|
||||
: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 []
|
||||
(let [{:keys [name description]} (<sub [:communities/create])]
|
||||
(defn view
|
||||
[]
|
||||
(let [{:keys [name description]} (rf/sub [:communities/create])]
|
||||
[rn/keyboard-avoiding-view {:style {:flex 1}}
|
||||
[form]
|
||||
[toolbar/toolbar
|
||||
{:show-border? true
|
||||
:center
|
||||
[quo/button {:disabled (not (valid? name description))
|
||||
:type :secondary
|
||||
:on-press #(debounce/dispatch-and-chill [::communities/create-confirmation-pressed] 3000)}
|
||||
[quo/button
|
||||
{:disabled (not (valid? name description))
|
||||
:type :secondary
|
||||
:on-press #(debounce/dispatch-and-chill [::communities/create-confirmation-pressed] 3000)}
|
||||
(i18n/label :t/create)]}]]))
|
||||
|
||||
@@ -1,42 +1,46 @@
|
||||
(ns status-im.ui.screens.communities.create-category
|
||||
(:require [status-im.ui.components.keyboard-avoid-presentation :as kb-presentation]
|
||||
[status-im.utils.handlers :refer [<sub]]
|
||||
[status-im.ui.components.toolbar :as toolbar]
|
||||
[status-im.ui.components.react :as react]
|
||||
(:require [clojure.string :as string]
|
||||
[quo.core :as quo]
|
||||
[utils.debounce :as debounce]
|
||||
[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]
|
||||
[reagent.core :as reagent]
|
||||
[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))
|
||||
(<= (count category-name) 30)))
|
||||
|
||||
(def selected-items (reagent/atom #{}))
|
||||
|
||||
(defn render-fn [{:keys [chat-id] :as home-item}]
|
||||
(let [selected (get @selected-items chat-id)
|
||||
(defn render-fn
|
||||
[{:keys [chat-id] :as home-item}]
|
||||
(let [selected (get @selected-items chat-id)
|
||||
on-change (fn []
|
||||
(swap! selected-items #(if selected (disj % chat-id) (conj % chat-id))))]
|
||||
[react/view {:flex-direction :row :flex 1 :align-items :center}
|
||||
[react/view {:padding-left 16}
|
||||
[quo/checkbox {:value selected
|
||||
:on-change on-change}]]
|
||||
[quo/checkbox
|
||||
{:value selected
|
||||
:on-change on-change}]]
|
||||
[react/view {:flex 1}
|
||||
[inner-item/home-list-item-old
|
||||
(assoc home-item :public? true)
|
||||
{:on-press on-change}]]]))
|
||||
|
||||
(defn view []
|
||||
(let [{:keys [community-id]} (<sub [:get-screen-params])
|
||||
category-name (reagent/atom "")
|
||||
(defn view
|
||||
[]
|
||||
(let [{:keys [community-id]} (rf/sub [:get-screen-params])
|
||||
category-name (reagent/atom "")
|
||||
_ (reset! selected-items #{})]
|
||||
(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}}
|
||||
[react/view {:flex 1}
|
||||
[react/view {:padding-horizontal 16}
|
||||
@@ -57,12 +61,13 @@
|
||||
[toolbar/toolbar
|
||||
{:show-border? true
|
||||
:center
|
||||
[quo/button {:disabled (not (valid? @category-name))
|
||||
:type :secondary
|
||||
:on-press #(debounce/dispatch-and-chill
|
||||
[::communities/create-category-confirmation-pressed
|
||||
community-id
|
||||
@category-name
|
||||
(vec @selected-items)]
|
||||
3000)}
|
||||
[quo/button
|
||||
{:disabled (not (valid? @category-name))
|
||||
:type :secondary
|
||||
:on-press #(debounce/dispatch-and-chill
|
||||
[::communities/create-category-confirmation-pressed
|
||||
community-id
|
||||
@category-name
|
||||
(vec @selected-items)]
|
||||
3000)}
|
||||
(i18n/label :t/create)]}]]))))
|
||||
|
||||
@@ -1,65 +1,72 @@
|
||||
(ns status-im.ui.screens.communities.create-channel
|
||||
(:require [clojure.string :as string]
|
||||
[quo.react-native :as rn]
|
||||
[quo.core :as quo]
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
[status-im.ui.components.toolbar :as toolbar]
|
||||
[quo.react-native :as rn]
|
||||
[status-im.communities.core :as communities]
|
||||
[utils.debounce :as debounce]
|
||||
[status-im.utils.handlers :refer [>evt <sub]]
|
||||
[status-im.ui.screens.communities.create :as create]
|
||||
[status-im.ui.components.keyboard-avoid-presentation :as kb-presentation]
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
[status-im.ui.components.emoji-thumbnail.preview :as emoji-thumbnail-preview]
|
||||
[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))
|
||||
(not (string/blank? channel-description))
|
||||
(<= (count channel-name) create/max-name-length)
|
||||
(<= (count channel-description) create/max-description-length)))
|
||||
|
||||
(defn thumbnail []
|
||||
(let [{:keys [color emoji]} (<sub [:communities/create-channel])
|
||||
size styles/emoji-thumbnail-preview-size]
|
||||
(defn thumbnail
|
||||
[]
|
||||
(let [{:keys [color emoji]} (rf/sub [:communities/create-channel])
|
||||
size styles/emoji-thumbnail-preview-size]
|
||||
[rn/view styles/emoji-thumbnail-preview
|
||||
[emoji-thumbnail-preview/emoji-thumbnail-touchable
|
||||
emoji color size
|
||||
#(>evt [:open-modal :community-emoji-thumbnail-picker nil])]]))
|
||||
#(rf/dispatch [:open-modal :community-emoji-thumbnail-picker nil])]]))
|
||||
|
||||
(defn form []
|
||||
(let [{:keys [name description]} (<sub [:communities/create-channel])]
|
||||
[rn/scroll-view {:style {:flex 1}
|
||||
:content-container-style {:padding-bottom 16}}
|
||||
(defn form
|
||||
[]
|
||||
(let [{:keys [name description]} (rf/sub [:communities/create-channel])]
|
||||
[rn/scroll-view
|
||||
{:style {:flex 1}
|
||||
:content-container-style {:padding-bottom 16}}
|
||||
[:<>
|
||||
[thumbnail]
|
||||
[rn/view {:padding-horizontal 16}
|
||||
[quo/text-input
|
||||
{: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
|
||||
:auto-focus false}]]
|
||||
[quo/separator {:style {:margin-vertical 16}}]
|
||||
[rn/view {:padding-horizontal 16}
|
||||
[create/countable-label {:label (i18n/label :t/description)
|
||||
:text description
|
||||
:max-length create/max-description-length}]
|
||||
[create/countable-label
|
||||
{:label (i18n/label :t/description)
|
||||
:text description
|
||||
:max-length create/max-description-length}]
|
||||
[quo/text-input
|
||||
{:placeholder (i18n/label :t/give-a-short-description-community)
|
||||
:multiline true
|
||||
:default-value description
|
||||
:on-change-text #(>evt [::communities/create-channel-field :description %])}]]]]))
|
||||
:on-change-text #(rf/dispatch [::communities/create-channel-field :description %])}]]]]))
|
||||
|
||||
(defn view []
|
||||
(let [{:keys [name description]} (<sub [:communities/create-channel])]
|
||||
(defn view
|
||||
[]
|
||||
(let [{:keys [name description]} (rf/sub [:communities/create-channel])]
|
||||
[kb-presentation/keyboard-avoiding-view {:style {:flex 1}}
|
||||
[quo/separator]
|
||||
[form]
|
||||
[toolbar/toolbar
|
||||
{:show-border? true
|
||||
:center
|
||||
[quo/button {:disabled (not (valid? name description))
|
||||
:type :secondary
|
||||
:on-press #(debounce/dispatch-and-chill
|
||||
[::communities/create-channel-confirmation-pressed]
|
||||
3000)}
|
||||
[quo/button
|
||||
{:disabled (not (valid? name description))
|
||||
:type :secondary
|
||||
:on-press #(debounce/dispatch-and-chill
|
||||
[::communities/create-channel-confirmation-pressed]
|
||||
3000)}
|
||||
(i18n/label :t/create)]}]]))
|
||||
|
||||
@@ -1,19 +1,21 @@
|
||||
(ns status-im.ui.screens.communities.edit
|
||||
(: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.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 []
|
||||
(let [{:keys [name description]} (<sub [:communities/create])]
|
||||
(defn edit
|
||||
[]
|
||||
(let [{:keys [name description]} (rf/sub [:communities/create])]
|
||||
[:<>
|
||||
[community.create/form]
|
||||
[toolbar/toolbar
|
||||
{:show-border? true
|
||||
:center
|
||||
[quo/button {:disabled (not (community.create/valid? name description))
|
||||
:type :secondary
|
||||
:on-press #(>evt [::communities/edit-confirmation-pressed])}
|
||||
[quo/button
|
||||
{:disabled (not (community.create/valid? name description))
|
||||
:type :secondary
|
||||
:on-press #(rf/dispatch [::communities/edit-confirmation-pressed])}
|
||||
(i18n/label :t/save)]}]]))
|
||||
|
||||
@@ -1,27 +1,30 @@
|
||||
(ns status-im.ui.screens.communities.edit-channel
|
||||
(:require [clojure.string :as string]
|
||||
[quo.core :as quo]
|
||||
[status-im.communities.core :as communities]
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
[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]
|
||||
[status-im.utils.handlers :refer [<sub]]
|
||||
[status-im.ui.screens.communities.create-channel :as create-channel]))
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(defn valid? [community-name]
|
||||
(defn valid?
|
||||
[community-name]
|
||||
(not (string/blank? community-name)))
|
||||
|
||||
(defn view []
|
||||
(let [{:keys [name]} (<sub [:communities/create-channel])]
|
||||
(defn view
|
||||
[]
|
||||
(let [{:keys [name]} (rf/sub [:communities/create-channel])]
|
||||
(fn []
|
||||
[:<>
|
||||
[create-channel/form]
|
||||
[toolbar/toolbar
|
||||
{:show-border? true
|
||||
:center
|
||||
[quo/button {:disabled (not (valid? name))
|
||||
:type :secondary
|
||||
:on-press #(debounce/dispatch-and-chill
|
||||
[::communities/edit-channel-confirmation-pressed]
|
||||
3000)}
|
||||
[quo/button
|
||||
{:disabled (not (valid? name))
|
||||
:type :secondary
|
||||
:on-press #(debounce/dispatch-and-chill
|
||||
[::communities/edit-channel-confirmation-pressed]
|
||||
3000)}
|
||||
(i18n/label :t/save)]}]])))
|
||||
|
||||
@@ -1,20 +1,23 @@
|
||||
(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]
|
||||
[quo.core :as quo]
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
[status-im.utils.handlers :refer [>evt]]
|
||||
[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 "")]
|
||||
(fn []
|
||||
[:<>
|
||||
[rn/scroll-view {:style {:flex 1}
|
||||
:content-container-style {:padding 16}}
|
||||
[rn/view {:style {:padding-bottom 16
|
||||
:padding-top 10}}
|
||||
[rn/scroll-view
|
||||
{:style {:flex 1}
|
||||
:content-container-style {:padding 16}}
|
||||
[rn/view
|
||||
{:style {:padding-bottom 16
|
||||
:padding-top 10}}
|
||||
[quo/text-input
|
||||
{:label (i18n/label :t/community-key)
|
||||
:placeholder (i18n/label :t/community-key-placeholder)
|
||||
@@ -24,7 +27,8 @@
|
||||
|
||||
[toolbar/toolbar
|
||||
{:show-border? true
|
||||
:center [quo/button {:disabled (= @community-key "")
|
||||
:type :secondary
|
||||
:on-press #(>evt [::communities/import @community-key])}
|
||||
:center [quo/button
|
||||
{:disabled (= @community-key "")
|
||||
:type :secondary
|
||||
:on-press #(rf/dispatch [::communities/import @community-key])}
|
||||
(i18n/label :t/import)]}]])))
|
||||
|
||||
@@ -1,21 +1,24 @@
|
||||
(ns status-im.ui.screens.communities.invite
|
||||
(:require [reagent.core :as reagent]
|
||||
[quo.react-native :as rn]
|
||||
(:require [clojure.string :as string]
|
||||
[quo.core :as quo]
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
[status-im.constants :as constants]
|
||||
[status-im.ui.components.toolbar :as toolbar]
|
||||
[status-im.utils.handlers :refer [<sub >evt-once]]
|
||||
[quo.react-native :as rn]
|
||||
[reagent.core :as reagent]
|
||||
[status-im.communities.core :as communities]
|
||||
[status-im.ui.components.topbar :as topbar]
|
||||
[status-im.ui.components.chat-icon.screen :as chat-icon.screen]
|
||||
[status-im.constants :as constants]
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
[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
|
||||
:padding-vertical 8}}
|
||||
[rn/view
|
||||
{:style {:padding-horizontal 16
|
||||
:padding-vertical 8}}
|
||||
[quo/text-input
|
||||
{:label (i18n/label :t/enter-user-pk)
|
||||
:placeholder (i18n/label :t/enter-user-pk)
|
||||
@@ -25,7 +28,8 @@
|
||||
[quo/separator {:style {:margin-vertical 8}}]
|
||||
[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)]
|
||||
[quo/list-item
|
||||
{:title first-name
|
||||
@@ -39,44 +43,51 @@
|
||||
(swap! selected disj public-key)
|
||||
(swap! selected conj public-key)))}]))
|
||||
|
||||
(defn invite []
|
||||
(defn invite
|
||||
[]
|
||||
(let [user-pk (reagent/atom "")
|
||||
contacts-selected (reagent/atom #{})
|
||||
{:keys [invite?]} (<sub [:get-screen-params])]
|
||||
{:keys [invite?]} (rf/sub [:get-screen-params])]
|
||||
(fn []
|
||||
(let [contacts-data (<sub [:contacts/active])
|
||||
(let [contacts-data (rf/sub [:contacts/active])
|
||||
{:keys [permissions
|
||||
can-manage-users?]} (<sub [:communities/edited-community])
|
||||
selected @contacts-selected
|
||||
contacts (map (fn [{:keys [public-key] :as contact}]
|
||||
(assoc contact :active (contains? selected public-key)))
|
||||
contacts-data)
|
||||
can-manage-users?]}
|
||||
(rf/sub [:communities/edited-community])
|
||||
selected @contacts-selected
|
||||
contacts (map (fn [{:keys [public-key] :as contact}]
|
||||
(assoc contact :active (contains? selected public-key)))
|
||||
contacts-data)
|
||||
;; no-membership communities can only be shared
|
||||
can-invite? (and can-manage-users?
|
||||
invite?
|
||||
(not= (:access permissions) constants/community-no-membership-access))]
|
||||
can-invite? (and can-manage-users?
|
||||
invite?
|
||||
(not= (:access permissions) constants/community-no-membership-access))]
|
||||
[:<>
|
||||
[topbar/topbar {:title (i18n/label (if can-invite?
|
||||
:t/invite-people
|
||||
:t/community-share-title))
|
||||
:modal? true}]
|
||||
[rn/flat-list {:style {:flex 1}
|
||||
:content-container-style {:padding-vertical 16}
|
||||
;:header [header user-pk]
|
||||
:render-data {:selected contacts-selected}
|
||||
:render-fn contacts-list-item
|
||||
:key-fn (fn [{:keys [active public-key]}]
|
||||
(str public-key active))
|
||||
:data contacts}]
|
||||
[topbar/topbar
|
||||
{:title (i18n/label (if can-invite?
|
||||
:t/invite-people
|
||||
:t/community-share-title))
|
||||
:modal? true}]
|
||||
[rn/flat-list
|
||||
{:style {:flex 1}
|
||||
:content-container-style {:padding-vertical 16}
|
||||
;:header [header user-pk]
|
||||
:render-data {:selected contacts-selected}
|
||||
:render-fn contacts-list-item
|
||||
:key-fn (fn [{:keys [active public-key]}]
|
||||
(str public-key active))
|
||||
:data contacts}]
|
||||
[toolbar/toolbar
|
||||
{:show-border? true
|
||||
:center
|
||||
[quo/button {:disabled (and (string/blank? @user-pk)
|
||||
[quo/button
|
||||
{:disabled (and (string/blank? @user-pk)
|
||||
(zero? (count selected)))
|
||||
:accessibility-label :share-community-link
|
||||
:type :secondary
|
||||
:on-press #(>evt-once
|
||||
:accessibility-label :share-community-link
|
||||
:type :secondary
|
||||
:on-press #(debounce/dispatch-and-chill
|
||||
[(if can-invite?
|
||||
::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))]}]]))))
|
||||
|
||||
@@ -1,22 +1,24 @@
|
||||
(ns status-im.ui.screens.communities.members
|
||||
(:require [quo.react-native :as rn]
|
||||
[quo.core :as quo]
|
||||
(:require [quo.core :as quo]
|
||||
[quo.react-native :as rn]
|
||||
[reagent.core :as reagent]
|
||||
[status-im.communities.core :as communities]
|
||||
[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.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]
|
||||
(>evt [:bottom-sheet/hide])
|
||||
(>evt event))
|
||||
(defn hide-sheet-and-dispatch
|
||||
[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
|
||||
{:theme :accent
|
||||
@@ -30,30 +32,36 @@
|
||||
(when can-kick-users?
|
||||
[:<>
|
||||
[quo/separator {:style {:margin-vertical 8}}]
|
||||
[quo/list-item {:theme :negative
|
||||
:icon :main-icons/arrow-left
|
||||
:title (i18n/label :t/member-kick)
|
||||
:on-press #(>evt [::communities/member-kick community-id public-key])}]])
|
||||
[quo/list-item
|
||||
{:theme :negative
|
||||
:icon :main-icons/arrow-left
|
||||
:title (i18n/label :t/member-kick)
|
||||
:on-press #(rf/dispatch [::communities/member-kick community-id public-key])}]])
|
||||
(when can-manage-users?
|
||||
[:<>
|
||||
[quo/list-item {:theme :negative
|
||||
:icon :main-icons/cancel
|
||||
:title (i18n/label :t/member-ban)
|
||||
:on-press #(>evt [::communities/member-ban community-id public-key])}]])
|
||||
[quo/list-item
|
||||
{:theme :negative
|
||||
:icon :main-icons/cancel
|
||||
:title (i18n/label :t/member-ban)
|
||||
:on-press #(rf/dispatch [::communities/member-ban community-id public-key])}]])
|
||||
(when admin?
|
||||
[:<>
|
||||
[quo/list-item {:theme :accent
|
||||
:icon :main-icons/make-admin
|
||||
:title (i18n/label :t/make-moderator)
|
||||
:on-press #(>evt [:community.member/add-role community-id public-key constants/community-member-role-moderator])}]])])
|
||||
[quo/list-item
|
||||
{:theme :accent
|
||||
:icon :main-icons/make-admin
|
||||
: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
|
||||
my-public-key
|
||||
can-manage-users?
|
||||
can-kick-users?
|
||||
admin?]}]
|
||||
(let [member (<sub [:contacts/contact-by-identity public-key])
|
||||
[first-name second-name] (<sub [:contacts/contact-two-names-by-identity public-key])]
|
||||
(defn render-member
|
||||
[public-key _ _
|
||||
{:keys [community-id
|
||||
my-public-key
|
||||
can-manage-users?
|
||||
can-kick-users?
|
||||
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
|
||||
{:title first-name
|
||||
:subtitle second-name
|
||||
@@ -62,66 +70,78 @@
|
||||
{:public-key public-key
|
||||
:photo-path (multiaccounts/displayed-photo member)}]
|
||||
:accessory (when (not= public-key my-public-key)
|
||||
[quo/button {:on-press
|
||||
#(>evt [:bottom-sheet/show-sheet
|
||||
{:content (fn []
|
||||
[member-sheet first-name member community-id can-kick-users? can-manage-users? admin?])}])
|
||||
:type :icon
|
||||
:theme :icon
|
||||
:accessibility-label :menu-option}
|
||||
[quo/button
|
||||
{:on-press
|
||||
#(rf/dispatch [:bottom-sheet/show-sheet
|
||||
{:content (fn []
|
||||
[member-sheet first-name member community-id
|
||||
can-kick-users? can-manage-users? admin?])}])
|
||||
:type :icon
|
||||
:theme :icon
|
||||
:accessibility-label :menu-option}
|
||||
:main-icons/more])}]))
|
||||
|
||||
(defn header [community-id]
|
||||
(defn header
|
||||
[community-id]
|
||||
[:<>
|
||||
[quo/list-item {:icon :main-icons/share
|
||||
:title (i18n/label :t/invite-people)
|
||||
:accessibility-label :community-invite-people
|
||||
:theme :accent
|
||||
:on-press #(>evt [:communities/invite-people-pressed community-id])}]
|
||||
[quo/list-item
|
||||
{:icon :main-icons/share
|
||||
:title (i18n/label :t/invite-people)
|
||||
:accessibility-label :community-invite-people
|
||||
:theme :accent
|
||||
:on-press #(rf/dispatch [:communities/invite-people-pressed community-id])}]
|
||||
[quo/separator {:style {:margin-vertical 8}}]])
|
||||
|
||||
(defn requests-to-join [community-id]
|
||||
(let [requests (<sub [:communities/requests-to-join-for-community community-id])
|
||||
(defn requests-to-join
|
||||
[community-id]
|
||||
(let [requests (rf/sub [:communities/requests-to-join-for-community community-id])
|
||||
requests-count (count requests)]
|
||||
[:<>
|
||||
[quo/list-item {:chevron true
|
||||
:accessory
|
||||
[react/view {:flex-direction :row}
|
||||
(when (pos? requests-count)
|
||||
[unviewed-indicator/unviewed-indicator requests-count])]
|
||||
:on-press #(>evt [:navigate-to :community-requests-to-join {:community-id community-id}])
|
||||
:title (i18n/label :t/membership-requests)}]
|
||||
[quo/list-item
|
||||
{:chevron true
|
||||
:accessory
|
||||
[react/view {:flex-direction :row}
|
||||
(when (pos? requests-count)
|
||||
[unviewed-indicator/unviewed-indicator requests-count])]
|
||||
: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}}]]))
|
||||
|
||||
(defn members []
|
||||
(let [{:keys [community-id]} (<sub [:get-screen-params])]
|
||||
(defn members
|
||||
[]
|
||||
(let [{:keys [community-id]} (rf/sub [:get-screen-params])]
|
||||
(fn []
|
||||
(let [my-public-key (<sub [:multiaccount/public-key])
|
||||
(let [my-public-key (rf/sub [:multiaccount/public-key])
|
||||
{:keys [permissions
|
||||
can-manage-users?
|
||||
admin]} (<sub [:communities/community community-id])
|
||||
sorted-members (<sub [:communities/sorted-community-members
|
||||
community-id])]
|
||||
admin]}
|
||||
(rf/sub [:communities/community community-id])
|
||||
sorted-members (rf/sub [:communities/sorted-community-members
|
||||
community-id])]
|
||||
[:<>
|
||||
[topbar/topbar {:title (i18n/label :t/community-members-title)
|
||||
:subtitle (str (count sorted-members))}]
|
||||
[topbar/topbar
|
||||
{:title (i18n/label :t/community-members-title)
|
||||
:subtitle (str (count sorted-members))}]
|
||||
[header community-id]
|
||||
(when (and can-manage-users? (= constants/community-on-request-access (:access permissions)))
|
||||
[requests-to-join community-id])
|
||||
[rn/flat-list {:data (keys sorted-members)
|
||||
:render-data {:community-id community-id
|
||||
:my-public-key my-public-key
|
||||
:can-kick-users? (and can-manage-users?
|
||||
(not= (:access permissions)
|
||||
constants/community-no-membership-access))
|
||||
:can-manage-users? can-manage-users?
|
||||
:admin? admin}
|
||||
:key-fn identity
|
||||
:render-fn render-member}]]))))
|
||||
[rn/flat-list
|
||||
{:data (keys sorted-members)
|
||||
:render-data {:community-id community-id
|
||||
:my-public-key my-public-key
|
||||
:can-kick-users? (and can-manage-users?
|
||||
(not= (:access permissions)
|
||||
constants/community-no-membership-access))
|
||||
:can-manage-users? can-manage-users?
|
||||
:admin? admin}
|
||||
:key-fn identity
|
||||
:render-fn render-member}]]))))
|
||||
|
||||
(defn members-container []
|
||||
(defn members-container
|
||||
[]
|
||||
(reagent/create-class
|
||||
{:display-name "community-members-view"
|
||||
: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}))
|
||||
|
||||
@@ -1,41 +1,47 @@
|
||||
(ns status-im.ui.screens.communities.membership
|
||||
(:require [quo.react-native :as rn]
|
||||
[status-im.ui.components.toolbar :as toolbar]
|
||||
[quo.core :as quo]
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
[status-im.utils.handlers :refer [>evt <sub]]
|
||||
(:require [quo.core :as quo]
|
||||
[quo.react-native :as rn]
|
||||
[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
|
||||
{:title :t/membership-approval
|
||||
:description :t/membership-approval-description}
|
||||
constants/community-no-membership-access
|
||||
{:title :t/membership-free
|
||||
:description :t/membership-free-description}})
|
||||
(def options
|
||||
{constants/community-on-request-access
|
||||
{:title :t/membership-approval
|
||||
:description :t/membership-approval-description}
|
||||
constants/community-no-membership-access
|
||||
{: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)
|
||||
:size :small
|
||||
:accessory :radio
|
||||
:active selected
|
||||
:on-press on-select}]
|
||||
[quo/list-item
|
||||
{:title (i18n/label title)
|
||||
:size :small
|
||||
:accessory :radio
|
||||
:active selected
|
||||
:on-press on-select}]
|
||||
[quo/list-footer
|
||||
(i18n/label description)]
|
||||
[quo/separator {:style {:margin-vertical 8}}]])
|
||||
|
||||
(defn membership []
|
||||
(let [{:keys [membership]} (<sub [:communities/create])]
|
||||
(defn membership
|
||||
[]
|
||||
(let [{:keys [membership]} (rf/sub [:communities/create])]
|
||||
[:<>
|
||||
[rn/scroll-view {}
|
||||
(doall
|
||||
(for [[id o] options]
|
||||
^{:key (str "option-" id)}
|
||||
[option o {:selected (= id membership)
|
||||
:on-select #(>evt [::communities/create-field :membership id])}]))]
|
||||
[option o
|
||||
{:selected (= id membership)
|
||||
:on-select #(rf/dispatch [::communities/create-field :membership id])}]))]
|
||||
[toolbar/toolbar
|
||||
{:show-border? true
|
||||
:center [quo/button {:type :secondary
|
||||
:on-press #(>evt [:navigate-back])}
|
||||
:center [quo/button
|
||||
{:type :secondary
|
||||
:on-press #(rf/dispatch [:navigate-back])}
|
||||
(i18n/label :t/done)]}]]))
|
||||
|
||||
@@ -1,52 +1,57 @@
|
||||
(ns status-im.ui.screens.communities.profile
|
||||
(:require [quo.core :as quo]
|
||||
[status-im.utils.handlers :refer [>evt <sub]]
|
||||
[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]
|
||||
(:require [clojure.string :as string]
|
||||
[quo.core :as quo]
|
||||
[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]
|
||||
[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 []
|
||||
(let [{:keys [community-id]} (<sub [:get-screen-params])]
|
||||
(defn management
|
||||
[]
|
||||
(let [{:keys [community-id]} (rf/sub [:get-screen-params])]
|
||||
(fn []
|
||||
(let [requests-to-join (<sub [:communities/requests-to-join-for-community community-id])
|
||||
community (<sub [:communities/community community-id])
|
||||
(let [requests-to-join (rf/sub [:communities/requests-to-join-for-community community-id])
|
||||
community (rf/sub [:communities/community community-id])
|
||||
{:keys [color members permissions description name admin]} community
|
||||
roles false
|
||||
notifications false
|
||||
show-members-count? (not= (:access permissions) constants/community-no-membership-access)
|
||||
request-membership? (= (:access permissions) constants/community-on-request-access)
|
||||
members-count (count members)]
|
||||
roles false
|
||||
notifications false
|
||||
show-members-count? (not= (:access permissions) constants/community-no-membership-access)
|
||||
request-membership? (= (:access permissions) constants/community-on-request-access)
|
||||
members-count (count members)]
|
||||
[:<>
|
||||
[quo/animated-header {:left-accessories [{:icon :main-icons/arrow-left
|
||||
:accessibility-label :back-button
|
||||
:on-press #(>evt [:navigate-back])}]
|
||||
:right-accessories [{:icon :main-icons/share
|
||||
:accessibility-label :invite-button
|
||||
:on-press #(>evt [:communities/share-community-pressed community-id])}]
|
||||
:extended-header (profile-header/extended-header
|
||||
{:title name
|
||||
:color (or color (rand-nth colors/chat-colors))
|
||||
:photo (if (= community-id constants/status-community-id)
|
||||
(:uri
|
||||
(rn/resolve-asset-source
|
||||
(resources/get-image :status-logo)))
|
||||
(get-in community [:images :large :uri]))
|
||||
:membership (when request-membership?
|
||||
(i18n/label :t/membership-approval))
|
||||
:subtitle (if show-members-count?
|
||||
(i18n/label-pluralize members-count :t/community-members {:count members-count})
|
||||
(i18n/label :t/open-membership))
|
||||
:community? true})
|
||||
:use-insets true}
|
||||
[quo/animated-header
|
||||
{:left-accessories [{:icon :main-icons/arrow-left
|
||||
:accessibility-label :back-button
|
||||
:on-press #(rf/dispatch [:navigate-back])}]
|
||||
:right-accessories [{:icon :main-icons/share
|
||||
:accessibility-label :invite-button
|
||||
:on-press #(rf/dispatch [:communities/share-community-pressed
|
||||
community-id])}]
|
||||
:extended-header (profile-header/extended-header
|
||||
{:title name
|
||||
:color (or color (rand-nth colors/chat-colors))
|
||||
:photo (if (= community-id constants/status-community-id)
|
||||
(:uri
|
||||
(rn/resolve-asset-source
|
||||
(resources/get-image :status-logo)))
|
||||
(get-in community [:images :large :uri]))
|
||||
:membership (when request-membership?
|
||||
(i18n/label :t/membership-approval))
|
||||
:subtitle (if show-members-count?
|
||||
(i18n/label-pluralize members-count
|
||||
:t/community-members
|
||||
{:count members-count})
|
||||
(i18n/label :t/open-membership))
|
||||
:community? true})
|
||||
:use-insets true}
|
||||
[:<>
|
||||
(when-not (string/blank? description)
|
||||
[:<>
|
||||
@@ -55,57 +60,67 @@
|
||||
[quo/separator {:style {:margin-vertical 8}}]])
|
||||
[:<>
|
||||
(let [link (communities/universal-link community-id)]
|
||||
[react/view {:padding-vertical 10
|
||||
:padding-horizontal 16}
|
||||
[react/view
|
||||
{:padding-vertical 10
|
||||
:padding-horizontal 16}
|
||||
[react/view {:margin-bottom 20}
|
||||
[quo/text {:color :secondary} (i18n/label :t/community-link)]]
|
||||
[copyable-text/copyable-text-view
|
||||
{:copied-text link}
|
||||
[react/view {:border-radius 16
|
||||
:padding-horizontal 16
|
||||
:padding-vertical 11
|
||||
:background-color colors/blue-light}
|
||||
[react/view
|
||||
{:border-radius 16
|
||||
:padding-horizontal 16
|
||||
:padding-vertical 11
|
||||
:background-color colors/blue-light}
|
||||
[quo/text {:color :link} (subs link 8)]]]])
|
||||
[quo/separator {:style {:margin-vertical 8}}]]
|
||||
(when show-members-count?
|
||||
[quo/list-item {:chevron true
|
||||
:accessory
|
||||
[react/view {:flex-direction :row}
|
||||
(when (pos? members-count)
|
||||
[quo/text {:color :secondary} (str members-count)])
|
||||
[unviewed-indicator/unviewed-indicator (count requests-to-join)]]
|
||||
:on-press #(>evt [:navigate-to :community-members {:community-id community-id}])
|
||||
:title (i18n/label :t/members-label)
|
||||
:icon :main-icons/group-chat}])
|
||||
[quo/list-item
|
||||
{:chevron true
|
||||
:accessory
|
||||
[react/view {:flex-direction :row}
|
||||
(when (pos? members-count)
|
||||
[quo/text {:color :secondary} (str members-count)])
|
||||
[unviewed-indicator/unviewed-indicator (count requests-to-join)]]
|
||||
:on-press #(rf/dispatch [:navigate-to :community-members {:community-id community-id}])
|
||||
:title (i18n/label :t/members-label)
|
||||
:icon :main-icons/group-chat}])
|
||||
(when (and admin roles)
|
||||
[quo/list-item {:chevron true
|
||||
:title (i18n/label :t/commonuity-role)
|
||||
:icon :main-icons/objects}])
|
||||
[quo/list-item
|
||||
{:chevron true
|
||||
:title (i18n/label :t/commonuity-role)
|
||||
:icon :main-icons/objects}])
|
||||
(when notifications
|
||||
[quo/list-item {:chevron true
|
||||
:title (i18n/label :t/chat-notification-preferences)
|
||||
:icon :main-icons/notification}])
|
||||
[quo/list-item
|
||||
{:chevron true
|
||||
:title (i18n/label :t/chat-notification-preferences)
|
||||
:icon :main-icons/notification}])
|
||||
(when (or show-members-count? notifications (and admin roles))
|
||||
[quo/separator {:style {:margin-vertical 8}}])
|
||||
(when admin
|
||||
[quo/list-item {:theme :accent
|
||||
:icon :main-icons/edit
|
||||
:title (i18n/label :t/edit-community)
|
||||
:on-press #(>evt [::communities/open-edit-community community-id])}])
|
||||
[quo/list-item {:theme :accent
|
||||
:icon :main-icons/arrow-left
|
||||
:title (i18n/label :t/leave-community)
|
||||
:on-press #(>evt [:communities/leave community-id])}]
|
||||
[quo/list-item
|
||||
{:theme :accent
|
||||
:icon :main-icons/edit
|
||||
:title (i18n/label :t/edit-community)
|
||||
:on-press #(rf/dispatch [::communities/open-edit-community community-id])}])
|
||||
[quo/list-item
|
||||
{:theme :accent
|
||||
:icon :main-icons/arrow-left
|
||||
:title (i18n/label :t/leave-community)
|
||||
:on-press #(rf/dispatch [:communities/leave community-id])}]
|
||||
;; Disable as not implemented yet
|
||||
(when false
|
||||
[quo/list-item {:theme :negative
|
||||
:icon :main-icons/delete
|
||||
:title (i18n/label :t/delete)
|
||||
:on-press #(>evt [::communities/delete-community community-id])}])]]]))))
|
||||
[quo/list-item
|
||||
{:theme :negative
|
||||
:icon :main-icons/delete
|
||||
:title (i18n/label :t/delete)
|
||||
:on-press #(rf/dispatch [::communities/delete-community community-id])}])]]]))))
|
||||
|
||||
(defn management-container []
|
||||
(defn management-container
|
||||
[]
|
||||
(reagent/create-class
|
||||
{:display-name "community-profile-view"
|
||||
{:display-name "community-profile-view"
|
||||
:component-did-mount (fn []
|
||||
(communities/fetch-requests-to-join! (get (<sub [:get-screen-params]) :community-id)))
|
||||
:reagent-render management}))
|
||||
(communities/fetch-requests-to-join! (get (rf/sub [:get-screen-params])
|
||||
:community-id)))
|
||||
:reagent-render management}))
|
||||
|
||||
@@ -1,43 +1,46 @@
|
||||
(ns status-im.ui.screens.communities.reorder-categories
|
||||
(:require [quo.core :as quo]
|
||||
[clojure.walk :as walk]
|
||||
[quo.react-native :as rn]
|
||||
[reagent.core :as reagent]
|
||||
(:require [clojure.set :as clojure.set]
|
||||
[clojure.string :as string]
|
||||
[clojure.set :as clojure.set]
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
[status-im.utils.utils :as utils]
|
||||
[status-im.constants :as constants]
|
||||
[clojure.walk :as walk]
|
||||
[quo.core :as quo]
|
||||
[quo.design-system.colors :as colors]
|
||||
[status-im.utils.platform :as platform]
|
||||
[status-im.communities.core :as communities]
|
||||
[status-im.utils.handlers :refer [>evt <sub]]
|
||||
[status-im.ui.components.icons.icons :as icons]
|
||||
[quo.react-native :as rn]
|
||||
[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.tabs :as tabs]
|
||||
[status-im.ui.components.topbar :as topbar]
|
||||
[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 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
|
||||
{:title (i18n/label :t/delete-confirmation)
|
||||
:content (i18n/label :t/delete-chat-confirmation)
|
||||
: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
|
||||
{:title (i18n/label :t/delete-confirmation)
|
||||
:content (i18n/label :t/delete-category-confirmation)
|
||||
: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]
|
||||
(= tab :categories)))
|
||||
|
||||
@@ -46,19 +49,21 @@
|
||||
(let [chat-id (string/replace id community-id "")
|
||||
background-color (if is-active? colors/gray-lighter colors/white)
|
||||
home-item (clojure.set/rename-keys home-item {:id :chat-id})]
|
||||
[rn/view {:accessibility-label :chat-item
|
||||
:style (merge styles/category-item
|
||||
{:background-color background-color})}
|
||||
[rn/view
|
||||
{:accessibility-label :chat-item
|
||||
:style (merge styles/category-item
|
||||
{:background-color background-color})}
|
||||
[rn/touchable-opacity
|
||||
{:accessibility-label :delete-community-chat
|
||||
:on-press #(show-delete-chat-confirmation community-id chat-id)}
|
||||
[icons/icon :main-icons/delete-circle {:no-color true}]]
|
||||
[rn/view {:flex 1}
|
||||
[inner-item/home-list-item-old (assoc home-item :edit? true) {:active-opacity 1}]]
|
||||
[rn/touchable-opacity {:on-long-press drag
|
||||
:delay-long-press 100
|
||||
:accessibility-label :chat-drag-handle
|
||||
:style {:padding 20}}
|
||||
[rn/touchable-opacity
|
||||
{:on-long-press drag
|
||||
:delay-long-press 100
|
||||
:accessibility-label :chat-drag-handle
|
||||
:style {:padding 20}}
|
||||
[icons/icon :main-icons/reorder-handle {:no-color true :width 18 :height 12}]]]))
|
||||
|
||||
(defn category-item
|
||||
@@ -67,9 +72,10 @@
|
||||
category-none? (string/blank? id)]
|
||||
[:<>
|
||||
[quo/separator]
|
||||
[rn/view {:accessibility-label :category-item
|
||||
:style (merge styles/category-item
|
||||
{:background-color background-color})}
|
||||
[rn/view
|
||||
{:accessibility-label :category-item
|
||||
:style (merge styles/category-item
|
||||
{:background-color background-color})}
|
||||
(if (not (categories-tab?))
|
||||
[icons/icon :main-icons/channel-category {:color colors/gray}]
|
||||
[rn/touchable-opacity
|
||||
@@ -79,10 +85,11 @@
|
||||
[rn/view {:flex 1}
|
||||
[rn/text {:style {:font-size 17 :margin-left 10 :color colors/black}} name]]
|
||||
(when (and (not category-none?) (categories-tab?))
|
||||
[rn/touchable-opacity {:accessibility-label :category-drag-handle
|
||||
:on-long-press drag
|
||||
:delay-long-press 100
|
||||
:style {:padding 20}}
|
||||
[rn/touchable-opacity
|
||||
{:accessibility-label :category-drag-handle
|
||||
:on-long-press drag
|
||||
:delay-long-press 100
|
||||
:style {:padding 20}}
|
||||
[icons/icon :main-icons/reorder-handle {:no-color true :width 18 :height 12}]])]]))
|
||||
|
||||
(defn render-fn
|
||||
@@ -98,80 +105,105 @@
|
||||
(if going-up?
|
||||
(if (= chat-type constants/community-chat-type)
|
||||
[categoryID (if second-call? (inc position) position)]
|
||||
(if second-call? [id 0]
|
||||
(calculate-chat-new-position-and-category (dec to) true old-category true)))
|
||||
(if second-call?
|
||||
[id 0]
|
||||
(calculate-chat-new-position-and-category (dec to) true old-category true)))
|
||||
(if (= chat-type constants/community-chat-type)
|
||||
(if (= categoryID old-category)
|
||||
[categoryID position]
|
||||
[categoryID (inc position)]) [id 0]))]
|
||||
[categoryID (inc position)])
|
||||
[id 0]))]
|
||||
[new-category new-position]))
|
||||
|
||||
(defn update-local-atom [data-js]
|
||||
(defn update-local-atom
|
||||
[data-js]
|
||||
(reset! data data-js)
|
||||
(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)
|
||||
[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 "")]
|
||||
(when-not (and (= new-position position) (= new-category categoryID))
|
||||
(update-local-atom data-js)
|
||||
(>evt [::communities/reorder-community-category-chat
|
||||
community-id new-category chat-id new-position]))))
|
||||
(rf/dispatch [::communities/reorder-community-category-chat
|
||||
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)]
|
||||
(when (and (< to (count @data)) (not= position to) (not= id ""))
|
||||
(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?)
|
||||
(on-drag-end-category from to data-js)
|
||||
(when-not (= to 0) (on-drag-end-chat from to data-js))))
|
||||
|
||||
(defn reset-data [categories chats]
|
||||
(reset! data (if (categories-tab?)
|
||||
categories
|
||||
(walk/postwalk-replace
|
||||
{:chat-id :id}
|
||||
(reduce (fn [acc category]
|
||||
(-> acc
|
||||
(conj category)
|
||||
(into (get chats (:id category))))) [] categories)))))
|
||||
(defn reset-data
|
||||
[categories chats]
|
||||
(reset! data
|
||||
(if (categories-tab?)
|
||||
categories
|
||||
(walk/postwalk-replace
|
||||
{:chat-id :id}
|
||||
(reduce (fn [acc category]
|
||||
(-> acc
|
||||
(conj category)
|
||||
(into (get chats (:id category)))))
|
||||
[]
|
||||
categories)))))
|
||||
|
||||
(defn draggable-list []
|
||||
(defn draggable-list
|
||||
[]
|
||||
[rn/draggable-flat-list
|
||||
{:key-fn :id
|
||||
:data @data
|
||||
:render-fn render-fn
|
||||
:autoscroll-threshold (if platform/android? 150 250)
|
||||
: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}])
|
||||
|
||||
(defn chats_and_categories []
|
||||
(defn chats_and_categories
|
||||
[]
|
||||
(let [{:keys [tab]} @state]
|
||||
[:<>
|
||||
[react/view {:flex-direction :row :margin-horizontal 16 :margin-vertical 8
|
||||
:border-radius 8 :background-color colors/blue-light}
|
||||
[react/view
|
||||
{: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 :categories (i18n/label :t/edit-categories) (= tab :categories)]]]))
|
||||
|
||||
(defn view []
|
||||
(let [{:keys [community-id]} (<sub [:get-screen-params])
|
||||
(defn view
|
||||
[]
|
||||
(let [{:keys [community-id]} (rf/sub [:get-screen-params])
|
||||
{:keys [id name images members permissions color]}
|
||||
(<sub [:communities/community community-id])
|
||||
sorted-categories (<sub [:communities/sorted-categories community-id])
|
||||
categories (if (categories-tab?)
|
||||
sorted-categories
|
||||
(conj sorted-categories
|
||||
{:id ""
|
||||
:position (count sorted-categories)
|
||||
:name (i18n/label :t/none)
|
||||
:community-id community-id}))
|
||||
chats (<sub [:chats/sorted-categories-by-community-id community-id])]
|
||||
(rf/sub [:communities/community community-id])
|
||||
sorted-categories (rf/sub [:communities/sorted-categories
|
||||
community-id])
|
||||
categories (if (categories-tab?)
|
||||
sorted-categories
|
||||
(conj sorted-categories
|
||||
{:id ""
|
||||
:position (count
|
||||
sorted-categories)
|
||||
:name (i18n/label :t/none)
|
||||
:community-id community-id}))
|
||||
chats (rf/sub
|
||||
[:chats/sorted-categories-by-community-id
|
||||
community-id])]
|
||||
(reset-data categories chats)
|
||||
[:<>
|
||||
[topbar/topbar
|
||||
@@ -182,4 +214,4 @@
|
||||
[community/blank-page (i18n/label :t/welcome-community-blank-message-edit-chats)]
|
||||
[:<>
|
||||
[chats_and_categories]
|
||||
[draggable-list]])]))
|
||||
[draggable-list]])]))
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user