Compare commits

...
144 changed files with 10500 additions and 8650 deletions
+1 -2
View File
@@ -1,7 +1,6 @@
{:lint-as {status-im.utils.views/defview clojure.core/defn
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
+27 -25
View File
@@ -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)
+6 -6
View File
@@ -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
+52 -41
View File
@@ -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)))
+9 -9
View File
@@ -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
View File
@@ -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))}))
+51 -44
View File
@@ -1,69 +1,76 @@
;reference https://eips.ethereum.org/EIPS/eip-3085 EIP-3085: Wallet Add Ethereum Chain RPC Method (`wallet_addEthereumChain`)
;reference https://eips.ethereum.org/EIPS/eip-3085 EIP-3085: Wallet Add Ethereum Chain RPC Method
;(`wallet_addEthereumChain`)
(ns status-im.browser.eip3085
(: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"}}})))
+37 -26
View File
@@ -1,43 +1,54 @@
;reference https://eips.ethereum.org/EIPS/eip-3326 EIP-3326: Wallet Switch Ethereum Chain RPC Method (`wallet_switchEthereumChain`)
;reference https://eips.ethereum.org/EIPS/eip-3326 EIP-3326: Wallet Switch Ethereum Chain RPC Method
;(`wallet_switchEthereumChain`)
(ns status-im.browser.eip3326
(: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.")}}})))
+57 -51
View File
@@ -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
View File
@@ -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
+51 -42
View File
@@ -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})
+117 -98
View File
@@ -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))
+37 -29
View File
@@ -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)})
+53 -43
View File
@@ -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]})]}
+232 -186
View File
@@ -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))
+80 -56
View File
@@ -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)})
+44 -33
View File
@@ -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]))))})
+33 -22
View File
@@ -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))
+5 -5
View File
@@ -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))))
+20 -20
View File
@@ -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
View File
@@ -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})]})
+36 -33
View File
@@ -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
+8 -8
View File
@@ -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))
+50 -45
View File
@@ -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)})
+8 -6
View File
@@ -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)))
+48 -41
View File
@@ -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 %)}]})
+42 -35
View File
@@ -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)}]})
+79 -64
View File
@@ -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))
+19 -14
View File
@@ -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
View File
@@ -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))))
+32 -30
View File
@@ -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)))
+118 -98
View File
@@ -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
View File
@@ -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 %])}}))
+24 -20
View File
@@ -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
+45 -40
View File
@@ -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])]
+41 -38
View File
@@ -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)))
+87 -76
View File
@@ -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
View File
@@ -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
View File
@@ -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
+20 -18
View File
@@ -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)))
+34 -32
View File
@@ -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)
+61 -55
View File
@@ -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))
+14 -14
View File
@@ -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
+90 -78
View File
@@ -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
View File
@@ -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)))
+72 -63
View File
@@ -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))))))
+101 -79
View File
@@ -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)))))
+17 -16
View File
@@ -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
+7 -6
View File
@@ -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
View File
@@ -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)))
+37 -32
View File
@@ -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
+35 -28
View File
@@ -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)))
+63 -46
View File
@@ -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))
+96 -86
View File
@@ -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]
+208 -177
View File
@@ -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)
+17 -15
View File
@@ -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]}]
+85 -68
View File
@@ -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
+53 -41
View File
@@ -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
View File
@@ -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))}))
+68 -56
View File
@@ -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}))
+18 -17
View File
@@ -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
+59 -49
View File
@@ -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])]
+105 -70
View File
@@ -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"
%)}]})
+51 -39
View File
@@ -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}
+84 -62
View File
@@ -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)))
+6 -6
View File
@@ -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})
+21 -17
View File
@@ -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})
+34 -29
View File
@@ -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)
+6 -6
View File
@@ -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)})
+78 -49
View File
@@ -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
View File
@@ -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
View File
@@ -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 %])})))
+23 -18
View File
@@ -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)
+25 -23
View File
@@ -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)})
+45 -20
View File
@@ -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)))
+16 -16
View File
@@ -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))}
+98 -89
View File
@@ -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)))))
+34 -31
View File
@@ -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]
+152 -120
View File
@@ -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)]}]]))
+11 -9
View File
@@ -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