From 2266d87ade5cf2291ee7730163b66fe3b787ce8c Mon Sep 17 00:00:00 2001 From: flexsurfer Date: Wed, 21 Sep 2022 16:03:06 +0200 Subject: [PATCH] subs refactor (#14054) --- src/status_im/core.cljs | 2 +- src/status_im/integration_test.cljs | 2 +- src/status_im/search/core_test.cljs | 18 +- src/status_im/subs.cljs | 3120 ----------------- src/status_im/subs/activity_center.cljs | 67 + src/status_im/subs/bootnodes.cljs | 31 + src/status_im/subs/browser.cljs | 32 + src/status_im/subs/chat/chats.cljs | 413 +++ src/status_im/subs/chat/messages.cljs | 138 + src/status_im/subs/communities.cljs | 162 + src/status_im/subs/contact.cljs | 239 ++ src/status_im/subs/ens.cljs | 92 + src/status_im/subs/general.cljs | 298 ++ src/status_im/subs/home.cljs | 39 + .../keycard/subs.cljs => subs/keycard.cljs} | 4 +- src/status_im/subs/mailservers.cljs | 88 + src/status_im/subs/multiaccount.cljs | 249 ++ src/status_im/subs/networks.cljs | 35 + src/status_im/subs/onboarding.cljs | 61 + src/status_im/subs/pairing.cljs | 22 + src/status_im/subs/root.cljs | 254 ++ src/status_im/subs/search.cljs | 95 + src/status_im/subs/stickers.cljs | 27 + src/status_im/{ => subs}/subs_test.cljs | 27 +- src/status_im/subs/wallet/signing.cljs | 220 ++ src/status_im/subs/wallet/transactions.cljs | 238 ++ src/status_im/subs/wallet/wallet.cljs | 283 ++ .../ui/screens/chat/components/input.cljs | 4 +- src/status_im/ui/screens/rpc_usage_info.cljs | 4 +- src/status_im/wallet/core.cljs | 2 +- src/status_im/wallet/subs_test.cljs | 18 - 31 files changed, 3125 insertions(+), 3159 deletions(-) delete mode 100644 src/status_im/subs.cljs create mode 100644 src/status_im/subs/activity_center.cljs create mode 100644 src/status_im/subs/bootnodes.cljs create mode 100644 src/status_im/subs/browser.cljs create mode 100644 src/status_im/subs/chat/chats.cljs create mode 100644 src/status_im/subs/chat/messages.cljs create mode 100644 src/status_im/subs/communities.cljs create mode 100644 src/status_im/subs/contact.cljs create mode 100644 src/status_im/subs/ens.cljs create mode 100644 src/status_im/subs/general.cljs create mode 100644 src/status_im/subs/home.cljs rename src/status_im/{ui/screens/keycard/subs.cljs => subs/keycard.cljs} (98%) create mode 100644 src/status_im/subs/mailservers.cljs create mode 100644 src/status_im/subs/multiaccount.cljs create mode 100644 src/status_im/subs/networks.cljs create mode 100644 src/status_im/subs/onboarding.cljs create mode 100644 src/status_im/subs/pairing.cljs create mode 100644 src/status_im/subs/root.cljs create mode 100644 src/status_im/subs/search.cljs create mode 100644 src/status_im/subs/stickers.cljs rename src/status_im/{ => subs}/subs_test.cljs (57%) create mode 100644 src/status_im/subs/wallet/signing.cljs create mode 100644 src/status_im/subs/wallet/transactions.cljs create mode 100644 src/status_im/subs/wallet/wallet.cljs delete mode 100644 src/status_im/wallet/subs_test.cljs diff --git a/src/status_im/core.cljs b/src/status_im/core.cljs index 16e2d27a74..112b1d06de 100644 --- a/src/status_im/core.cljs +++ b/src/status_im/core.cljs @@ -11,7 +11,7 @@ [status-im.native-module.core :as status] status-im.navigation.core [status-im.notifications.local :as notifications] - status-im.subs + status-im.subs.root [status-im.ui.components.react :as react] [status-im.utils.config :as config] status-im.utils.db diff --git a/src/status_im/integration_test.cljs b/src/status_im/integration_test.cljs index 8dd999a824..806a58dd02 100644 --- a/src/status_im/integration_test.cljs +++ b/src/status_im/integration_test.cljs @@ -7,7 +7,7 @@ [status-im.utils.security :as security] [status-im.multiaccounts.logout.core :as logout] [status-im.transport.core :as transport] - status-im.subs ;;so integration tests can run independently + status-im.subs.root ;;so integration tests can run independently [status-im.ethereum.core :as ethereum] [status-im.utils.test :as utils.test] [taoensso.timbre :as log])) diff --git a/src/status_im/search/core_test.cljs b/src/status_im/search/core_test.cljs index 38a480aed1..753f37d51f 100644 --- a/src/status_im/search/core_test.cljs +++ b/src/status_im/search/core_test.cljs @@ -1,6 +1,10 @@ (ns status-im.search.core-test (:require [cljs.test :refer-macros [deftest testing is]] - [status-im.subs :as search.subs])) + [status-im.subs.search :as search.subs])) + +(defn extract-chat-attributes [chat] + (let [{:keys [name alias tags]} (val chat)] + (into [name alias] tags))) (deftest filter-chats (let [chats {:chat-1 {:name "name1" @@ -19,35 +23,35 @@ (is (= (count chats) (count (search.subs/apply-filter "" chats - search.subs/extract-chat-attributes + extract-chat-attributes false))))) (testing "searching for a specific tag" (is (= 1 (count (search.subs/apply-filter "tag2" chats - search.subs/extract-chat-attributes + extract-chat-attributes false))))) (testing "searching for a partial tag" (is (= 3 (count (search.subs/apply-filter "tag" chats - search.subs/extract-chat-attributes + extract-chat-attributes false))))) (testing "searching for a specific alias" (is (= 1 (count (search.subs/apply-filter "alias4" chats - search.subs/extract-chat-attributes + extract-chat-attributes false))))) (testing "searching for a partial alias" (is (= 4 (count (search.subs/apply-filter "alias" chats - search.subs/extract-chat-attributes + extract-chat-attributes false))))) (testing "searching for a specific chat name" (is (= 1 (count (search.subs/apply-filter "name4" chats - search.subs/extract-chat-attributes + extract-chat-attributes false))))))) diff --git a/src/status_im/subs.cljs b/src/status_im/subs.cljs deleted file mode 100644 index d211cf9aa2..0000000000 --- a/src/status_im/subs.cljs +++ /dev/null @@ -1,3120 +0,0 @@ -(ns status-im.subs - (:require [cljs.spec.alpha :as spec] - [clojure.set :as clojure.set] - [clojure.string :as string] - [quo.design-system.colors :as colors] - [re-frame.core :as re-frame] - [status-im.add-new.db :as db] - [status-im.browser.core :as browser] - [status-im.chat.db :as chat.db] - [status-im.chat.models :as chat.models] - [status-im.chat.models.mentions :as mentions] - [status-im.chat.models.message-list :as models.message-list] - [status-im.chat.models.reactions :as models.reactions] - [status-im.communities.core :as communities] - [status-im.constants :as constants] - [status-im.contact.db :as contact.db] - [status-im.ens.core :as ens] - [status-im.ethereum.core :as ethereum] - [status-im.ethereum.tokens :as tokens] - [status-im.ethereum.transactions.core :as transactions] - [status-im.fleet.core :as fleet] - [status-im.group-chats.core :as group-chat] - [status-im.group-chats.db :as group-chats.db] - [status-im.i18n.i18n :as i18n] - [status-im.mailserver.core :as mailserver] - [status-im.multiaccounts.core :as multiaccounts] - [status-im.multiaccounts.db :as multiaccounts.db] - [status-im.multiaccounts.model :as multiaccounts.model] - [status-im.multiaccounts.recover.core :as recover] - [status-im.notifications.core :as notifications] - [status-im.pairing.core :as pairing] - [status-im.signing.gas :as signing.gas] - status-im.ui.screens.keycard.subs - [status-im.ui.screens.profile.visibility-status.utils :as visibility-status-utils] - [status-im.utils.build :as build] - [status-im.utils.config :as config] - [status-im.utils.currency :as currency] - [status-im.utils.datetime :as datetime] - [status-im.utils.gfycat.core :as gfycat] - [status-im.utils.mobile-sync :as mobile-network-utils] - [status-im.utils.money :as money] - [status-im.utils.security :as security] - [status-im.wallet.db :as wallet.db] - [status-im.wallet.utils :as wallet.utils] - [status-im.utils.image-server :as image-server])) - -;; TOP LEVEL =========================================================================================================== - -(defn reg-root-key-sub [sub-name db-key] - (re-frame/reg-sub sub-name (fn [db] (get db db-key)))) - -;;view -(reg-root-key-sub :view-id :view-id) -(reg-root-key-sub :screen-params :navigation/screen-params) - -;;bottom sheet -(reg-root-key-sub :bottom-sheet/show? :bottom-sheet/show?) -(reg-root-key-sub :bottom-sheet/view :bottom-sheet/view) -(reg-root-key-sub :bottom-sheet/options :bottom-sheet/options) - -;;general -(reg-root-key-sub :sync-state :sync-state) -(reg-root-key-sub :network-status :network-status) -(reg-root-key-sub :peers-count :peers-count) -(reg-root-key-sub :about-app/node-info :node-info) -(reg-root-key-sub :peers-summary :peers-summary) -(reg-root-key-sub :dimensions/window :dimensions/window) -(reg-root-key-sub :fleets/custom-fleets :custom-fleets) -(reg-root-key-sub :animations :animations) -(reg-root-key-sub :ui/search :ui/search) -(reg-root-key-sub :web3-node-version :web3-node-version) -(reg-root-key-sub :sync-data :sync-data) -(reg-root-key-sub :mobile-network/remember-choice? :mobile-network/remember-choice?) -(reg-root-key-sub :qr-modal :qr-modal) -(reg-root-key-sub :bootnodes/manage :bootnodes/manage) -(reg-root-key-sub :wakuv2-nodes/manage :wakuv2-nodes/manage) -(reg-root-key-sub :wakuv2-nodes/list :wakuv2-nodes/list) -(reg-root-key-sub :networks/current-network :networks/current-network) -(reg-root-key-sub :networks/networks :networks/networks) -(reg-root-key-sub :networks/manage :networks/manage) -(reg-root-key-sub :get-pairing-installations :pairing/installations) -(reg-root-key-sub :tooltips :tooltips) -(reg-root-key-sub :supported-biometric-auth :supported-biometric-auth) -(reg-root-key-sub :app-active-since :app-active-since) -(reg-root-key-sub :connectivity/ui-status-properties :connectivity/ui-status-properties) -(reg-root-key-sub :logged-in-since :logged-in-since) -(reg-root-key-sub :link-previews-whitelist :link-previews-whitelist) -(reg-root-key-sub :app-state :app-state) -(reg-root-key-sub :home-items-show-number :home-items-show-number) -(reg-root-key-sub :waku/v2-peer-stats :peer-stats) -(reg-root-key-sub :visibility-status-updates :visibility-status-updates) -(reg-root-key-sub :navigation2/navigation2-stacks :navigation2/navigation2-stacks) - -;;NOTE this one is not related to ethereum network -;; it is about cellular network/ wifi network -(reg-root-key-sub :network/type :network/type) - -;;profile -(reg-root-key-sub :my-profile/seed :my-profile/seed) -(reg-root-key-sub :my-profile/advanced? :my-profile/advanced?) -(reg-root-key-sub :my-profile/profile :my-profile/profile) - -;;multiaccount -(reg-root-key-sub :multiaccounts/multiaccounts :multiaccounts/multiaccounts) -(reg-root-key-sub :multiaccounts/login :multiaccounts/login) -(reg-root-key-sub :multiaccount :multiaccount) -(reg-root-key-sub :multiaccount/accounts :multiaccount/accounts) -(reg-root-key-sub :get-recover-multiaccount :multiaccounts/recover) -(reg-root-key-sub :multiaccounts/key-storage :multiaccounts/key-storage) -(reg-root-key-sub :multiaccount/reset-password-form-vals :multiaccount/reset-password-form-vals) -(reg-root-key-sub :multiaccount/reset-password-errors :multiaccount/reset-password-errors) -(reg-root-key-sub :multiaccount/resetting-password? :multiaccount/resetting-password?) - -;;chat -(reg-root-key-sub ::cooldown-enabled? :chat/cooldown-enabled?) -(reg-root-key-sub ::chats :chats) -(reg-root-key-sub ::chat-ui-props :chat-ui-props) -(reg-root-key-sub :chats/current-chat-id :current-chat-id) -(reg-root-key-sub :public-group-topic :public-group-topic) -(reg-root-key-sub :chats/loading? :chats/loading?) -(reg-root-key-sub :new-chat-name :new-chat-name) -(reg-root-key-sub :group-chat-profile/editing? :group-chat-profile/editing?) -(reg-root-key-sub :group-chat-profile/profile :group-chat-profile/profile) -(reg-root-key-sub :selected-participants :selected-participants) -(reg-root-key-sub :chat/inputs :chat/inputs) -(reg-root-key-sub :chat/memberships :chat/memberships) -(reg-root-key-sub :camera-roll-photos :camera-roll-photos) -(reg-root-key-sub :group-chat/invitations :group-chat/invitations) -(reg-root-key-sub :chats/mention-suggestions :chats/mention-suggestions) -(reg-root-key-sub :chat/inputs-with-mentions :chat/inputs-with-mentions) -(reg-root-key-sub :chats-home-list :chats-home-list) - -;;browser -(reg-root-key-sub :browsers :browser/browsers) -(reg-root-key-sub :browser/options :browser/options) -(reg-root-key-sub :dapps/permissions :dapps/permissions) -(reg-root-key-sub :bookmarks :bookmarks/bookmarks) - -;;stickers -(reg-root-key-sub :stickers/selected-pack :stickers/selected-pack) -(reg-root-key-sub :stickers/packs :stickers/packs) -(reg-root-key-sub :stickers/recent-stickers :stickers/recent-stickers) - -;;mailserver -(reg-root-key-sub :mailserver/current-id :mailserver/current-id) -(reg-root-key-sub :mailserver/mailservers :mailserver/mailservers) -(reg-root-key-sub :mailserver.edit/mailserver :mailserver.edit/mailserver) -(reg-root-key-sub :mailserver/state :mailserver/state) -(reg-root-key-sub :mailserver/pending-requests :mailserver/pending-requests) -(reg-root-key-sub :mailserver/request-error? :mailserver/request-error) -(reg-root-key-sub :mailserver/fetching-gaps-in-progress :mailserver/fetching-gaps-in-progress) - -;;contacts -(reg-root-key-sub ::contacts :contacts/contacts) -(reg-root-key-sub :contacts/current-contact-identity :contacts/identity) -(reg-root-key-sub :contacts/current-contact-ens-name :contacts/ens-name) -(reg-root-key-sub :contacts/new-identity :contacts/new-identity) -(reg-root-key-sub :group/selected-contacts :group/selected-contacts) -(reg-root-key-sub :contacts/blocked-set :contacts/blocked) - -;;wallet -(reg-root-key-sub :wallet :wallet) -(reg-root-key-sub :prices :prices) -(reg-root-key-sub :prices-loading? :prices-loading?) -(reg-root-key-sub :wallet.transactions :wallet.transactions) -(reg-root-key-sub :wallet/custom-token-screen :wallet/custom-token-screen) -(reg-root-key-sub :wallet/prepare-transaction :wallet/prepare-transaction) -(reg-root-key-sub :wallet-service/manual-setting :wallet-service/manual-setting) -(reg-root-key-sub :wallet/recipient :wallet/recipient) -(reg-root-key-sub :wallet/favourites :wallet/favourites) -(reg-root-key-sub :wallet/refreshing-history? :wallet/refreshing-history?) -(reg-root-key-sub :wallet/fetching-error :wallet/fetching-error) -(reg-root-key-sub :wallet/non-archival-node :wallet/non-archival-node) -(reg-root-key-sub :wallet/current-base-fee :wallet/current-base-fee) -(reg-root-key-sub :wallet/slow-base-fee :wallet/slow-base-fee) -(reg-root-key-sub :wallet/normal-base-fee :wallet/normal-base-fee) -(reg-root-key-sub :wallet/fast-base-fee :wallet/fast-base-fee) -(reg-root-key-sub :wallet/current-priority-fee :wallet/current-priority-fee) -(reg-root-key-sub :wallet/transactions-management-enabled? :wallet/transactions-management-enabled?) -(reg-root-key-sub :wallet/all-tokens :wallet/all-tokens) -(reg-root-key-sub :wallet/collectible-collections :wallet/collectible-collections) -(reg-root-key-sub :wallet/fetching-collection-assets :wallet/fetching-collection-assets) -(reg-root-key-sub :wallet/collectible-assets :wallet/collectible-assets) -(reg-root-key-sub :wallet/selected-collectible :wallet/selected-collectible) -(reg-root-key-sub :wallet/modal-selecting-source-token? :wallet/modal-selecting-source-token?) -(reg-root-key-sub :wallet/swap-from-token :wallet/swap-from-token) -(reg-root-key-sub :wallet/swap-to-token :wallet/swap-to-token) -(reg-root-key-sub :wallet/swap-from-token-amount :wallet/swap-from-token-amount) -(reg-root-key-sub :wallet/swap-to-token-amount :wallet/swap-to-token-amount) -(reg-root-key-sub :wallet/swap-advanced-mode? :wallet/swap-advanced-mode?) - -;;commands -(reg-root-key-sub :commands/select-account :commands/select-account) - -;;ethereum -(reg-root-key-sub :ethereum/current-block :ethereum/current-block) - -;;ens -(reg-root-key-sub :ens/registration :ens/registration) -(reg-root-key-sub :ens/registrations :ens/registrations) -(reg-root-key-sub :ens/names :ens/names) - -;;signing -(reg-root-key-sub :signing/sign :signing/sign) -(reg-root-key-sub :signing/tx :signing/tx) -(reg-root-key-sub :signing/edit-fee :signing/edit-fee) - -;;intro-wizard -(reg-root-key-sub :intro-wizard-state :intro-wizard) - -(reg-root-key-sub :popover/popover :popover/popover) -(reg-root-key-sub :visibility-status-popover/popover :visibility-status-popover/popover) -(reg-root-key-sub :add-account :add-account) - -(reg-root-key-sub :keycard :keycard) - -(reg-root-key-sub :auth-method :auth-method) - -(reg-root-key-sub :multiaccounts/loading :multiaccounts/loading) - -(reg-root-key-sub ::messages :messages) -(reg-root-key-sub ::reactions :reactions) -(reg-root-key-sub ::message-lists :message-lists) -(reg-root-key-sub ::pagination-info :pagination-info) -(reg-root-key-sub ::pin-message-lists :pin-message-lists) -(reg-root-key-sub ::pin-messages :pin-messages) - -(reg-root-key-sub :tos-accept-next-root :tos-accept-next-root) - -;; keycard -(reg-root-key-sub :keycard/banner-hidden :keycard/banner-hidden) - -;; delete profile -(reg-root-key-sub :delete-profile/error :delete-profile/error) -(reg-root-key-sub :delete-profile/keep-keys-on-keycard? :delete-profile/keep-keys-on-keycard?) - -;; push notifications -(reg-root-key-sub :push-notifications/servers :push-notifications/servers) -(reg-root-key-sub :push-notifications/preferences :push-notifications/preferences) - -(reg-root-key-sub :buy-crypto/on-ramps :buy-crypto/on-ramps) - -;; communities - -(reg-root-key-sub :raw-communities :communities) -(reg-root-key-sub :communities/create :communities/create) -(reg-root-key-sub :communities/create-channel :communities/create-channel) -(reg-root-key-sub :communities/requests-to-join :communities/requests-to-join) -(reg-root-key-sub :communities/community-id-input :communities/community-id-input) -(reg-root-key-sub :communities/enabled? :communities/enabled?) -(reg-root-key-sub :communities/resolve-community-info :communities/resolve-community-info) - -(reg-root-key-sub :activity.center/notifications :activity.center/notifications) -(reg-root-key-sub :activity.center/notifications-count :activity.center/notifications-count) - -(reg-root-key-sub :bug-report/description-error :bug-report/description-error) -(reg-root-key-sub :bug-report/details :bug-report/details) - -(reg-root-key-sub :backup/performing-backup :backup/performing-backup) - -;; wallet connect -(reg-root-key-sub :wallet-connect/proposal-metadata :wallet-connect/proposal-metadata) -(reg-root-key-sub :wallet-connect/enabled? :wallet-connect/enabled?) -(reg-root-key-sub :wallet-connect/session-connected :wallet-connect/session-connected) -(reg-root-key-sub :wallet-connect/showing-app-management-sheet? :wallet-connect/showing-app-management-sheet?) -(reg-root-key-sub :wallet-connect/sessions :wallet-connect/sessions) -(reg-root-key-sub :wallet-connect-legacy/sessions :wallet-connect-legacy/sessions) -(reg-root-key-sub :wallet-connect/session-managed :wallet-connect/session-managed) -(reg-root-key-sub :contact-requests/pending :contact-requests/pending) - -; media-server -(reg-root-key-sub :mediaserver/port :mediaserver/port) - -; Testing - -(reg-root-key-sub :messenger/started? :messenger/started?) - -(reg-root-key-sub :information-box-states :information-box-states) - -(re-frame/reg-sub - :information-box-closed? - :<- [:information-box-states] - (fn [states [_ id]] - (get states id))) - -(re-frame/reg-sub - :communities - :<- [:raw-communities] - :<- [:communities/enabled?] - (fn [[raw-communities communities-enabled?]] - (if communities-enabled? - raw-communities - []))) - -(re-frame/reg-sub - :communities/fetching-community - :<- [:communities/resolve-community-info] - (fn [info [_ id]] - (get info id))) - -(re-frame/reg-sub - :communities/section-list - :<- [:communities] - (fn [communities] - (->> (vals communities) - (group-by (comp (fnil string/upper-case "") first :name)) - (sort-by (fn [[title]] title)) - (map (fn [[title data]] - {:title title - :data data}))))) - -(re-frame/reg-sub - :communities/community - :<- [:communities] - (fn [communities [_ id]] - (get communities id))) - -(re-frame/reg-sub - :communities/community-chats - :<- [:communities] - (fn [communities [_ id]] - (get-in communities [id :chats]))) - -(re-frame/reg-sub - :communities/community-members - :<- [:communities] - (fn [communities [_ id]] - (get-in communities [id :members]))) - -(re-frame/reg-sub - :communities/sorted-community-members - (fn [[_ community-id]] - (let [contacts (re-frame/subscribe [:contacts/contacts]) - multiaccount (re-frame/subscribe [:multiaccount]) - members (re-frame/subscribe - [:communities/community-members community-id])] - [contacts multiaccount members])) - (fn [[contacts multiaccount members] _] - (let [names (reduce - (fn [acc identity] - (let [me? (= (:public-key multiaccount) - identity) - contact (when-not me? - (multiaccounts/contact-by-identity - contacts identity)) - name (first - (multiaccounts/contact-two-names-by-identity - contact multiaccount identity))] - (assoc acc identity name))) {} (keys members))] - (->> members - (sort-by #(get names (get % 0))) - (sort-by #(visibility-status-utils/visibility-status-order (get % 0))))))) - -(re-frame/reg-sub - :communities/featured-communities - :<- [:communities/enabled?] - :<- [:search/home-filter] - :<- [:communities] - (fn [[communities-enabled? search-filter communities]] - (filterv - (fn [{:keys [name featured id]}] - (and (or featured (= name "Status")) ;; TO DO: remove once featured communities exist - (or communities-enabled? - (= id constants/status-community-id)) - (or (empty? search-filter) - (string/includes? (string/lower-case (str name)) search-filter)))) - (vals communities)))) - -(re-frame/reg-sub - :communities/communities - :<- [:communities/enabled?] - :<- [:search/home-filter] - :<- [:communities] - (fn [[communities-enabled? search-filter communities]] - (filterv - (fn [{:keys [name joined id]}] - (and joined - (or communities-enabled? - (= id constants/status-community-id)) - (or (empty? search-filter) - (string/includes? (string/lower-case (str name)) search-filter)))) - (vals communities)))) - -(re-frame/reg-sub - :communities/edited-community - :<- [:communities] - :<- [:communities/community-id-input] - (fn [[communities community-id]] - (get communities community-id))) - -(re-frame/reg-sub - :communities/current-community - :<- [:communities] - :<- [:chats/current-raw-chat] - (fn [[communities {:keys [community-id]}]] - (get communities community-id))) - -(re-frame/reg-sub - :communities/unviewed-count - (fn [[_ community-id]] - [(re-frame/subscribe [:chats/by-community-id community-id])]) - (fn [[chats]] - (reduce (fn [acc {:keys [unviewed-messages-count]}] - (+ acc (or unviewed-messages-count 0))) - 0 - chats))) - -(re-frame/reg-sub - :communities/unviewed-counts - (fn [[_ community-id]] - [(re-frame/subscribe [:chats/by-community-id community-id])]) - (fn [[chats]] - (reduce (fn [acc {:keys [unviewed-mentions-count unviewed-messages-count]}] - {:unviewed-messages-count (+ (:unviewed-messages-count acc) (or unviewed-messages-count 0)) - :unviewed-mentions-count (+ (:unviewed-mentions-count acc) (or unviewed-mentions-count 0))}) - {:unviewed-messages-count 0 - :unviewed-mentions-count 0} - chats))) - -(re-frame/reg-sub - :communities/requests-to-join-for-community - :<- [:communities/requests-to-join] - (fn [requests [_ community-id]] - (->> - (get requests community-id {}) - vals - (filter (fn [{:keys [state]}] - (= state constants/request-to-join-pending-state)))))) - -;;GENERAL ============================================================================================================== - -(re-frame/reg-sub - :visibility-status-updates/visibility-status-update - :<- [:visibility-status-updates] - (fn [visibility-status-updates [_ public-key]] - (get visibility-status-updates public-key))) - -(re-frame/reg-sub - :multiaccount/logged-in? - (fn [db] - (multiaccounts.model/logged-in? {:db db}))) - -(re-frame/reg-sub - :hide-screen? - :<- [:app-state] - :<- [:multiaccount] - (fn [[state multiaccount]] - (and (= state "inactive") (:preview-privacy? multiaccount)))) - -;; Intro wizard -(re-frame/reg-sub - :intro-wizard - :<- [:intro-wizard-state] - :<- [:dimensions/window] - (fn [[wizard-state {:keys [width height]}]] - (assoc wizard-state - :view-height height :view-width width))) - -(re-frame/reg-sub - :intro-wizard/choose-key - :<- [:intro-wizard] - (fn [wizard-state] - (select-keys wizard-state [:multiaccounts :selected-id :view-height]))) - -(re-frame/reg-sub - :intro-wizard/select-key-storage - :<- [:intro-wizard] - (fn [wizard-state] - (select-keys wizard-state [:selected-storage-type :recovering?]))) - -(re-frame/reg-sub - :intro-wizard/enter-phrase - :<- [:intro-wizard] - (fn [wizard-state] - (select-keys wizard-state [:processing? - :passphrase-word-count - :next-button-disabled? - :passphrase-error]))) - -(re-frame/reg-sub - :intro-wizard/recovery-success - :<- [:intro-wizard] - (fn [wizard-state] - {:pubkey (get-in wizard-state [:derived constants/path-whisper-keyword :public-key]) - :name (get-in wizard-state [:derived constants/path-whisper-keyword :name]) - :identicon (get-in wizard-state [:derived constants/path-whisper-keyword :identicon]) - :processing? (:processing? wizard-state)})) - -(re-frame/reg-sub - :intro-wizard/recover-existing-account? - :<- [:intro-wizard] - :<- [:multiaccounts/multiaccounts] - (fn [[intro-wizard multiaccounts]] - (recover/existing-account? (:root-key intro-wizard) multiaccounts))) - -(defn login-ma-keycard-pairing - "Compute the keycard-pairing value of the multiaccount selected for login" - [db _] - (when-let [acc-to-login (-> db :multiaccounts/login)] - (-> db - :multiaccounts/multiaccounts - (get (:key-uid acc-to-login)) - :keycard-pairing))) - -(re-frame/reg-sub - :intro-wizard/acc-to-login-keycard-pairing - login-ma-keycard-pairing) - -(re-frame/reg-sub - :current-network - :<- [:networks/networks] - :<- [:networks/current-network] - (fn [[networks current-network]] - (when-let [network (get networks current-network)] - (assoc network - :rpc-network? (get-in network [:config :UpstreamConfig :Enabled]))))) - -(re-frame/reg-sub - :custom-rpc-node - :<- [:current-network] - (fn [network] - (ethereum/custom-rpc-node? network))) - -(re-frame/reg-sub - :chain-keyword - :<- [:current-network] - (fn [network] - (ethereum/network->chain-keyword network))) - -(re-frame/reg-sub - :chain-name - :<- [:current-network] - (fn [network] - (ethereum/network->chain-name network))) - -(re-frame/reg-sub - :chain-id - :<- [:current-network] - (fn [network] - (ethereum/network->chain-id network))) - -(re-frame/reg-sub - :mainnet? - :<- [:chain-id] - (fn [chain-id] - (= 1 chain-id))) - -(re-frame/reg-sub - :ethereum-network? - :<- [:chain-id] - (fn [chain-id] - (< chain-id 6))) - -(re-frame/reg-sub - :network-name - :<- [:current-network] - (fn [network] - (:name network))) - -(re-frame/reg-sub - :disconnected? - :<- [:peers-count] - :<- [:waku/v2-flag] - :<- [:waku/v2-peer-stats] - (fn [[peers-count wakuv2-flag peer-stats]] - ;; If wakuv2 is enabled, - ;; then fetch connectivity status from - ;; peer-stats (populated from "wakuv2.peerstats" status-go signal) - ;; Otherwise use peers-count fetched from "discovery.summary" signal - (if wakuv2-flag (not (:isOnline peer-stats)) (zero? peers-count)))) - -(re-frame/reg-sub - :offline? - :<- [:network-status] - :<- [:sync-state] - :<- [:disconnected?] - (fn [[network-status sync-state disconnected?]] - (or disconnected? - (= network-status :offline) - (= sync-state :offline)))) - -(re-frame/reg-sub - :syncing? - :<- [:sync-state] - (fn [sync-state] - (#{:pending :in-progress} sync-state))) - -(re-frame/reg-sub - :dimensions/window-width - :<- [:dimensions/window] - :width) - -(re-frame/reg-sub - :dimensions/window-height - :<- [:dimensions/window] - :height) - -(re-frame/reg-sub - :dimensions/small-screen? - :<- [:dimensions/window-height] - (fn [height] - (< height 550))) - -(re-frame/reg-sub - :get-screen-params - :<- [:screen-params] - :<- [:view-id] - (fn [[params view-id-db] [_ view-id]] - (get params (or view-id view-id-db)))) - -(re-frame/reg-sub - :delete-swipe-position - :<- [:animations] - (fn [animations [_ type item-id]] - (get-in animations [type item-id :delete-swiped]))) - -(re-frame/reg-sub - :search/home-filter - :<- [:ui/search] - (fn [search] - (get search :home-filter))) - -(re-frame/reg-sub - :search/recipient-filter - :<- [:ui/search] - (fn [search] - (get search :recipient-filter))) - -(re-frame/reg-sub - :search/currency-filter - :<- [:ui/search] - (fn [search] - (get search :currency-filter))) - -(re-frame/reg-sub - :search/token-filter - :<- [:ui/search] - (fn [search] - (get search :token-filter))) - -(defn- node-version [web3-node-version] - (or web3-node-version "N/A")) - -(re-frame/reg-sub - :get-app-version - :<- [:web3-node-version] - (fn [web3-node-version] - (str build/app-short-version "; " (node-version web3-node-version)))) - -(re-frame/reg-sub - :get-app-short-version - (fn [_] build/app-short-version)) - -(re-frame/reg-sub - :get-commit-hash - (fn [_] build/commit-hash)) - -(re-frame/reg-sub - :get-app-node-version - :<- [:web3-node-version] - node-version) - -(re-frame/reg-sub - :my-profile/recovery - :<- [:my-profile/seed] - (fn [seed] - (or seed {:step :intro}))) - -(re-frame/reg-sub - :bottom-sheet - :<- [:bottom-sheet/show?] - :<- [:bottom-sheet/view] - :<- [:bottom-sheet/options] - (fn [[show? view options]] - {:show? show? - :view view - :options options})) - -(re-frame/reg-sub - :is-contact-selected? - :<- [:group/selected-contacts] - (fn [selected-contacts [_ element]] - (-> selected-contacts - (contains? element)))) - -(re-frame/reg-sub - :is-participant-selected? - :<- [:selected-participants] - (fn [selected-participants [_ element]] - (-> selected-participants - (contains? element)))) - -(re-frame/reg-sub - :ethereum/chain-keyword - :<- [:current-network] - (fn [network] - (ethereum/network->chain-keyword network))) - -(re-frame/reg-sub - :ethereum/native-currency - :<- [:current-network] - (fn [network] - (tokens/native-currency network))) - -;;MULTIACCOUNT ============================================================================================================== - -(re-frame/reg-sub - :multiaccount/public-key - :<- [:multiaccount] - (fn [{:keys [public-key]}] - public-key)) - -(re-frame/reg-sub - :multiaccount/contact - :<- [:multiaccount] - (fn [current-account] - (some-> - current-account - (select-keys [:name :preferred-name :public-key :identicon :image :images]) - (clojure.set/rename-keys {:name :alias}) - (multiaccounts/contact-with-names)))) - -(re-frame/reg-sub - :multiaccount/preferred-name - :<- [:multiaccount] - (fn [{:keys [preferred-name]}] - preferred-name)) - -(re-frame/reg-sub - :multiaccount/default-account - :<- [:multiaccount/accounts] - (fn [accounts] - (ethereum/get-default-account accounts))) - -(re-frame/reg-sub - :multiaccount/visible-accounts - :<- [:multiaccount/accounts] - (fn [accounts] - (remove :hidden accounts))) - -(re-frame/reg-sub - :sign-in-enabled? - :<- [:multiaccounts/login] - (fn [{:keys [password]}] - (spec/valid? ::multiaccounts.db/password - (security/safe-unmask-data password)))) - -(re-frame/reg-sub - :fleets/current-fleet - :<- [:multiaccount] - (fn [multiaccount] - (fleet/current-fleet-sub multiaccount))) - -(re-frame/reg-sub - :opensea-enabled? - :<- [:multiaccount] - (fn [{:keys [opensea-enabled?]}] - (boolean opensea-enabled?))) - -(re-frame/reg-sub - :log-level/current-log-level - :<- [:multiaccount] - (fn [multiaccount] - (get multiaccount :log-level))) - -(re-frame/reg-sub - :waku/bloom-filter-mode - :<- [:multiaccount] - (fn [multiaccount] - (boolean (get multiaccount :waku-bloom-filter-mode)))) - -(re-frame/reg-sub - :waku/v2-flag - :<- [:fleets/current-fleet] - (fn [fleet] - (string/starts-with? (name fleet) "wakuv2"))) - -(re-frame/reg-sub - :dapps-address - :<- [:multiaccount] - (fn [acc] - (get acc :dapps-address))) - -(re-frame/reg-sub - :dapps-account - :<- [:multiaccount/accounts] - :<- [:dapps-address] - (fn [[accounts address]] - (some #(when (= (:address %) address) %) accounts))) - -(re-frame/reg-sub - :multiaccount/current-account - :<- [:multiaccount/accounts] - :<- [:get-screen-params :wallet-account] - (fn [[accounts acc]] - (some #(when (= (string/lower-case (:address %)) - (string/lower-case (:address acc))) %) accounts))) - -(re-frame/reg-sub - :account-by-address - :<- [:multiaccount/accounts] - (fn [accounts [_ address]] - (when (string? address) - (some #(when (= (string/lower-case (:address %)) - (string/lower-case address)) %) accounts)))) - -(re-frame/reg-sub - :multiple-multiaccounts? - :<- [:multiaccounts/multiaccounts] - (fn [multiaccounts] - (> (count multiaccounts) 1))) - -;; NOTE: this subscription only works on login -(re-frame/reg-sub - :multiaccounts.login/keycard-account? - :<- [:multiaccounts/multiaccounts] - :<- [:multiaccounts/login] - (fn [[multiaccounts {:keys [key-uid]}]] - (get-in multiaccounts [key-uid :keycard-pairing]))) - -(re-frame/reg-sub - :multiaccounts/keycard-account? - :<- [:multiaccount] - (fn [multiaccount] - (:keycard-pairing multiaccount))) - -(re-frame/reg-sub - :accounts-without-watch-only - :<- [:multiaccount/accounts] - (fn [accounts] - (filter #(not= (:type %) :watch) accounts))) - -(re-frame/reg-sub - :visible-accounts-without-watch-only - :<- [:multiaccount/accounts] - (fn [accounts] - (remove :hidden (filter #(not= (:type %) :watch) accounts)))) - -(defn filter-recipient-accounts - [search-filter {:keys [name]}] - (string/includes? (string/lower-case (str name)) search-filter)) - -(re-frame/reg-sub - :accounts-for-recipient - :<- [:multiaccount/visible-accounts] - :<- [:wallet/prepare-transaction] - :<- [:search/recipient-filter] - (fn [[accounts {:keys [from]} search-filter]] - (let [accounts (remove #(= (:address %) (:address from)) accounts)] - (if (string/blank? search-filter) - accounts - (filter (partial filter-recipient-accounts - (string/lower-case search-filter)) - accounts))))) - -(re-frame/reg-sub - :add-account-disabled? - :<- [:multiaccount/accounts] - :<- [:add-account] - (fn [[accounts {:keys [address type account seed private-key]}]] - (or (string/blank? (:name account)) - (case type - :generate - false - :watch - (or (not (ethereum/address? address)) - (some #(when (= (:address %) address) %) accounts)) - :key - (string/blank? (security/safe-unmask-data private-key)) - :seed - (string/blank? (security/safe-unmask-data seed)) - false)))) - -(re-frame/reg-sub - :multiaccount/current-user-visibility-status - :<- [:multiaccount] - (fn [{:keys [current-user-visibility-status]}] - current-user-visibility-status)) - -;;CHAT ============================================================================================================== - -(re-frame/reg-sub - :chats/chat - :<- [::chats] - (fn [chats [_ chat-id]] - (get chats chat-id))) - -(re-frame/reg-sub - :chats/by-community-id - :<- [::chats] - (fn [chats [_ community-id]] - (->> chats - (keep (fn [[_ chat]] - (when (= (:community-id chat) community-id) - chat))) - (sort-by :timestamp >)))) - -(re-frame/reg-sub - :chats/with-empty-category-by-community-id - (fn [[_ community-id]] - [(re-frame/subscribe [:chats/by-community-id community-id]) - (re-frame/subscribe [:communities/community-chats community-id])]) - (fn [[chats comm-chats] [_ community-id]] - (filter #(string/blank? (get-in comm-chats [(string/replace (:chat-id %) community-id "") :categoryID])) chats))) - -(re-frame/reg-sub - :chats/categories-by-community-id - (fn [[_ community-id]] - [(re-frame/subscribe [:chats/by-community-id community-id]) - (re-frame/subscribe [:communities/community-chats community-id])]) - (fn [[chats comm-chats] [_ community-id]] - (let [chat-cat (into {} (map (fn [{:keys [id categoryID]}] {(str community-id id) categoryID}) (vals comm-chats)))] - (group-by :categoryID (map #(cond-> (assoc % :categoryID (chat-cat (:chat-id %))) - (= community-id constants/status-community-id) - (assoc :color colors/blue)) - chats))))) - -(re-frame/reg-sub - :chats/sorted-categories-by-community-id - (fn [[_ community-id]] - [(re-frame/subscribe [:chats/by-community-id community-id]) - (re-frame/subscribe [:communities/community-chats community-id])]) - (fn [[chats comm-chats] [_ community-id]] - (let [chat-cat (into {} (map (fn [{:keys [id categoryID position]}] - {(str community-id id) {:categoryID categoryID - :position position}}) - (vals comm-chats)))] - (group-by :categoryID (sort-by :position - (map #(cond-> (merge % (chat-cat (:chat-id %))) - (= community-id constants/status-community-id) - (assoc :color colors/blue)) - chats)))))) - -(re-frame/reg-sub - :chats/category-by-chat-id - (fn [[_ community-id _]] - [(re-frame/subscribe [:communities/community community-id])]) - (fn [[{:keys [chats categories]}] [_ community-id chat-id]] - (get categories (get-in chats [(string/replace chat-id community-id "") :categoryID])))) - -(re-frame/reg-sub - :chats/community-chat-by-id - (fn [[_ community-id _]] - [(re-frame/subscribe [:communities/community community-id])]) - (fn [[{:keys [chats]}] [_ community-id chat-id]] - (get chats (string/replace chat-id community-id "")))) - -(re-frame/reg-sub - :community/categories - (fn [[_ community-id]] - [(re-frame/subscribe [:communities/community community-id])]) - (fn [[{:keys [categories]}] _] - categories)) - -(re-frame/reg-sub - :communities/sorted-categories - :<- [:communities] - (fn [communities [_ id]] - (->> (get-in communities [id :categories]) - (map #(assoc (get % 1) :community-id id)) - (sort-by :position) - (into [])))) - -(re-frame/reg-sub - :chats/current-chat-ui-props - :<- [::chat-ui-props] - :<- [:chats/current-chat-id] - (fn [[chat-ui-props id]] - (get chat-ui-props id))) - -(re-frame/reg-sub - :chats/current-chat-ui-prop - :<- [:chats/current-chat-ui-props] - (fn [ui-props [_ prop]] - (get ui-props prop))) - -(re-frame/reg-sub - :chats/current-chat-contact - :<- [:contacts/contacts] - :<- [:chats/current-chat-id] - (fn [[contacts current-chat-id]] - (get contacts current-chat-id))) - -(re-frame/reg-sub - :chats/home-list-chats - :<- [::chats] - :<- [:chats-home-list] - (fn [[chats active-chats]] - (reduce #(if-let [item (get chats %2)] - (conj %1 item) - %1) - [] - active-chats))) - -(re-frame/reg-sub - :chat-by-id - :<- [::chats] - (fn [chats [_ chat-id]] - (get chats chat-id))) - -(re-frame/reg-sub - :chats/synced-from - (fn [[_ chat-id] _] - (re-frame/subscribe [:chat-by-id chat-id])) - (fn [{:keys [synced-from]}] - synced-from)) - -(re-frame/reg-sub - :chats/muted - (fn [[_ chat-id] _] - (re-frame/subscribe [:chat-by-id chat-id])) - (fn [{:keys [muted]}] - muted)) - -(re-frame/reg-sub - :chats/chat-type - (fn [[_ chat-id] _] - (re-frame/subscribe [:chat-by-id chat-id])) - (fn [{:keys [chat-type]}] - chat-type)) - -(re-frame/reg-sub - :chats/joined - (fn [[_ chat-id] _] - (re-frame/subscribe [:chat-by-id chat-id])) - (fn [{:keys [joined]}] - joined)) - -(re-frame/reg-sub - :chats/synced-to-and-from - (fn [[_ chat-id] _] - (re-frame/subscribe [:chat-by-id chat-id])) - (fn [chat] - (select-keys chat [:synced-to :synced-from]))) - -(re-frame/reg-sub - :chats/current-raw-chat - :<- [::chats] - :<- [:chats/current-chat-id] - (fn [[chats current-chat-id]] - (get chats current-chat-id))) - -(re-frame/reg-sub - :chats/current-chat-inputs - :<- [:chats/current-chat-id] - :<- [:chat/inputs] - (fn [[chat-id inputs]] - (get inputs chat-id))) - -(re-frame/reg-sub - :chats/timeline-chat-input - :<- [:chat/inputs] - :<- [:multiaccount/public-key] - (fn [[inputs public-key]] - (get inputs (chat.models/profile-chat-topic public-key)))) - -(re-frame/reg-sub - :chats/timeline-chat-input-text - :<- [:chats/timeline-chat-input] - (fn [input] - (:input-text input))) - -(re-frame/reg-sub - :chats/current-chat-membership - :<- [:chats/current-chat-id] - :<- [:chat/memberships] - (fn [[chat-id memberships]] - (get memberships chat-id))) - -(re-frame/reg-sub - :chats/current-chat - :<- [:chats/current-raw-chat] - :<- [:multiaccount/public-key] - :<- [:communities/current-community] - :<- [:contacts/blocked-set] - :<- [:contacts/contacts] - :<- [:chat/inputs] - :<- [:mutual-contact-requests/enabled?] - (fn [[{:keys [group-chat chat-id] :as current-chat} my-public-key community blocked-users-set contacts inputs mutual-contact-requests-enabled?]] - (when current-chat - (cond-> current-chat - (chat.models/public-chat? current-chat) - (assoc :show-input? true) - - (and (chat.models/group-chat? current-chat) - (group-chats.db/member? my-public-key current-chat)) - (assoc :show-input? true - :member? true) - - (and (chat.models/community-chat? current-chat) - (communities/can-post? community my-public-key (:chat-id current-chat))) - (assoc :show-input? true) - - (not group-chat) - (assoc :show-input? - (and - (or - (not mutual-contact-requests-enabled?) - (get-in inputs [chat-id :metadata :sending-contact-request]) - (and mutual-contact-requests-enabled? - (= constants/contact-request-state-mutual - (get-in contacts [chat-id :contact-request-state])))) - (not (contains? blocked-users-set chat-id)))))))) - -(re-frame/reg-sub - :chats/current-chat-chat-view - :<- [:chats/current-chat] - (fn [current-chat] - (select-keys current-chat [:chat-id :show-input? :group-chat :admins :invitation-admin :public? :chat-type :color :chat-name :synced-to :synced-from :community-id :emoji]))) - -(re-frame/reg-sub - :current-chat/metadata - :<- [:chats/current-raw-chat] - (fn [current-chat] - (select-keys current-chat - [:community-id - :contacts - :public? - :group-chat - :chat-type - :chat-id - :chat-name - :color - :invitation-admin]))) - -(re-frame/reg-sub - :current-chat/one-to-one-chat? - :<- [:chats/current-raw-chat] - (fn [current-chat] - (not (or (chat.models/group-chat? current-chat) - (chat.models/public-chat? current-chat))))) - -(re-frame/reg-sub - :current-chat/public? - :<- [:chats/current-raw-chat] - (fn [current-chat] - (chat.models/public-chat? current-chat))) - -(re-frame/reg-sub - :chats/chat-messages - :<- [::messages] - (fn [messages [_ chat-id]] - (get messages chat-id {}))) - -(re-frame/reg-sub - :chats/pinned - :<- [::pin-messages] - (fn [pin-messages [_ chat-id]] - (get pin-messages chat-id {}))) - -(re-frame/reg-sub - :chats/pinned-sorted-list - :<- [::pin-messages] - (fn [pin-messages [_ chat-id]] - (->> - (get pin-messages chat-id {}) - vals - (sort-by :pinned-at <)))) - -(re-frame/reg-sub - :chats/message-reactions - :<- [:multiaccount/public-key] - :<- [::reactions] - (fn [[current-public-key reactions] [_ message-id chat-id]] - (models.reactions/message-reactions - current-public-key - (get-in reactions [chat-id message-id])))) - -(re-frame/reg-sub - :mailserver/current-name - :<- [:mailserver/current-id] - :<- [:fleets/current-fleet] - :<- [:mailserver/mailservers] - (fn [[current-mailserver-id current-fleet mailservers]] - (get-in mailservers [current-fleet current-mailserver-id :name]))) - -(re-frame/reg-sub - :chats/all-loaded? - :<- [::pagination-info] - (fn [pagination-info [_ chat-id]] - (get-in pagination-info [chat-id :all-loaded?]))) - -(re-frame/reg-sub - :chats/loading-messages? - :<- [::pagination-info] - (fn [pagination-info [_ chat-id]] - (get-in pagination-info [chat-id :loading-messages?]))) - -(re-frame/reg-sub - :chats/loading-pin-messages? - :<- [::pagination-info] - (fn [pagination-info [_ chat-id]] - (get-in pagination-info [chat-id :loading-pin-messages?]))) - -(re-frame/reg-sub - :chats/public? - :<- [::chats] - (fn [chats [_ chat-id]] - (get-in chats [chat-id :public?]))) - -(re-frame/reg-sub - :chats/message-list - :<- [::message-lists] - (fn [message-lists [_ chat-id]] - (get message-lists chat-id))) - -(re-frame/reg-sub - :chats/pin-message-list - :<- [::pin-message-lists] - (fn [pin-message-lists [_ chat-id]] - (get pin-message-lists chat-id))) - -(defn hydrate-messages - "Pull data from messages and add it to the sorted list" - ([message-list messages] (hydrate-messages message-list messages {})) - ([message-list messages pinned-messages] - (keep #(if (= :message (% :type)) - (when-let [message (messages (% :message-id))] - (let [pinned-message (get pinned-messages (% :message-id)) - pinned (if pinned-message true (some? (message :pinned-by))) - pinned-by (when pinned (or (message :pinned-by) (pinned-message :pinned-by))) - message (assoc message :pinned pinned :pinned-by pinned-by)] - (merge message %))) - %) - message-list))) - -(re-frame/reg-sub - :chats/chat-no-messages? - (fn [[_ chat-id] _] - (re-frame/subscribe [:chats/chat-messages chat-id])) - (fn [messages] - (empty? messages))) - -(re-frame/reg-sub - :chats/raw-chat-messages-stream - (fn [[_ chat-id] _] - [(re-frame/subscribe [:chats/message-list chat-id]) - (re-frame/subscribe [:chats/chat-messages chat-id]) - (re-frame/subscribe [:chats/pinned chat-id]) - (re-frame/subscribe [:chats/loading-messages? chat-id]) - (re-frame/subscribe [:chats/synced-from chat-id]) - (re-frame/subscribe [:chats/chat-type chat-id]) - (re-frame/subscribe [:chats/joined chat-id])]) - (fn [[message-list messages pin-messages loading-messages? synced-from chat-type joined] [_ chat-id]] - ;;TODO (perf) - (let [message-list-seq (models.message-list/->seq message-list)] - ; Don't show gaps if that's the case as we are still loading messages - (if (and (empty? message-list-seq) loading-messages?) - [] - (-> message-list-seq - (chat.db/add-datemarks) - (hydrate-messages messages pin-messages) - (chat.db/collapse-gaps chat-id synced-from (datetime/timestamp) chat-type joined loading-messages?)))))) - -(re-frame/reg-sub - :chats/raw-chat-pin-messages-stream - (fn [[_ chat-id] _] - [(re-frame/subscribe [:chats/pin-message-list chat-id]) - (re-frame/subscribe [:chats/pinned chat-id]) - (re-frame/subscribe [:chats/loading-pin-messages? chat-id]) - (re-frame/subscribe [:chats/synced-from chat-id])]) - (fn [[pin-message-list messages loading-messages?] [_]] - ;;TODO (perf) - (let [pin-message-list-seq (models.message-list/->seq pin-message-list)] - ; Don't show gaps if that's the case as we are still loading messages - (if (and (empty? pin-message-list-seq) loading-messages?) - [] - (-> pin-message-list-seq - (chat.db/add-datemarks) - (hydrate-messages messages)))))) - -;;we want to keep data unchanged so react doesn't change component when we leave screen -(def memo-profile-messages-stream (atom nil)) - -(re-frame/reg-sub - :chats/profile-messages-stream - (fn [[_ chat-id] _] - [(re-frame/subscribe [:chats/raw-chat-messages-stream chat-id]) - (re-frame/subscribe [:chats/chat-no-messages? chat-id]) - (re-frame/subscribe [:view-id])]) - (fn [[messages empty view-id]] - (when (or (= view-id :profile) empty) - (reset! memo-profile-messages-stream messages)) - @memo-profile-messages-stream)) - -(def memo-timeline-messages-stream (atom nil)) - -(re-frame/reg-sub - :chats/timeline-messages-stream - :<- [:chats/message-list constants/timeline-chat-id] - :<- [:chats/chat-messages constants/timeline-chat-id] - :<- [:view-id] - (fn [[message-list messages view-id]] - (if (= view-id :status) - (let [res (-> (models.message-list/->seq message-list) - (hydrate-messages messages))] - (reset! memo-timeline-messages-stream res) - res) - @memo-timeline-messages-stream))) - -(re-frame/reg-sub - :chats/current-profile-chat - :<- [:contacts/current-contact-identity] - (fn [identity] - (chat.models/profile-chat-topic identity))) - -(re-frame/reg-sub - :chats/photo-path - :<- [:contacts/contacts] - :<- [:profile/multiaccount] - :<- [:mediaserver/port] - (fn [[contacts multiaccount port] [_ id]] - (let [contact (or (get contacts id) - (when (= id (:public-key multiaccount)) - multiaccount))] - (if (nil? contact) - (image-server/get-identicons-uri port id) - (multiaccounts/displayed-photo contact))))) - -(re-frame/reg-sub - :contacts/name-and-photo - :<- [:contacts/contacts] - :<- [:multiaccount] - (fn [[contacts multiaccount] [_ id]] - (let [contact (or (get contacts id) - (when (= id (:public-key multiaccount)) - multiaccount) - (contact.db/public-key->new-contact id))] - {:name (multiaccounts/displayed-name contact) - :photo (multiaccounts/displayed-photo contact)}))) - -(re-frame/reg-sub - :chats/unread-messages-number - :<- [:chats/home-list-chats] - (fn [chats _] - (reduce (fn [{:keys [public other]} {:keys [unviewed-messages-count public?] :as chat}] - (if (or public? (chat.models/community-chat? chat)) - {:public (+ public unviewed-messages-count) - :other other} - {:other (+ other unviewed-messages-count) - :public public})) - {:public 0 - :other 0} - chats))) - -(re-frame/reg-sub - :chats/cooldown-enabled? - :<- [:chats/current-chat] - :<- [::cooldown-enabled?] - (fn [[{:keys [public?]} cooldown-enabled?]] - (and public? - cooldown-enabled?))) - -(re-frame/reg-sub - :chats/reply-message - :<- [:chats/current-chat-inputs] - (fn [{:keys [metadata]}] - (:responding-to-message metadata))) - -(re-frame/reg-sub - :chats/edit-message - :<- [:chats/current-chat-inputs] - (fn [{:keys [metadata]}] - (:editing-message metadata))) - -(re-frame/reg-sub - :chats/sending-contact-request - :<- [:chats/current-chat-inputs] - (fn [{:keys [metadata]}] - (:sending-contact-request metadata))) - -(re-frame/reg-sub - :chats/sending-image - :<- [:chats/current-chat-inputs] - (fn [{:keys [metadata]}] - (:sending-image metadata))) - -(re-frame/reg-sub - :chats/timeline-sending-image - :<- [:chats/timeline-chat-input] - (fn [{:keys [metadata]}] - (:sending-image metadata))) - -(re-frame/reg-sub - :chats/chat-toolbar - :<- [:multiaccounts/login] - :<- [:chats/sending-image] - :<- [:mainnet?] - :<- [:current-chat/one-to-one-chat?] - :<- [:current-chat/metadata] - :<- [:chats/reply-message] - :<- [:chats/edit-message] - :<- [:chats/sending-contact-request] - (fn [[{:keys [processing]} sending-image mainnet? one-to-one-chat? {:keys [public?]} reply edit sending-contact-request]] - (let [sending-image (seq sending-image)] - {:send (not processing) - :stickers (and (or config/stickers-test-enabled? mainnet?) - (not sending-image) - (not sending-contact-request) - (not reply)) - :image (and (not reply) - (not edit) - (not sending-contact-request) - (not public?)) - :extensions (and one-to-one-chat? - (or config/commands-enabled? mainnet?) - (not edit) - (not sending-contact-request) - (not reply)) - :audio (and (not sending-image) - (not reply) - (not edit) - (not sending-contact-request) - (not public?)) - :sending-image sending-image}))) - -(re-frame/reg-sub - :public-chat.new/topic-error-message - :<- [:public-group-topic] - (fn [topic] - (when-not (or (empty? topic) - (db/valid-topic? topic)) - (i18n/label :topic-name-error)))) - -(defn filter-selected-contacts - [selected-contacts contacts] - (filter #(:added (contacts %)) selected-contacts)) - -(re-frame/reg-sub - :selected-contacts-count - :<- [:group/selected-contacts] - :<- [:contacts/contacts] - (fn [[selected-contacts contacts]] - (count (filter-selected-contacts selected-contacts contacts)))) - -(re-frame/reg-sub - :selected-participants-count - :<- [:selected-participants] - (fn [selected-participants] - (count selected-participants))) - -(defn filter-contacts [selected-contacts active-contacts] - (filter #(selected-contacts (:public-key %)) active-contacts)) - -(re-frame/reg-sub - :selected-group-contacts - :<- [:group/selected-contacts] - :<- [:contacts/active] - (fn [[selected-contacts active-contacts]] - (filter-contacts selected-contacts active-contacts))) - -(re-frame/reg-sub - :group-chat/inviter-info - (fn [[_ chat-id] _] - [(re-frame/subscribe [:chat-by-id chat-id]) - (re-frame/subscribe [:multiaccount/public-key])]) - (fn [[chat my-public-key]] - {:member? (group-chats.db/member? my-public-key chat) - :inviter-pk (group-chats.db/get-inviter-pk my-public-key chat)})) - -(re-frame/reg-sub - :group-chat/invitations-by-chat-id - :<- [:group-chat/invitations] - (fn [invitations [_ chat-id]] - (filter #(= (:chat-id %) chat-id) (vals invitations)))) - -(re-frame/reg-sub - :group-chat/pending-invitations-by-chat-id - (fn [[_ chat-id] _] - [(re-frame/subscribe [:group-chat/invitations-by-chat-id chat-id])]) - (fn [[invitations]] - (filter #(= constants/invitation-state-requested (:state %)) invitations))) - -(re-frame/reg-sub - :group-chat/removed-from-current-chat? - :<- [:chats/current-raw-chat] - :<- [:multiaccount/public-key] - (fn [[current-chat pk]] - (group-chat/member-removed? current-chat pk))) - -(re-frame/reg-sub - :chats/transaction-status - ;;TODO address here for transactions - :<- [:wallet/transactions] - :<- [:ethereum/current-block] - (fn [[transactions current-block] [_ hash]] - (when-let [transaction (get transactions hash)] - {:exists? true - :confirmed? - (-> transaction - (wallet.db/get-confirmations current-block) - (>= transactions/confirmations-count-threshold))}))) - -(re-frame/reg-sub - :chats/mentionable-users - :<- [:chats/current-chat] - :<- [:contacts/blocked-set] - :<- [:contacts/contacts] - :<- [:multiaccount] - (fn [[{:keys [users community-id] :as chat} blocked all-contacts - {:keys [public-key] :as current-multiaccount}]] - (let [community-members @(re-frame/subscribe [:communities/community-members community-id]) - mentionable-users (mentions/get-mentionable-users chat all-contacts current-multiaccount community-members) - members-left (into #{} (filter #(group-chat/member-removed? chat %) (keys users)))] - (apply dissoc mentionable-users (conj (concat blocked members-left) public-key))))) - -(re-frame/reg-sub - :chat/mention-suggestions - :<- [:chats/current-chat-id] - :<- [:chats/mention-suggestions] - (fn [[chat-id mentions]] - (take 15 (get mentions chat-id)))) - -(re-frame/reg-sub - :chat/input-with-mentions - :<- [:chats/current-chat-id] - :<- [:chat/inputs-with-mentions] - (fn [[chat-id cursor]] - (get cursor chat-id))) - -;;BOOTNODES ============================================================================================================ - -(re-frame/reg-sub - :custom-bootnodes/enabled? - :<- [:multiaccount] - :<- [:networks/current-network] - (fn [[{:keys [custom-bootnodes-enabled?]} current-network]] - (get custom-bootnodes-enabled? current-network))) - -(re-frame/reg-sub - :custom-bootnodes/network-bootnodes - :<- [:multiaccount] - :<- [:networks/current-network] - (fn [[multiaccount current-network]] - (get-in multiaccount [:custom-bootnodes current-network]))) - -(re-frame/reg-sub - :get-manage-bootnode - :<- [:bootnodes/manage] - (fn [manage] - manage)) - -(re-frame/reg-sub - :wakuv2-nodes/validation-errors - :<- [:wakuv2-nodes/manage] - (fn [manage] - (set (keep - (fn [[k {:keys [error]}]] - (when error k)) - manage)))) - -;;BROWSER ============================================================================================================== - -(re-frame/reg-sub - :browser/browsers - :<- [:browsers] - (fn [browsers] - (reduce (fn [acc [k browser]] - (update acc k assoc :url (browser/get-current-url browser))) - browsers - browsers))) - -(re-frame/reg-sub - :browser/browsers-vals - :<- [:browser/browsers] - (fn [browsers] - (reverse (vals browsers)))) - -(re-frame/reg-sub - :get-current-browser - :<- [:browser/options] - :<- [:browser/browsers] - (fn [[options browsers]] - (let [browser (get browsers (:browser-id options))] - (assoc browser :secure? (browser/secure? browser options))))) - -;;STICKERS ============================================================================================================= - -(re-frame/reg-sub - :stickers/installed-packs - :<- [:stickers/packs] - (fn [packs] - (filter #(= (:status %) constants/sticker-pack-status-installed) (vals packs)))) - -(re-frame/reg-sub - :stickers/all-packs - :<- [:stickers/packs] - (fn [packs] - (map (fn [{:keys [status] :as pack}] - (-> pack - (assoc :installed (= status constants/sticker-pack-status-installed)) - (assoc :pending (= status constants/sticker-pack-status-pending)) - (assoc :owned (= status constants/sticker-pack-status-owned)))) - (vals packs)))) - -(re-frame/reg-sub - :stickers/get-current-pack - :<- [:get-screen-params] - :<- [:stickers/all-packs] - (fn [[{:keys [id]} packs]] - (first (filter #(= (:id %) id) packs)))) - -;;HOME ============================================================================================================== - -(def memo-home-items (atom nil)) - -(re-frame/reg-sub - :home-items - :<- [:search/home-filter] - :<- [:search/filtered-chats] - :<- [:communities/communities] - :<- [:view-id] - :<- [:home-items-show-number] - (fn [[search-filter filtered-chats communities view-id home-items-show-number]] - (if (or (= view-id :home) - (and @config/new-ui-enabled? (= view-id :chat-stack))) - (let [communities-count (count communities) - chats-count (count filtered-chats) - ;; If we have both communities & chats we want to display - ;; a separator between them - - communities-with-separator (if (and (pos? communities-count) - (pos? chats-count)) - (update communities - (dec communities-count) - assoc :last? true) - communities) - res {:search-filter search-filter - :items (concat communities-with-separator (take home-items-show-number filtered-chats))}] - (reset! memo-home-items res) - res) - ;;we want to keep data unchanged so react doesn't change component when we leave screen - @memo-home-items))) - -(re-frame/reg-sub - :hide-home-tooltip? - :<- [:multiaccount] - (fn [multiaccount] - (:hide-home-tooltip? multiaccount))) - -;;PAIRING ============================================================================================================== - -(re-frame/reg-sub - :pairing/installations - :<- [:get-pairing-installations] - :<- [:pairing/installation-id] - (fn [[installations installation-id]] - (->> installations - vals - (pairing/sort-installations installation-id)))) - -(re-frame/reg-sub - :pairing/installation-id - :<- [:multiaccount] - (fn [multiaccount] (:installation-id multiaccount))) - -(re-frame/reg-sub - :pairing/installation-name - :<- [:multiaccount] - (fn [multiaccount] (:installation-name multiaccount))) - -;;PROFILE ============================================================================================================== - -(re-frame/reg-sub - :mnemonic - :<- [:multiaccount] - (fn [{:keys [mnemonic]}] - mnemonic)) - -(re-frame/reg-sub - :get-profile-unread-messages-number - :<- [:multiaccount] - (fn [{:keys [mnemonic]}] - (if mnemonic 1 0))) - -;;WALLET ============================================================================================================== - -(re-frame/reg-sub - :balance - :<- [:wallet] - (fn [wallet [_ address]] - (get-in wallet [:accounts address :balance]))) - -(re-frame/reg-sub - :balance-default - :<- [:wallet] - :<- [:multiaccount/accounts] - (fn [[wallet accounts]] - (get-in wallet [:accounts (:address (ethereum/get-default-account accounts)) :balance]))) - -(re-frame/reg-sub - :balances - :<- [:wallet] - :<- [:multiaccount/visible-accounts] - (fn [[wallet accounts]] - (let [accounts (map :address accounts)] - (map :balance (vals (select-keys (:accounts wallet) accounts)))))) - -(re-frame/reg-sub - :empty-balances? - :<- [:balances] - (fn [balances] - (every? - (fn [balance] - (every? - (fn [^js asset] - (or (nil? asset) (.isZero asset))) - (vals balance))) - balances))) - -(re-frame/reg-sub - :price - :<- [:prices] - (fn [prices [_ fsym tsym]] - (get-in prices [fsym tsym :price]))) - -(re-frame/reg-sub - :last-day - :<- [:prices] - (fn [prices [_ fsym tsym]] - (get-in prices [fsym tsym :last-day]))) - -(re-frame/reg-sub - :wallet.settings/currency - :<- [:multiaccount] - (fn [settings] - (or (get settings :currency) :usd))) - -(defn- get-balance-total-value - [balance prices currency token->decimals] - (reduce-kv (fn [acc symbol value] - (if-let [price (get-in prices [symbol currency :price])] - (+ acc (or (some-> (money/internal->formatted value symbol (token->decimals symbol)) - ^js (money/crypto->fiat price) - .toNumber) - 0)) - acc)) 0 balance)) - -(re-frame/reg-sub - :wallet/token->decimals - :<- [:wallet/all-tokens] - (fn [all-tokens] - (into {} (map #(vector (:symbol %) (:decimals %)) (vals all-tokens))))) - -(re-frame/reg-sub - :portfolio-value - :<- [:balances] - :<- [:prices] - :<- [:wallet/currency] - :<- [:wallet/token->decimals] - (fn [[balances prices currency token->decimals]] - (if (and balances prices) - (let [currency-key (-> currency :code keyword) - balance-total-value (apply + (map #(get-balance-total-value % prices currency-key token->decimals) balances))] - (if (pos? balance-total-value) - (-> balance-total-value - (money/with-precision 2) - str - (i18n/format-currency (:code currency))) - "0")) - "..."))) - -(re-frame/reg-sub - :account-portfolio-value - (fn [[_ address] _] - [(re-frame/subscribe [:balance address]) - (re-frame/subscribe [:prices]) - (re-frame/subscribe [:wallet/currency]) - (re-frame/subscribe [:wallet/token->decimals])]) - (fn [[balance prices currency token->decimals]] - (if (and balance prices) - (let [currency-key (-> currency :code keyword) - balance-total-value (get-balance-total-value balance prices currency-key token->decimals)] - (if (pos? balance-total-value) - (-> balance-total-value - (money/with-precision 2) - str - (i18n/format-currency (:code currency))) - "0")) - "..."))) - -(re-frame/reg-sub - :wallet/sorted-tokens - :<- [:wallet/all-tokens] - (fn [all-tokens] - (tokens/sorted-tokens-for all-tokens))) - -(re-frame/reg-sub - :wallet/grouped-chain-tokens - :<- [:wallet/sorted-tokens] - :<- [:wallet/visible-tokens-symbols] - (fn [[all-tokens visible-tokens]] - (let [vt-set (set visible-tokens)] - (group-by :custom? (map #(assoc % :checked? (boolean (get vt-set (keyword (:symbol %))))) all-tokens))))) - -(re-frame/reg-sub - :wallet/fetching-tx-history? - :<- [:wallet] - (fn [wallet [_ address]] - (get-in wallet [:fetching address :history?]))) - -(re-frame/reg-sub - :wallet/fetching-recent-tx-history? - :<- [:wallet] - (fn [wallet [_ address]] - (get-in wallet [:fetching address :recent?]))) - -(re-frame/reg-sub - :wallet/tx-history-fetched? - :<- [:wallet] - (fn [wallet [_ address]] - (get-in wallet [:fetching address :all-fetched?]))) - -(re-frame/reg-sub - :wallet/chain-explorer-link - (fn [db [_ address]] - (let [network (:networks/current-network db) - link (get-in config/default-networks-by-id - [network :chain-explorer-link])] - (when link - (str link address))))) - -(re-frame/reg-sub - :wallet/error-message - :<- [:wallet] - (fn [wallet] - (or (get-in wallet [:errors :balance-update]) - (get-in wallet [:errors :prices-update])))) - -(re-frame/reg-sub - :wallet/visible-tokens-symbols - :<- [:ethereum/chain-keyword] - :<- [:multiaccount] - (fn [[chain current-multiaccount]] - (get-in current-multiaccount [:wallet/visible-tokens chain]))) - -(re-frame/reg-sub - :wallet/visible-assets - :<- [:current-network] - :<- [:wallet/visible-tokens-symbols] - :<- [:wallet/sorted-tokens] - (fn [[network visible-tokens-symbols all-tokens-sorted]] - (conj (filter #(contains? visible-tokens-symbols (:symbol %)) all-tokens-sorted) - (tokens/native-currency network)))) - -(re-frame/reg-sub - :wallet/visible-assets-with-amount - (fn [[_ address] _] - [(re-frame/subscribe [:balance address]) - (re-frame/subscribe [:wallet/visible-assets])]) - (fn [[balance visible-assets]] - (map #(assoc % :amount (get balance (:symbol %))) visible-assets))) - -(defn update-value [prices currency] - (fn [{:keys [symbol decimals amount] :as token}] - (let [price (get-in prices [symbol (-> currency :code keyword) :price])] - (assoc token - :price price - :value (when (and amount price) - (-> (money/internal->formatted amount symbol decimals) - (money/crypto->fiat price) - (money/with-precision 2) - str - (i18n/format-currency (:code currency)))))))) - -(re-frame/reg-sub - :wallet/visible-assets-with-values - (fn [[_ address] _] - [(re-frame/subscribe [:wallet/visible-assets-with-amount address]) - (re-frame/subscribe [:prices]) - (re-frame/subscribe [:wallet/currency])]) - (fn [[assets prices currency]] - (let [{:keys [tokens nfts]} (group-by #(if (:nft? %) :nfts :tokens) assets) - tokens-with-values (map (update-value prices currency) tokens)] - {:tokens tokens-with-values - :nfts nfts}))) - -(defn get-asset-amount [balances sym] - (reduce #(if-let [^js bl (get %2 sym)] - (.plus ^js (or ^js %1 ^js (money/bignumber 0)) bl) - %1) - nil - balances)) - -(re-frame/reg-sub - :wallet/all-visible-assets-with-amount - :<- [:balances] - :<- [:wallet/visible-assets] - (fn [[balances visible-assets]] - (map #(assoc % :amount (get-asset-amount balances (:symbol %))) visible-assets))) - -(re-frame/reg-sub - :wallet/all-visible-assets-with-values - :<- [:wallet/all-visible-assets-with-amount] - :<- [:prices] - :<- [:wallet/currency] - (fn [[assets prices currency]] - (let [{:keys [tokens nfts]} (group-by #(if (:nft? %) :nfts :tokens) assets) - tokens-with-values (map (update-value prices currency) tokens)] - {:tokens tokens-with-values - :nfts nfts}))) - -(re-frame/reg-sub - :wallet/transferrable-assets-with-amount - (fn [[_ address]] - (re-frame/subscribe [:wallet/visible-assets-with-amount address])) - (fn [all-assets] - (filter #(not (:nft? %)) all-assets))) - -(re-frame/reg-sub - :wallet/currency - :<- [:wallet.settings/currency] - (fn [currency-id] - (get currency/currencies currency-id))) - -(defn filter-recipient-favs - [search-filter {:keys [name]}] - (string/includes? (string/lower-case (str name)) search-filter)) - -(re-frame/reg-sub - :wallet/favourites-filtered - :<- [:wallet/favourites] - :<- [:search/recipient-filter] - (fn [[favs search-filter]] - (let [favs (vals favs)] - (if (string/blank? search-filter) - favs - (filter (partial filter-recipient-favs - (string/lower-case search-filter)) - favs))))) - -(re-frame/reg-sub - :wallet/collectible-collection - :<- [:wallet/collectible-collections] - (fn [all-collections [_ address]] - (when address - (let [all-collections (get all-collections (string/lower-case address) [])] - (sort-by :name all-collections))))) - -(re-frame/reg-sub - :wallet/collectible-assets-by-collection-and-address - :<- [:wallet/collectible-assets] - (fn [all-assets [_ address collectible-slug]] - (get-in all-assets [address collectible-slug] []))) - -(re-frame/reg-sub - :wallet/fetching-assets-by-collectible-slug - :<- [:wallet/fetching-collection-assets] - (fn [fetching-collection-assets [_ collectible-slug]] - (get fetching-collection-assets collectible-slug false))) - -;;ACTIVITY CENTER NOTIFICATIONS ======================================================================================== - -(re-frame/reg-sub - :activity-center/notifications-read - (fn [db] - (get-in db [:activity-center :notifications-read :data]))) - -(re-frame/reg-sub - :activity-center/notifications-unread - (fn [db] - (get-in db [:activity-center :notifications-unread :data]))) - -(re-frame/reg-sub - :activity-center/current-status-filter - (fn [db] - (get-in db [:activity-center :current-status-filter]))) - -(re-frame/reg-sub - :activity-center/notifications-per-read-status - :<- [:activity-center/notifications-read] - :<- [:activity-center/notifications-unread] - :<- [:activity-center/current-status-filter] - (fn [[notifications-read notifications-unread status-filter]] - (if (= status-filter :unread) - notifications-unread - notifications-read))) - -(defn- group-notifications-by-date - [notifications] - (->> notifications - (group-by #(datetime/timestamp->date-key (:timestamp %))) - (sort-by key >) - (map (fn [[date-key notifications]] - (let [first-notification (first notifications)] - {:title (clojure.string/capitalize (datetime/day-relative (:timestamp first-notification))) - :key date-key - :data (sort-by :timestamp > notifications)}))))) - -(re-frame/reg-sub - :activity.center/notifications-grouped-by-date - :<- [:activity.center/notifications] - :<- [:contacts/contacts] - (fn [[{:keys [notifications]} contacts]] - (let [supported-notifications - (filter (fn [{:keys [type last-message message]}] - (or (and (= constants/activity-center-notification-type-one-to-one-chat type) - (not (nil? last-message))) - (and (= constants/activity-center-notification-type-contact-request type) - (not= constants/contact-request-message-state-none - (-> contacts - (multiaccounts/contact-by-identity (:from message)) - :contact-request-state))) - (= constants/activity-center-notification-type-contact-request-retracted type) - (= constants/activity-center-notification-type-private-group-chat type) - (= constants/activity-center-notification-type-reply type) - (= constants/activity-center-notification-type-mention type))) - notifications)] - (group-notifications-by-date - (map #(assoc % :timestamp (or (:timestamp %) (:timestamp (or (:message %) (:last-message %)))) - :contact (multiaccounts/contact-by-identity contacts (get-in % [:message :from]))) - supported-notifications))))) - -;;WALLET TRANSACTIONS ================================================================================================== - -(re-frame/reg-sub - :wallet/accounts - :<- [:wallet] - (fn [wallet] - (get wallet :accounts))) - -(re-frame/reg-sub - :wallet/account-by-transaction-hash - :<- [:wallet/accounts] - (fn [accounts [_ hash]] - (some (fn [[address account]] - (when-let [transaction (get-in account [:transactions hash])] - (assoc transaction :address address))) - accounts))) - -(re-frame/reg-sub - :wallet/transactions - :<- [:wallet] - (fn [wallet [_ address]] - (get-in wallet [:accounts address :transactions]))) - -(re-frame/reg-sub - :wallet/filters - :<- [:wallet] - (fn [wallet] - (get wallet :filters))) - -(defn enrich-transaction - [{:keys [type to from value token] :as transaction} - contacts native-currency] - (let [[contact-address key-contact key-wallet] - (if (= type :inbound) - [from :from-contact :to-wallet] - [to :to-contact :from-wallet]) - wallet (i18n/label :main-wallet) - contact (get contacts contact-address) - {:keys [symbol-display symbol decimals] :as asset} - (or token native-currency) - amount-text (if value - (wallet.utils/format-amount value decimals) - "...") - currency-text (when asset - (clojure.core/name (or symbol-display symbol)))] - (cond-> transaction - contact (assoc key-contact (:name contact)) - :always (assoc key-wallet wallet - :amount-text amount-text - :currency-text currency-text)))) - -(re-frame/reg-sub - :wallet.transactions/transactions - (fn [[_ address] _] - [(re-frame/subscribe [:wallet/transactions address]) - (re-frame/subscribe [:contacts/contacts-by-address]) - (re-frame/subscribe [:ethereum/native-currency])]) - (fn [[transactions contacts native-currency]] - (reduce (fn [acc [hash transaction]] - (assoc acc - hash - (enrich-transaction transaction contacts native-currency))) ;;TODO this doesn't look good for performance, we need to calculate this only once for each transaction - {} - transactions))) - -(re-frame/reg-sub - :wallet.transactions/all-filters? - :<- [:wallet/filters] - (fn [filters] - (= wallet.db/default-wallet-filters - filters))) - -(def filters-labels - {:inbound (i18n/label :t/incoming) - :outbound (i18n/label :t/outgoing) - :pending (i18n/label :t/pending) - :failed (i18n/label :t/failed)}) - -(re-frame/reg-sub - :wallet.transactions/filters - :<- [:wallet/filters] - (fn [filters] - (map (fn [id] - (let [checked? (filters id)] - {:id id - :label (filters-labels id) - :checked? checked? - :on-touch #(if checked? - (re-frame/dispatch [:wallet.transactions/remove-filter id]) - (re-frame/dispatch [:wallet.transactions/add-filter id]))})) - wallet.db/default-wallet-filters))) - -(re-frame/reg-sub - :wallet.transactions.filters/screen - :<- [:wallet.transactions/filters] - :<- [:wallet.transactions/all-filters?] - (fn [[filters all-filters?]] - {:all-filters? all-filters? - :filters filters - :on-touch-select-all (when-not all-filters? - #(re-frame/dispatch - [:wallet.transactions/add-all-filters]))})) - -(defn- enrich-transaction-for-list - [filters - {:keys [type from-contact from to-contact to hash timestamp] :as transaction} - address] - (when (filters type) - (assoc (case type - :inbound - (assoc transaction - :label (i18n/label :t/from) - :contact-accessibility-label :sender-text - :address-accessibility-label :sender-address-text - :contact from-contact - :address from) - (assoc transaction - :label (i18n/label :t/to) - :contact-accessibility-label :recipient-name-text - :address-accessibility-label :recipient-address-text - :contact to-contact - :address to)) - :time-formatted (datetime/timestamp->time timestamp) - :on-touch-fn #(re-frame/dispatch [:wallet.ui/show-transaction-details hash address])))) - -(defn- group-transactions-by-date - [transactions] - (->> transactions - (group-by #(datetime/timestamp->date-key (:timestamp %))) - (sort-by key >) - (map (fn [[date-key transactions]] - {:title (datetime/timestamp->mini-date (:timestamp (first transactions))) - :key date-key - :data (sort-by :timestamp > transactions)})))) - -(re-frame/reg-sub - :wallet.transactions.history/screen - (fn [[_ address] _] - [(re-frame/subscribe [:wallet.transactions/transactions address]) - (re-frame/subscribe [:wallet/filters]) - (re-frame/subscribe [:wallet.transactions/all-filters?])]) - (fn [[transactions filters all-filters?] [_ address]] - {:all-filters? all-filters? - :total (count transactions) - :transaction-history-sections - (->> transactions - vals - (keep #(enrich-transaction-for-list filters % address)) - (group-transactions-by-date))})) - -(re-frame/reg-sub - :wallet/recipient-recent-txs - (fn [[_ address] _] - [(re-frame/subscribe [:wallet.transactions/transactions address])]) - (fn [[transactions] _] - (->> transactions - vals - (sort-by :timestamp >) - (remove #(= (:type %) :pending)) - (take 3)))) - -(re-frame/reg-sub - :wallet.transactions.details/current-transaction - (fn [[_ _ address] _] - [(re-frame/subscribe [:wallet.transactions/transactions address]) - (re-frame/subscribe [:ethereum/native-currency]) - (re-frame/subscribe [:chain-id])]) - (fn [[transactions native-currency chain-id] [_ hash _]] - (let [{:keys [gas-used gas-price fee-cap tip-cap hash timestamp type] - :as transaction} - (get transactions hash) - native-currency-text (name (or (:symbol-display native-currency) - (:symbol native-currency)))] - (when transaction - (merge transaction - {:gas-price-eth (if gas-price - (money/wei->str :eth - gas-price - native-currency-text) - "-") - :gas-price-gwei (if gas-price - (money/wei->str :gwei - gas-price) - "-") - :fee-cap-gwei (if fee-cap - (money/wei->str :gwei - fee-cap) - "-") - :tip-cap-gwei (if tip-cap - (money/wei->str :gwei - tip-cap) - "-") - :date (datetime/timestamp->long-date timestamp)} - (if (= type :unsigned) - {:block (i18n/label :not-applicable) - :cost (i18n/label :not-applicable) - :gas-limit (i18n/label :not-applicable) - :gas-used (i18n/label :not-applicable) - :nonce (i18n/label :not-applicable) - :hash (i18n/label :not-applicable)} - {:cost (when gas-used - (money/wei->str :eth - (money/fee-value gas-used gas-price) - native-currency-text)) - :url (transactions/get-transaction-details-url - chain-id - hash)})))))) - -(re-frame/reg-sub - :wallet.transactions.details/screen - (fn [[_ hash address] _] - [(re-frame/subscribe [:wallet.transactions.details/current-transaction hash address]) - (re-frame/subscribe [:ethereum/current-block])]) - (fn [[transaction current-block]] - (let [confirmations (wallet.db/get-confirmations transaction - current-block)] - (assoc transaction - :confirmations confirmations - :confirmations-progress - (if (>= confirmations transactions/confirmations-count-threshold) - 100 - (* 100 (/ confirmations transactions/confirmations-count-threshold))))))) - -;;WALLET SEND ========================================================================================================== - -(re-frame/reg-sub - ::send-transaction - :<- [:wallet] - (fn [wallet] - (:send-transaction wallet))) - -(re-frame/reg-sub - :wallet.send/symbol - :<- [::send-transaction] - (fn [send-transaction] - (:symbol send-transaction))) - -(re-frame/reg-sub - :wallet.send/camera-flashlight - :<- [::send-transaction] - (fn [send-transaction] - (:camera-flashlight send-transaction))) - -(re-frame/reg-sub - :wallet/settings - :<- [:wallet] - (fn [{:keys [settings]}] - (reduce-kv #(conj %1 %3) [] settings))) - -(re-frame/reg-sub - :wallet.request/transaction - :<- [:wallet] - :request-transaction) - -(re-frame/reg-sub - :wallet/binance-chain? - :<- [:current-network] - (fn [network] - (ethereum/binance-chain-id? (get-in network [:config :NetworkId])))) - -;;UI ============================================================================================================== - -(re-frame/reg-sub - :connectivity/state - :<- [:network-status] - :<- [:disconnected?] - :<- [:mailserver/connecting?] - :<- [:mailserver/connection-error?] - :<- [:mailserver/request-error?] - :<- [:network/type] - :<- [:multiaccount] - (fn [[network-status disconnected? mailserver-connecting? mailserver-connection-error? - mailserver-request-error? network-type {:keys [syncing-on-mobile-network? use-mailservers?]}]] - (merge {:mobile (mobile-network-utils/cellular? network-type) - :sync syncing-on-mobile-network? - :peers :online} - (cond - (= network-status :offline) - {:peers :offline - :node :offline} - - (not use-mailservers?) - {:node :disabled} - - (or mailserver-connection-error? mailserver-connecting?) - {:node :connecting} - - mailserver-request-error? - {:node :error} - - disconnected? - {:peers :offline - :node :offline} - - :else - {:peers :online - :node :online})))) - -;;CONTACT ============================================================================================================== - -(re-frame/reg-sub - ::query-current-chat-contacts - :<- [:chats/current-chat] - :<- [:contacts/contacts] - (fn [[chat contacts] [_ query-fn]] - (contact.db/query-chat-contacts chat contacts query-fn))) - -(re-frame/reg-sub - :multiaccount/profile-pictures-show-to - :<- [:multiaccount] - (fn [multiaccount] - (get multiaccount :profile-pictures-show-to))) - -(re-frame/reg-sub - :mutual-contact-requests/enabled? - :<- [:multiaccount] - (fn [settings] - (boolean (:mutual-contact-enabled? settings)))) - -(re-frame/reg-sub - ::profile-pictures-visibility - :<- [:multiaccount] - (fn [multiaccount] - (get multiaccount :profile-pictures-visibility))) - -(defn- replace-contact-image-uri [contact port identity] - (let [identicon (image-server/get-identicons-uri port identity) - contact-images (:images contact) - contact-images (reduce (fn [acc image] (let [image-name (:type image) - ; We pass the clock so that we reload the image if the image is updated - clock (:clock image) - uri (image-server/get-contact-image-uri port identity image-name clock)] - (assoc-in acc [(keyword image-name) :uri] uri))) - contact-images - (vals contact-images))] - (assoc contact :identicon identicon :images contact-images))) - -(defn- reduce-contacts-image-uri [contacts port] - (reduce-kv (fn [acc public-key contact] - (let [contact (replace-contact-image-uri contact port public-key)] - (assoc acc public-key contact))) - {} - contacts)) - -(re-frame/reg-sub - :contacts/contacts - :<- [::contacts] - :<- [::profile-pictures-visibility] - :<- [:multiaccount/public-key] - :<- [:mediaserver/port] - (fn [[contacts profile-pictures-visibility public-key port]] - (let [contacts (contact.db/enrich-contacts contacts profile-pictures-visibility public-key)] - (reduce-contacts-image-uri contacts port)))) - -(re-frame/reg-sub - :contacts/active - :<- [:contacts/contacts] - (fn [contacts] - (contact.db/get-active-contacts contacts))) - -(re-frame/reg-sub - :contacts/sorted-contacts - :<- [:contacts/active] - (fn [active-contacts] - (->> active-contacts - (sort-by :alias) - (sort-by - #(visibility-status-utils/visibility-status-order (:public-key %)))))) - -(re-frame/reg-sub - :contacts/active-count - :<- [:contacts/active] - (fn [active-contacts] - (count active-contacts))) - -(re-frame/reg-sub - :contacts/blocked - :<- [:contacts/contacts] - (fn [contacts] - (->> contacts - (filter (fn [[_ contact]] - (:blocked contact))) - (contact.db/sort-contacts)))) - -(re-frame/reg-sub - :contacts/blocked-count - :<- [:contacts/blocked] - (fn [blocked-contacts] - (count blocked-contacts))) - -(defn filter-recipient-contacts - [search-filter {:keys [names]}] - (let [{:keys [nickname three-words-name ens-name]} names] - (or - (when ens-name - (string/includes? (string/lower-case (str ens-name)) search-filter)) - (string/includes? (string/lower-case three-words-name) search-filter) - (when nickname - (string/includes? (string/lower-case nickname) search-filter))))) - -(re-frame/reg-sub - :contacts/active-with-ens-names - :<- [:contacts/active] - :<- [:search/recipient-filter] - (fn [[contacts search-filter]] - (let [contacts (filter :ens-verified contacts)] - (if (string/blank? search-filter) - contacts - (filter (partial filter-recipient-contacts - (string/lower-case search-filter)) - contacts))))) - -(defn- enrich-contact [_ identity ens-name port] - (let [contact (contact.db/enrich-contact - (contact.db/public-key-and-ens-name->new-contact identity ens-name))] - (replace-contact-image-uri contact port identity))) - -(re-frame/reg-sub - :contacts/current-contact - :<- [:contacts/contacts] - :<- [:contacts/current-contact-identity] - :<- [:contacts/current-contact-ens-name] - :<- [:mediaserver/port] - (fn [[contacts identity ens-name port]] - (let [contact (get contacts identity)] - (cond-> contact - (nil? contact) (enrich-contact identity ens-name port))))) - -(re-frame/reg-sub - :contacts/contact-by-identity - :<- [:contacts/contacts] - (fn [contacts [_ identity]] - (multiaccounts/contact-by-identity contacts identity))) - -(re-frame/reg-sub - :contacts/contact-added? - (fn [[_ identity] _] - [(re-frame/subscribe [:contacts/contact-by-identity identity])]) - (fn [[contact] _] - (:added contact))) - -(re-frame/reg-sub - :contacts/contact-blocked? - (fn [[_ identity] _] - [(re-frame/subscribe [:contacts/contact-by-identity identity])]) - (fn [[contact] _] - (:blocked contact))) - -(re-frame/reg-sub - :contacts/contact-two-names-by-identity - (fn [[_ identity] _] - [(re-frame/subscribe [:contacts/contact-by-identity identity]) - (re-frame/subscribe [:multiaccount])]) - (fn [[contact current-multiaccount] [_ identity]] - (multiaccounts/contact-two-names-by-identity contact current-multiaccount - identity))) - -(re-frame/reg-sub - :contacts/contact-name-by-identity - (fn [[_ identity] _] - [(re-frame/subscribe [:contacts/contact-two-names-by-identity identity])]) - (fn [[names] _] - (first names))) - -(re-frame/reg-sub - :messages/quote-info - :<- [:chats/messages] - :<- [:contacts/contacts] - :<- [:multiaccount] - (fn [[messages contacts current-multiaccount] [_ message-id]] - (when-let [message (get messages message-id)] - (let [identity (:from message) - me? (= (:public-key current-multiaccount) identity)] - (if me? - {:quote {:from identity - :text (get-in message [:content :text])} - :ens-name (:preferred-name current-multiaccount) - :alias (gfycat/generate-gfy identity)} - (let [contact (or (contacts identity) - (contact.db/public-key->new-contact identity))] - {:quote {:from identity - :text (get-in message [:content :text])} - :ens-name (when (:ens-verified contact) - (:name contact)) - :alias (or (:alias contact) - (gfycat/generate-gfy identity))})))))) - -(re-frame/reg-sub - :contacts/all-contacts-not-in-current-chat - :<- [::query-current-chat-contacts remove] - (fn [contacts] - (filter :added contacts))) - -(re-frame/reg-sub - :contacts/current-chat-contacts - :<- [:chats/current-chat] - :<- [:contacts/contacts] - :<- [:multiaccount] - (fn [[{:keys [contacts admins]} all-contacts current-multiaccount]] - (contact.db/get-all-contacts-in-group-chat contacts admins all-contacts current-multiaccount))) - -(re-frame/reg-sub - :contacts/contacts-by-chat - (fn [[_ _ chat-id] _] - [(re-frame/subscribe [:chats/chat chat-id]) - (re-frame/subscribe [:contacts/contacts])]) - (fn [[chat all-contacts] [_ query-fn]] - (contact.db/query-chat-contacts chat all-contacts query-fn))) - -(re-frame/reg-sub - :contacts/contact-by-address - :<- [:contacts/contacts] - :<- [:multiaccount/contact] - (fn [[contacts multiaccount] [_ address]] - (if (ethereum/address= address (:public-key multiaccount)) - multiaccount - (contact.db/find-contact-by-address contacts address)))) - -(re-frame/reg-sub - :contacts/contacts-by-address - :<- [:contacts/contacts] - (fn [contacts] - (reduce (fn [acc [_ {:keys [address] :as contact}]] - (if address - (assoc acc address contact) - acc)) - {} - contacts))) - -;;MAILSERVER =========================================================================================================== - -(re-frame/reg-sub - :mailserver/connecting? - :<- [:mailserver/state] - (fn [state] - (#{:connecting :added} state))) - -(re-frame/reg-sub - :mailserver/connection-error? - :<- [:mailserver/state] - (fn [state] - (#{:error :disconnected} state))) - -(re-frame/reg-sub - :chats/fetching-gap-in-progress? - :<- [:mailserver/fetching-gaps-in-progress] - (fn [gaps [_ ids _]] - (seq (select-keys gaps ids)))) - -(re-frame/reg-sub - :mailserver/fetching? - :<- [:mailserver/state] - :<- [:mailserver/pending-requests] - :<- [:mailserver/connecting?] - :<- [:mailserver/connection-error?] - :<- [:mailserver/request-error?] - (fn [[state pending-requests connecting? connection-error? request-error?]] - (and pending-requests - (= state :connected) - (pos-int? pending-requests) - (not (or connecting? connection-error? request-error?))))) - -(re-frame/reg-sub - :mailserver/fleet-mailservers - :<- [:fleets/current-fleet] - :<- [:mailserver/mailservers] - (fn [[current-fleet mailservers]] - (current-fleet mailservers))) - -(re-frame/reg-sub - :mailserver.edit/connected? - :<- [:mailserver.edit/mailserver] - :<- [:mailserver/current-id] - (fn [[mailserver current-mailserver-id]] - (= (get-in mailserver [:id :value]) - current-mailserver-id))) - -(re-frame/reg-sub - :mailserver/use-status-nodes? - (fn [db _] - (boolean (mailserver/fetch-use-mailservers? {:db db})))) - -(re-frame/reg-sub - :mailserver.edit/validation-errors - :<- [:mailserver.edit/mailserver] - (fn [mailserver] - (set (keep - (fn [[k {:keys [error]}]] - (when error k)) - mailserver)))) - -(re-frame/reg-sub - :mailserver/connected? - :<- [:mailserver/state] - :<- [:disconnected?] - (fn [[mail-state disconnected?]] - (let [mailserver-connected? (= :connected mail-state)] - (and mailserver-connected? - (not disconnected?))))) - -(re-frame/reg-sub - :mailserver/preferred-id - :<- [:multiaccount] - (fn [multiaccount] - (get-in multiaccount - [:pinned-mailservers (fleet/current-fleet-sub multiaccount)]))) - -;;SEARCH ============================================================================================================== - -(defn extract-chat-attributes [chat] - (let [{:keys [name alias tags]} (val chat)] - (into [name alias] tags))) - -(defn sort-by-timestamp - [coll] - (when (not-empty coll) - (sort-by #(-> % second :timestamp) > - (into {} coll)))) - -(defn apply-filter - "extract-attributes-fn is a function that take an element from the collection - and returns a vector of attributes which are strings - apply-filter returns the elements for which at least one attribute includes - the search-filter - apply-filter returns nil if there is no element that match the filter - apply-filter returns full collection if the search-filter is empty" - [search-filter coll extract-attributes-fn sort?] - (let [results (if (not-empty search-filter) - (let [search-filter (string/lower-case search-filter)] - (filter (fn [element] - (some (fn [v] - (let [s (cond (string? v) v - (keyword? v) (name v))] - (when (string? s) - (string/includes? (string/lower-case s) - search-filter)))) - (extract-attributes-fn element))) - coll)) - coll)] - (if sort? - (sort-by-timestamp results) - results))) - -(defn filter-chat - [contacts search-filter {:keys [group-chat alias name chat-id]}] - (let [alias (if-not group-chat - (string/lower-case (or alias - (get-in contacts [chat-id :alias]) - (gfycat/generate-gfy chat-id))) - "") - nickname (get-in contacts [chat-id :nickname])] - (or - (string/includes? (string/lower-case (str name)) search-filter) - (string/includes? (string/lower-case alias) search-filter) - (when nickname - (string/includes? (string/lower-case nickname) search-filter)) - (and - (get-in contacts [chat-id :ens-verified]) - (string/includes? (string/lower-case - (str (get-in contacts [chat-id :name]))) - search-filter))))) - -(re-frame/reg-sub - :search/filtered-chats - :<- [:chats/home-list-chats] - :<- [:contacts/contacts] - :<- [:search/home-filter] - (fn [[chats contacts search-filter]] - ;; Short-circuit if search-filter is empty - (let [filtered-chats (if (seq search-filter) - (filter - (partial filter-chat - contacts - (string/lower-case search-filter)) - chats) - chats)] - (sort-by :timestamp > filtered-chats)))) - -(defn extract-currency-attributes [currency] - (let [{:keys [code display-name]} (val currency)] - [code display-name])) - -(re-frame/reg-sub - :search/filtered-currencies - :<- [:search/currency-filter] - (fn [search-currency-filter] - {:search-filter search-currency-filter - :currencies (apply-filter search-currency-filter currency/currencies extract-currency-attributes false)})) - -(defn extract-token-attributes [token] - (let [{:keys [symbol name]} token] - [symbol name])) - -(re-frame/reg-sub - :wallet/filtered-grouped-chain-tokens - :<- [:wallet/grouped-chain-tokens] - :<- [:search/token-filter] - (fn [[{custom-tokens true default-tokens nil} search-token-filter]] - {:search-filter search-token-filter - :tokens {true (apply-filter search-token-filter custom-tokens extract-token-attributes false) - nil (apply-filter search-token-filter default-tokens extract-token-attributes false)}})) - -;;ENS ================================================================================================================== -(re-frame/reg-sub - :multiaccount/usernames - :<- [:multiaccount] - (fn [multiaccount] - (:usernames multiaccount))) - -(re-frame/reg-sub - :ens/preferred-name - :<- [:multiaccount] - (fn [multiaccount] - (:preferred-name multiaccount))) - -(re-frame/reg-sub - :ens/search-screen - :<- [:ens/registration] - (fn [{:keys [custom-domain? username state]}] - {:state state - :username username - :custom-domain? custom-domain?})) - -(defn- ens-amount-label - [chain-id] - (str (ens/registration-cost chain-id) - (case chain-id - 3 " STT" - 1 " SNT" - ""))) - -(re-frame/reg-sub - :ens/checkout-screen - :<- [:ens/registration] - :<- [:chain-keyword] - :<- [:multiaccount/default-account] - :<- [:multiaccount/public-key] - :<- [:chain-id] - :<- [:wallet] - (fn [[{:keys [custom-domain? username address]} - chain default-account public-key chain-id wallet]] - (let [address (or address (ethereum/normalized-hex (:address default-account))) - balance (get-in wallet [:accounts address :balance])] - {:address address - :username username - :public-key public-key - :custom-domain? custom-domain? - :chain chain - :amount-label (ens-amount-label chain-id) - :sufficient-funds? (money/sufficient-funds? - (money/formatted->internal (money/bignumber 10) (ethereum/chain-keyword->snt-symbol chain) 18) - (get balance (ethereum/chain-keyword->snt-symbol chain)))}))) - -(re-frame/reg-sub - :ens/confirmation-screen - :<- [:ens/registration] - (fn [{:keys [username state]}] - {:state state - :username username})) - -(re-frame/reg-sub - :ens.name/screen - :<- [:get-screen-params :ens-name-details] - :<- [:ens/names] - (fn [[name ens]] - (let [{:keys [address public-key expiration-date releasable?]} (get ens name) - pending? (nil? address)] - (cond-> {:name name - :custom-domain? (not (string/ends-with? name ".stateofus.eth"))} - pending? - (assoc :pending? true) - (not pending?) - (assoc :address address - :public-key public-key - :releasable? releasable? - :expiration-date expiration-date))))) - -(re-frame/reg-sub - :ens.main/screen - :<- [:multiaccount/usernames] - :<- [:multiaccount] - :<- [:ens/preferred-name] - :<- [:ens/registrations] - (fn [[names multiaccount preferred-name registrations]] - {:names names - :multiaccount multiaccount - :preferred-name preferred-name - :registrations registrations})) - -;;SIGNING ============================================================================================================= - -(re-frame/reg-sub - :signing/fee - :<- [:signing/tx] - (fn [{:keys [gas gasPrice maxFeePerGas]}] - (signing.gas/calculate-max-fee gas (or maxFeePerGas gasPrice)))) - -(re-frame/reg-sub - :signing/currencies - :<- [:prices] - :<- [:wallet/currency] - :<- [:ethereum/native-currency] - (fn [[prices {:keys [code]} {:keys [symbol]}]] - [(name symbol) - code - (get-in prices [symbol (keyword code) :price])])) - -(re-frame/reg-sub - :signing/priority-fee-suggestions-range - :<- [:wallet/current-priority-fee] - :<- [:wallet/slow-base-fee] - :<- [:wallet/normal-base-fee] - :<- [:wallet/fast-base-fee] - (fn [[latest-tip slow normal fast]] - (reduce - (fn [acc [k fees]] - (assoc acc k (reduce - (fn [acc [k fee]] - (assoc acc k (-> fee - money/wei->gwei - (money/to-fixed 2)))) - {} - fees))) - {} - (signing.gas/get-fee-options latest-tip slow normal fast)))) - -(re-frame/reg-sub - :signing/phrase - :<- [:multiaccount] - (fn [{:keys [signing-phrase]}] - signing-phrase)) - -(re-frame/reg-sub - :signing/sign-message - :<- [:signing/sign] - :<- [:multiaccount/accounts] - :<- [:prices] - (fn [[sign wallet-accounts prices]] - (if (= :pinless (:type sign)) - (let [message (get-in sign [:formatted-data :message]) - wallet-acc (some #(when (= (:address %) (:receiver message)) %) wallet-accounts)] - (cond-> sign - (and (:amount message) (:currency message)) - (assoc :fiat-amount - (money/fiat-amount-value (:amount message) - (:currency message) - :USD prices) - :fiat-currency "USD") - (and (:receiver message) wallet-acc) - (assoc :account wallet-acc))) - sign))) - -(defn- too-precise-amount? - "Checks if number has any extra digit beyond the allowed number of decimals. - It does so by checking the number against its rounded value." - [amount decimals] - (let [^js bn (money/bignumber amount)] - (not (.eq bn (.round bn decimals))))) - -(defn get-amount-error [amount decimals] - (when (and (seq amount) decimals) - (let [normalized-amount (money/normalize amount) - value (money/bignumber normalized-amount)] - (cond - (not (money/valid? value)) - {:amount-error (i18n/label :t/validation-amount-invalid-number)} - - (too-precise-amount? normalized-amount decimals) - {:amount-error (i18n/label :t/validation-amount-is-too-precise {:decimals decimals})} - - :else nil)))) - -(defn get-sufficient-funds-error - [balance symbol amount] - (when-not (money/sufficient-funds? amount (get balance symbol)) - {:amount-error (i18n/label :t/wallet-insufficient-funds)})) - -(defn gas-required-exceeds-allowance? [gas-error-message] - (and gas-error-message (string/starts-with? - gas-error-message - "gas required exceeds allowance"))) - -(defn get-sufficient-gas-error - [gas-error-message balance symbol amount ^js gas ^js gasPrice] - (if (and gas gasPrice) - (let [^js fee (.times gas gasPrice) - ^js available-ether (money/bignumber (get balance :ETH 0)) - ^js available-for-gas (if (= :ETH symbol) - (.minus available-ether (money/bignumber amount)) - available-ether)] - (merge {:gas-error-state (when gas-error-message :gas-is-set)} - (when-not (money/sufficient-funds? fee (money/bignumber available-for-gas)) - {:gas-error (i18n/label :t/wallet-insufficient-gas)}))) - (let [insufficient-balance? (gas-required-exceeds-allowance? gas-error-message)] - {:gas-error-state (when gas-error-message :gas-isnt-set) - :insufficient-balalce? insufficient-balance? - :gas-error (if insufficient-balance? - (i18n/label :t/insufficient-balance-to-cover-fee) - (or gas-error-message - (i18n/label :t/invalid-number)))}))) - -(re-frame/reg-sub - :signing/amount-errors - (fn [[_ address] _] - [(re-frame/subscribe [:signing/tx]) - (re-frame/subscribe [:balance address])]) - (fn [[{:keys [amount token gas gasPrice maxFeePerGas approve? gas-error-message]} balance]] - (let [gas-price (or maxFeePerGas gasPrice)] - (if (and amount token (not approve?)) - (let [amount-bn (money/formatted->internal (money/bignumber amount) (:symbol token) (:decimals token)) - amount-error (or (get-amount-error amount (:decimals token)) - (get-sufficient-funds-error balance (:symbol token) amount-bn))] - (merge amount-error (get-sufficient-gas-error gas-error-message balance (:symbol token) amount-bn gas gas-price))) - (get-sufficient-gas-error gas-error-message balance nil nil gas gas-price))))) - -(re-frame/reg-sub - :wallet.send/prepare-transaction-with-balance - :<- [:wallet/prepare-transaction] - :<- [:wallet] - :<- [:offline?] - :<- [:wallet/all-tokens] - :<- [:current-network] - (fn [[{:keys [symbol from to amount-text] :as transaction} - wallet offline? all-tokens current-network]] - (let [balance (get-in wallet [:accounts (:address from) :balance]) - {:keys [decimals] :as token} (tokens/asset-for all-tokens current-network symbol) - {:keys [value error]} (wallet.db/parse-amount amount-text decimals) - amount (money/formatted->internal value symbol decimals) - {:keys [amount-error] :as transaction-new} - (merge transaction - {:amount-error error} - (when amount - (get-sufficient-funds-error balance symbol amount)))] - (assoc transaction-new - :amount amount - :balance balance - :token (assoc token :amount (get balance (:symbol token))) - :sign-enabled? (and to - (nil? amount-error) - (not (nil? amount)) - (not offline?)))))) - -(re-frame/reg-sub - :wallet.request/prepare-transaction-with-balance - :<- [:wallet/prepare-transaction] - :<- [:wallet] - :<- [:offline?] - :<- [:wallet/all-tokens] - :<- [:current-network] - (fn [[{:keys [symbol from to amount-text] :as transaction} - wallet offline? all-tokens current-network]] - (let [balance (get-in wallet [:accounts (:address from) :balance]) - {:keys [decimals] :as token} (tokens/asset-for all-tokens current-network symbol) - {:keys [value error]} (wallet.db/parse-amount amount-text decimals) - amount (money/formatted->internal value symbol decimals) - {:keys [amount-error] :as transaction-new} - (assoc transaction - :amount-error error)] - (assoc transaction-new - :amount amount - :balance balance - :token (assoc token :amount (get balance (:symbol token))) - :sign-enabled? (and to - from - (nil? amount-error) - (not (nil? amount)) - (not offline?)))))) - -;; NETWORK SETTINGS - -(defn- filter-networks [network-type] - (fn [network] - (let [chain-id (ethereum/network->chain-id network) - testnet? (ethereum/testnet? chain-id) - custom? (:custom? network)] - (case network-type - :custom custom? - :mainnet (and (not custom?) (not testnet?)) - :testnet (and (not custom?) testnet?))))) - -(defn- label-networks [default-networks] - (fn [network] - (let [custom? (not (default-networks (:id network)))] - (assoc network :custom? custom?)))) - -(re-frame/reg-sub - :get-networks - :<- [:networks/networks] - (fn [networks] - (let [networks (map (label-networks (into #{} (map :id config/default-networks))) (sort-by :name (vals networks))) - types [:mainnet :testnet :custom]] - (zipmap - types - (map #(filter (filter-networks %) networks) types))))) - -(re-frame/reg-sub - :manage-network-valid? - :<- [:networks/manage] - (fn [manage] - (not-any? :error (vals manage)))) - -;; LINK PREVIEW ======================================================================================================== - -(re-frame/reg-sub - :link-preview/cache - :<- [:multiaccount] - (fn [multiaccount] - (get multiaccount :link-previews-cache))) - -(re-frame/reg-sub - :link-preview/enabled-sites - :<- [:multiaccount] - (fn [multiaccount] - (get multiaccount :link-previews-enabled-sites))) - -(re-frame/reg-sub - :link-preview/link-preview-request-enabled - :<- [:multiaccount] - (fn [multiaccount] - (get multiaccount :link-preview-request-enabled))) - -;; NOTIFICATIONS - -(re-frame/reg-sub - :notifications/wallet-transactions - :<- [:push-notifications/preferences] - (fn [pref] - (first (filter #(notifications/preference= % {:service "wallet" - :event "transaction" - :identifier "all"}) pref)))) - -(re-frame/reg-sub - :profile/has-picture - :<- [:multiaccount] - (fn [multiaccount] - (pos? (count (get multiaccount :images))))) - -(defn- replace-multiaccount-image-uri [multiaccount port] - (let [public-key (:public-key multiaccount) - identicon (image-server/get-identicons-uri port public-key) - multiaccount (assoc multiaccount :identicon identicon) - images (:images multiaccount) - images (reduce (fn [acc current] - (let [key-uid (:keyUid current) - image-name (:type current) - uri (image-server/get-account-image-uri port public-key image-name key-uid)] - (conj acc (assoc current :uri uri)))) - [] - images)] - (assoc multiaccount :images images))) - -(re-frame/reg-sub - :profile/multiaccount - :<- [:multiaccount] - :<- [:mediaserver/port] - (fn [[multiaccount port]] - (replace-multiaccount-image-uri multiaccount port))) - -(re-frame/reg-sub - :mobile-network/syncing-allowed? - :<- [:network/type] - :<- [:multiaccount] - (fn [[network-type {:keys [syncing-on-mobile-network?]}]] - (or (= network-type "wifi") - (and - (= network-type "cellular") - syncing-on-mobile-network?)))) - -;; RESET PASSWORD -(re-frame/reg-sub - :multiaccount/reset-password-form-vals-and-errors - :<- [:multiaccount/reset-password-form-vals] - :<- [:multiaccount/reset-password-errors] - :<- [:multiaccount/resetting-password?] - (fn [[form-vals errors resetting?]] - (let [{:keys [current-password new-password confirm-new-password]} form-vals] - {:form-vals form-vals - :errors errors - :resetting? resetting? - :next-enabled? - (and (pos? (count current-password)) - (pos? (count new-password)) - (pos? (count confirm-new-password)) - (>= (count new-password) 6) - (>= (count current-password) 6) - (= new-password confirm-new-password))}))) - -(re-frame/reg-sub - :bookmarks/active - :<- [:bookmarks] - (fn [bookmarks] - (into {} (remove #(:removed (second %)) bookmarks)))) - -;; NAVIGATION2 - -(re-frame/reg-sub - :navigation2/switcher-cards - :<- [:navigation2/navigation2-stacks] - (fn [stacks [_ toggle-switcher-screen]] - (sort-by :clock > - (reduce (fn [acc stack-vector] - (let [{:keys [type clock id]} (get stack-vector 1)] - (conj acc {:type type - :clock clock - :id id - :toggle-switcher-screen toggle-switcher-screen}))) - '() stacks)))) diff --git a/src/status_im/subs/activity_center.cljs b/src/status_im/subs/activity_center.cljs new file mode 100644 index 0000000000..53dbc1a77b --- /dev/null +++ b/src/status_im/subs/activity_center.cljs @@ -0,0 +1,67 @@ +(ns status-im.subs.activity-center + (:require [re-frame.core :as re-frame] + [status-im.utils.datetime :as datetime] + [status-im.multiaccounts.core :as multiaccounts] + [status-im.constants :as constants] + [clojure.string :as string])) + +(re-frame/reg-sub + :activity-center/notifications-read + (fn [db] + (get-in db [:activity-center :notifications-read :data]))) + +(re-frame/reg-sub + :activity-center/notifications-unread + (fn [db] + (get-in db [:activity-center :notifications-unread :data]))) + +(re-frame/reg-sub + :activity-center/current-status-filter + (fn [db] + (get-in db [:activity-center :current-status-filter]))) + +(re-frame/reg-sub + :activity-center/notifications-per-read-status + :<- [:activity-center/notifications-read] + :<- [:activity-center/notifications-unread] + :<- [:activity-center/current-status-filter] + (fn [[notifications-read notifications-unread status-filter]] + (if (= status-filter :unread) + notifications-unread + notifications-read))) + +(defn- group-notifications-by-date + [notifications] + (->> notifications + (group-by #(datetime/timestamp->date-key (:timestamp %))) + (sort-by key >) + (map (fn [[date-key notifications]] + (let [first-notification (first notifications)] + {:title (string/capitalize (datetime/day-relative (:timestamp first-notification))) + :key date-key + :data (sort-by :timestamp > notifications)}))))) + +(re-frame/reg-sub + :activity.center/notifications-grouped-by-date + :<- [:activity.center/notifications] + :<- [:contacts/contacts] + (fn [[{:keys [notifications]} contacts]] + (let [supported-notifications + (filter (fn [{:keys [type last-message message]}] + (or (and (= constants/activity-center-notification-type-one-to-one-chat type) + (not (nil? last-message))) + (and (= constants/activity-center-notification-type-contact-request type) + (not= constants/contact-request-message-state-none + (-> contacts + (multiaccounts/contact-by-identity (:from message)) + :contact-request-state))) + (= constants/activity-center-notification-type-contact-request-retracted type) + (= constants/activity-center-notification-type-private-group-chat type) + (= constants/activity-center-notification-type-reply type) + (= constants/activity-center-notification-type-mention type))) + notifications)] + (group-notifications-by-date + (map #(assoc % + :timestamp (or (:timestamp %) (:timestamp (or (:message %) (:last-message %)))) + :contact (multiaccounts/contact-by-identity contacts (get-in % [:message :from]))) + supported-notifications))))) \ No newline at end of file diff --git a/src/status_im/subs/bootnodes.cljs b/src/status_im/subs/bootnodes.cljs new file mode 100644 index 0000000000..f5e3d77132 --- /dev/null +++ b/src/status_im/subs/bootnodes.cljs @@ -0,0 +1,31 @@ +(ns status-im.subs.bootnodes + (:require [re-frame.core :as re-frame])) + +(re-frame/reg-sub + :custom-bootnodes/enabled? + :<- [:multiaccount] + :<- [:networks/current-network] + (fn [[{:keys [custom-bootnodes-enabled?]} current-network]] + (get custom-bootnodes-enabled? current-network))) + +(re-frame/reg-sub + :custom-bootnodes/network-bootnodes + :<- [:multiaccount] + :<- [:networks/current-network] + (fn [[multiaccount current-network]] + (get-in multiaccount [:custom-bootnodes current-network]))) + +(re-frame/reg-sub + :get-manage-bootnode + :<- [:bootnodes/manage] + (fn [manage] + manage)) + +(re-frame/reg-sub + :wakuv2-nodes/validation-errors + :<- [:wakuv2-nodes/manage] + (fn [manage] + (set (keep + (fn [[k {:keys [error]}]] + (when error k)) + manage)))) \ No newline at end of file diff --git a/src/status_im/subs/browser.cljs b/src/status_im/subs/browser.cljs new file mode 100644 index 0000000000..707c46a345 --- /dev/null +++ b/src/status_im/subs/browser.cljs @@ -0,0 +1,32 @@ +(ns status-im.subs.browser + (:require [re-frame.core :as re-frame] + [status-im.browser.core :as browser])) + +(re-frame/reg-sub + :browser/browsers + :<- [:browsers] + (fn [browsers] + (reduce (fn [acc [k browser]] + (update acc k assoc :url (browser/get-current-url browser))) + browsers + browsers))) + +(re-frame/reg-sub + :browser/browsers-vals + :<- [:browser/browsers] + (fn [browsers] + (reverse (vals browsers)))) + +(re-frame/reg-sub + :get-current-browser + :<- [:browser/options] + :<- [:browser/browsers] + (fn [[options browsers]] + (let [browser (get browsers (:browser-id options))] + (assoc browser :secure? (browser/secure? browser options))))) + +(re-frame/reg-sub + :bookmarks/active + :<- [:bookmarks] + (fn [bookmarks] + (into {} (remove #(:removed (second %)) bookmarks)))) \ No newline at end of file diff --git a/src/status_im/subs/chat/chats.cljs b/src/status_im/subs/chat/chats.cljs new file mode 100644 index 0000000000..4634609311 --- /dev/null +++ b/src/status_im/subs/chat/chats.cljs @@ -0,0 +1,413 @@ +(ns status-im.subs.chat.chats + (:require [re-frame.core :as re-frame] + [clojure.string :as string] + [status-im.constants :as constants] + [quo.design-system.colors :as colors] + [status-im.chat.models :as chat.models] + [status-im.communities.core :as communities] + [status-im.utils.image-server :as image-server] + [status-im.multiaccounts.core :as multiaccounts] + [status-im.group-chats.core :as group-chat] + [status-im.chat.models.mentions :as mentions] + [status-im.group-chats.db :as group-chats.db] + [status-im.utils.config :as config] + [status-im.add-new.db :as db] + [status-im.i18n.i18n :as i18n])) + +(re-frame/reg-sub + :chats/chat + :<- [:chats/chats] + (fn [chats [_ chat-id]] + (get chats chat-id))) + +(re-frame/reg-sub + :chats/by-community-id + :<- [:chats/chats] + (fn [chats [_ community-id]] + (->> chats + (keep (fn [[_ chat]] + (when (= (:community-id chat) community-id) + chat))) + (sort-by :timestamp >)))) + +(re-frame/reg-sub + :chats/with-empty-category-by-community-id + (fn [[_ community-id]] + [(re-frame/subscribe [:chats/by-community-id community-id]) + (re-frame/subscribe [:communities/community-chats community-id])]) + (fn [[chats comm-chats] [_ community-id]] + (filter #(string/blank? (get-in comm-chats [(string/replace (:chat-id %) community-id "") :categoryID])) chats))) + +(re-frame/reg-sub + :chats/sorted-categories-by-community-id + (fn [[_ community-id]] + [(re-frame/subscribe [:chats/by-community-id community-id]) + (re-frame/subscribe [:communities/community-chats community-id])]) + (fn [[chats comm-chats] [_ community-id]] + (let [chat-cat (into {} (map (fn [{:keys [id categoryID position]}] + {(str community-id id) {:categoryID categoryID + :position position}}) + (vals comm-chats)))] + (group-by :categoryID (sort-by :position + (map #(cond-> (merge % (chat-cat (:chat-id %))) + (= community-id constants/status-community-id) + (assoc :color colors/blue)) + chats)))))) + +(re-frame/reg-sub + :chats/category-by-chat-id + (fn [[_ community-id _]] + [(re-frame/subscribe [:communities/community community-id])]) + (fn [[{:keys [chats categories]}] [_ community-id chat-id]] + (get categories (get-in chats [(string/replace chat-id community-id "") :categoryID])))) + +(re-frame/reg-sub + :chats/community-chat-by-id + (fn [[_ community-id _]] + [(re-frame/subscribe [:communities/community community-id])]) + (fn [[{:keys [chats]}] [_ community-id chat-id]] + (get chats (string/replace chat-id community-id "")))) + +(re-frame/reg-sub + :chats/home-list-chats + :<- [:chats/chats] + :<- [:chats-home-list] + (fn [[chats active-chats]] + (reduce #(if-let [item (get chats %2)] + (conj %1 item) + %1) + [] + active-chats))) + +(re-frame/reg-sub + :chat-by-id + :<- [:chats/chats] + (fn [chats [_ chat-id]] + (get chats chat-id))) + +(re-frame/reg-sub + :chats/synced-from + (fn [[_ chat-id] _] + (re-frame/subscribe [:chat-by-id chat-id])) + (fn [{:keys [synced-from]}] + synced-from)) + +(re-frame/reg-sub + :chats/muted + (fn [[_ chat-id] _] + (re-frame/subscribe [:chat-by-id chat-id])) + (fn [{:keys [muted]}] + muted)) + +(re-frame/reg-sub + :chats/chat-type + (fn [[_ chat-id] _] + (re-frame/subscribe [:chat-by-id chat-id])) + (fn [{:keys [chat-type]}] + chat-type)) + +(re-frame/reg-sub + :chats/joined + (fn [[_ chat-id] _] + (re-frame/subscribe [:chat-by-id chat-id])) + (fn [{:keys [joined]}] + joined)) + +(re-frame/reg-sub + :chats/synced-to-and-from + (fn [[_ chat-id] _] + (re-frame/subscribe [:chat-by-id chat-id])) + (fn [chat] + (select-keys chat [:synced-to :synced-from]))) + +(re-frame/reg-sub + :chats/current-raw-chat + :<- [:chats/chats] + :<- [:chats/current-chat-id] + (fn [[chats current-chat-id]] + (get chats current-chat-id))) + +(re-frame/reg-sub + :chats/current-chat-inputs + :<- [:chats/current-chat-id] + :<- [:chat/inputs] + (fn [[chat-id inputs]] + (get inputs chat-id))) + +(re-frame/reg-sub + :chats/timeline-chat-input + :<- [:chat/inputs] + :<- [:multiaccount/public-key] + (fn [[inputs public-key]] + (get inputs (chat.models/profile-chat-topic public-key)))) + +(re-frame/reg-sub + :chats/timeline-chat-input-text + :<- [:chats/timeline-chat-input] + (fn [input] + (:input-text input))) + +(re-frame/reg-sub + :chats/current-chat-membership + :<- [:chats/current-chat-id] + :<- [:chat/memberships] + (fn [[chat-id memberships]] + (get memberships chat-id))) + +(re-frame/reg-sub + :chats/current-chat + :<- [:chats/current-raw-chat] + :<- [:multiaccount/public-key] + :<- [:communities/current-community] + :<- [:contacts/blocked-set] + :<- [:contacts/contacts] + :<- [:chat/inputs] + :<- [:mutual-contact-requests/enabled?] + (fn [[{:keys [group-chat chat-id] :as current-chat} my-public-key community blocked-users-set contacts inputs mutual-contact-requests-enabled?]] + (when current-chat + (cond-> current-chat + (chat.models/public-chat? current-chat) + (assoc :show-input? true) + + (and (chat.models/group-chat? current-chat) + (group-chats.db/member? my-public-key current-chat)) + (assoc :show-input? true + :member? true) + + (and (chat.models/community-chat? current-chat) + (communities/can-post? community my-public-key (:chat-id current-chat))) + (assoc :show-input? true) + + (not group-chat) + (assoc :show-input? + (and + (or + (not mutual-contact-requests-enabled?) + (get-in inputs [chat-id :metadata :sending-contact-request]) + (and mutual-contact-requests-enabled? + (= constants/contact-request-state-mutual + (get-in contacts [chat-id :contact-request-state])))) + (not (contains? blocked-users-set chat-id)))))))) + +(re-frame/reg-sub + :chats/current-chat-chat-view + :<- [:chats/current-chat] + (fn [current-chat] + (select-keys current-chat [:chat-id :show-input? :group-chat :admins :invitation-admin :public? :chat-type :color :chat-name :synced-to :synced-from :community-id :emoji]))) + +(re-frame/reg-sub + :current-chat/metadata + :<- [:chats/current-raw-chat] + (fn [current-chat] + (select-keys current-chat + [:community-id + :contacts + :public? + :group-chat + :chat-type + :chat-id + :chat-name + :color + :invitation-admin]))) + +(re-frame/reg-sub + :current-chat/one-to-one-chat? + :<- [:chats/current-raw-chat] + (fn [current-chat] + (not (or (chat.models/group-chat? current-chat) + (chat.models/public-chat? current-chat))))) + +(re-frame/reg-sub + :chats/current-profile-chat + :<- [:contacts/current-contact-identity] + (fn [identity] + (chat.models/profile-chat-topic identity))) + +(re-frame/reg-sub + :chats/photo-path + :<- [:contacts/contacts] + :<- [:profile/multiaccount] + :<- [:mediaserver/port] + (fn [[contacts multiaccount port] [_ id]] + (let [contact (or (get contacts id) + (when (= id (:public-key multiaccount)) + multiaccount))] + (if (nil? contact) + (image-server/get-identicons-uri port id) + (multiaccounts/displayed-photo contact))))) + +(re-frame/reg-sub + :chats/unread-messages-number + :<- [:chats/home-list-chats] + (fn [chats _] + (reduce (fn [{:keys [public other]} {:keys [unviewed-messages-count public?] :as chat}] + (if (or public? (chat.models/community-chat? chat)) + {:public (+ public unviewed-messages-count) + :other other} + {:other (+ other unviewed-messages-count) + :public public})) + {:public 0 + :other 0} + chats))) + +(re-frame/reg-sub + :chats/current-chat-cooldown-enabled? + :<- [:chats/current-chat] + :<- [:chats/cooldown-enabled?] + (fn [[{:keys [public?]} cooldown-enabled?]] + (and public? + cooldown-enabled?))) + +(re-frame/reg-sub + :chats/reply-message + :<- [:chats/current-chat-inputs] + (fn [{:keys [metadata]}] + (:responding-to-message metadata))) + +(re-frame/reg-sub + :chats/edit-message + :<- [:chats/current-chat-inputs] + (fn [{:keys [metadata]}] + (:editing-message metadata))) + +(re-frame/reg-sub + :chats/sending-contact-request + :<- [:chats/current-chat-inputs] + (fn [{:keys [metadata]}] + (:sending-contact-request metadata))) + +(re-frame/reg-sub + :chats/sending-image + :<- [:chats/current-chat-inputs] + (fn [{:keys [metadata]}] + (:sending-image metadata))) + +(re-frame/reg-sub + :chats/timeline-sending-image + :<- [:chats/timeline-chat-input] + (fn [{:keys [metadata]}] + (:sending-image metadata))) + +(re-frame/reg-sub + :chats/chat-toolbar + :<- [:multiaccounts/login] + :<- [:chats/sending-image] + :<- [:mainnet?] + :<- [:current-chat/one-to-one-chat?] + :<- [:current-chat/metadata] + :<- [:chats/reply-message] + :<- [:chats/edit-message] + :<- [:chats/sending-contact-request] + (fn [[{:keys [processing]} sending-image mainnet? one-to-one-chat? {:keys [public?]} reply edit sending-contact-request]] + (let [sending-image (seq sending-image)] + {:send (not processing) + :stickers (and (or config/stickers-test-enabled? mainnet?) + (not sending-image) + (not sending-contact-request) + (not reply)) + :image (and (not reply) + (not edit) + (not sending-contact-request) + (not public?)) + :extensions (and one-to-one-chat? + (or config/commands-enabled? mainnet?) + (not edit) + (not sending-contact-request) + (not reply)) + :audio (and (not sending-image) + (not reply) + (not edit) + (not sending-contact-request) + (not public?)) + :sending-image sending-image}))) + +(re-frame/reg-sub + :public-chat.new/topic-error-message + :<- [:public-group-topic] + (fn [topic] + (when-not (or (empty? topic) + (db/valid-topic? topic)) + (i18n/label :topic-name-error)))) + +(defn filter-selected-contacts + [selected-contacts contacts] + (filter #(:added (contacts %)) selected-contacts)) + +(re-frame/reg-sub + :selected-contacts-count + :<- [:group/selected-contacts] + :<- [:contacts/contacts] + (fn [[selected-contacts contacts]] + (count (filter-selected-contacts selected-contacts contacts)))) + +(re-frame/reg-sub + :selected-participants-count + :<- [:selected-participants] + (fn [selected-participants] + (count selected-participants))) + +(defn filter-contacts [selected-contacts active-contacts] + (filter #(selected-contacts (:public-key %)) active-contacts)) + +(re-frame/reg-sub + :selected-group-contacts + :<- [:group/selected-contacts] + :<- [:contacts/active] + (fn [[selected-contacts active-contacts]] + (filter-contacts selected-contacts active-contacts))) + +(re-frame/reg-sub + :group-chat/inviter-info + (fn [[_ chat-id] _] + [(re-frame/subscribe [:chat-by-id chat-id]) + (re-frame/subscribe [:multiaccount/public-key])]) + (fn [[chat my-public-key]] + {:member? (group-chats.db/member? my-public-key chat) + :inviter-pk (group-chats.db/get-inviter-pk my-public-key chat)})) + +(re-frame/reg-sub + :group-chat/invitations-by-chat-id + :<- [:group-chat/invitations] + (fn [invitations [_ chat-id]] + (filter #(= (:chat-id %) chat-id) (vals invitations)))) + +(re-frame/reg-sub + :group-chat/pending-invitations-by-chat-id + (fn [[_ chat-id] _] + [(re-frame/subscribe [:group-chat/invitations-by-chat-id chat-id])]) + (fn [[invitations]] + (filter #(= constants/invitation-state-requested (:state %)) invitations))) + +(re-frame/reg-sub + :group-chat/removed-from-current-chat? + :<- [:chats/current-raw-chat] + :<- [:multiaccount/public-key] + (fn [[current-chat pk]] + (group-chat/member-removed? current-chat pk))) + +(re-frame/reg-sub + :chats/mentionable-users + :<- [:chats/current-chat] + :<- [:contacts/blocked-set] + :<- [:contacts/contacts] + :<- [:multiaccount] + (fn [[{:keys [users community-id] :as chat} blocked all-contacts + {:keys [public-key] :as current-multiaccount}]] + (let [community-members @(re-frame/subscribe [:communities/community-members community-id]) + mentionable-users (mentions/get-mentionable-users chat all-contacts current-multiaccount community-members) + members-left (into #{} (filter #(group-chat/member-removed? chat %) (keys users)))] + (apply dissoc mentionable-users (conj (concat blocked members-left) public-key))))) + +(re-frame/reg-sub + :chat/mention-suggestions + :<- [:chats/current-chat-id] + :<- [:chats/mention-suggestions] + (fn [[chat-id mentions]] + (take 15 (get mentions chat-id)))) + +(re-frame/reg-sub + :chat/input-with-mentions + :<- [:chats/current-chat-id] + :<- [:chat/inputs-with-mentions] + (fn [[chat-id cursor]] + (get cursor chat-id))) \ No newline at end of file diff --git a/src/status_im/subs/chat/messages.cljs b/src/status_im/subs/chat/messages.cljs new file mode 100644 index 0000000000..c5d52d6dc4 --- /dev/null +++ b/src/status_im/subs/chat/messages.cljs @@ -0,0 +1,138 @@ +(ns status-im.subs.chat.messages + (:require [re-frame.core :as re-frame] + [status-im.chat.models.reactions :as models.reactions] + [status-im.chat.models.message-list :as models.message-list] + [status-im.chat.db :as chat.db] + [status-im.utils.datetime :as datetime] + [status-im.constants :as constants])) + +(re-frame/reg-sub + :chats/chat-messages + :<- [:messages/messages] + (fn [messages [_ chat-id]] + (get messages chat-id {}))) + +(re-frame/reg-sub + :chats/pinned + :<- [:messages/pin-messages] + (fn [pin-messages [_ chat-id]] + (get pin-messages chat-id {}))) + +(re-frame/reg-sub + :chats/pinned-sorted-list + :<- [:messages/pin-messages] + (fn [pin-messages [_ chat-id]] + (->> + (get pin-messages chat-id {}) + vals + (sort-by :pinned-at <)))) + +(re-frame/reg-sub + :chats/message-reactions + :<- [:multiaccount/public-key] + :<- [:messages/reactions] + (fn [[current-public-key reactions] [_ message-id chat-id]] + (models.reactions/message-reactions + current-public-key + (get-in reactions [chat-id message-id])))) + +(re-frame/reg-sub + :chats/all-loaded? + :<- [:messages/pagination-info] + (fn [pagination-info [_ chat-id]] + (get-in pagination-info [chat-id :all-loaded?]))) + +(re-frame/reg-sub + :chats/loading-messages? + :<- [:messages/pagination-info] + (fn [pagination-info [_ chat-id]] + (get-in pagination-info [chat-id :loading-messages?]))) + +(re-frame/reg-sub + :chats/loading-pin-messages? + :<- [:messages/pagination-info] + (fn [pagination-info [_ chat-id]] + (get-in pagination-info [chat-id :loading-pin-messages?]))) + +(re-frame/reg-sub + :chats/message-list + :<- [:messages/message-lists] + (fn [message-lists [_ chat-id]] + (get message-lists chat-id))) + +(re-frame/reg-sub + :chats/pin-message-list + :<- [:messages/pin-message-lists] + (fn [pin-message-lists [_ chat-id]] + (get pin-message-lists chat-id))) + +(defn hydrate-messages + "Pull data from messages and add it to the sorted list" + ([message-list messages] (hydrate-messages message-list messages {})) + ([message-list messages pinned-messages] + (keep #(if (= :message (% :type)) + (when-let [message (messages (% :message-id))] + (let [pinned-message (get pinned-messages (% :message-id)) + pinned (if pinned-message true (some? (message :pinned-by))) + pinned-by (when pinned (or (message :pinned-by) (pinned-message :pinned-by))) + message (assoc message :pinned pinned :pinned-by pinned-by)] + (merge message %))) + %) + message-list))) + +(re-frame/reg-sub + :chats/chat-no-messages? + (fn [[_ chat-id] _] + (re-frame/subscribe [:chats/chat-messages chat-id])) + (fn [messages] + (empty? messages))) + +(re-frame/reg-sub + :chats/raw-chat-messages-stream + (fn [[_ chat-id] _] + [(re-frame/subscribe [:chats/message-list chat-id]) + (re-frame/subscribe [:chats/chat-messages chat-id]) + (re-frame/subscribe [:chats/pinned chat-id]) + (re-frame/subscribe [:chats/loading-messages? chat-id]) + (re-frame/subscribe [:chats/synced-from chat-id]) + (re-frame/subscribe [:chats/chat-type chat-id]) + (re-frame/subscribe [:chats/joined chat-id])]) + (fn [[message-list messages pin-messages loading-messages? synced-from chat-type joined] [_ chat-id]] + ;;TODO (perf) + (let [message-list-seq (models.message-list/->seq message-list)] + ; Don't show gaps if that's the case as we are still loading messages + (if (and (empty? message-list-seq) loading-messages?) + [] + (-> message-list-seq + (chat.db/add-datemarks) + (hydrate-messages messages pin-messages) + (chat.db/collapse-gaps chat-id synced-from (datetime/timestamp) chat-type joined loading-messages?)))))) + +;;we want to keep data unchanged so react doesn't change component when we leave screen +(def memo-profile-messages-stream (atom nil)) + +(re-frame/reg-sub + :chats/profile-messages-stream + (fn [[_ chat-id] _] + [(re-frame/subscribe [:chats/raw-chat-messages-stream chat-id]) + (re-frame/subscribe [:chats/chat-no-messages? chat-id]) + (re-frame/subscribe [:view-id])]) + (fn [[messages empty view-id]] + (when (or (= view-id :profile) empty) + (reset! memo-profile-messages-stream messages)) + @memo-profile-messages-stream)) + +(def memo-timeline-messages-stream (atom nil)) + +(re-frame/reg-sub + :chats/timeline-messages-stream + :<- [:chats/message-list constants/timeline-chat-id] + :<- [:chats/chat-messages constants/timeline-chat-id] + :<- [:view-id] + (fn [[message-list messages view-id]] + (if (= view-id :status) + (let [res (-> (models.message-list/->seq message-list) + (hydrate-messages messages))] + (reset! memo-timeline-messages-stream res) + res) + @memo-timeline-messages-stream))) diff --git a/src/status_im/subs/communities.cljs b/src/status_im/subs/communities.cljs new file mode 100644 index 0000000000..7435538809 --- /dev/null +++ b/src/status_im/subs/communities.cljs @@ -0,0 +1,162 @@ +(ns status-im.subs.communities + (:require [re-frame.core :as re-frame] + [clojure.string :as string] + [status-im.multiaccounts.core :as multiaccounts] + [status-im.ui.screens.profile.visibility-status.utils :as visibility-status-utils] + [status-im.constants :as constants])) + +(re-frame/reg-sub + :communities + :<- [:raw-communities] + :<- [:communities/enabled?] + (fn [[raw-communities communities-enabled?]] + (if communities-enabled? + raw-communities + []))) + +(re-frame/reg-sub + :communities/fetching-community + :<- [:communities/resolve-community-info] + (fn [info [_ id]] + (get info id))) + +(re-frame/reg-sub + :communities/section-list + :<- [:communities] + (fn [communities] + (->> (vals communities) + (group-by (comp (fnil string/upper-case "") first :name)) + (sort-by (fn [[title]] title)) + (map (fn [[title data]] + {:title title + :data data}))))) + +(re-frame/reg-sub + :communities/community + :<- [:communities] + (fn [communities [_ id]] + (get communities id))) + +(re-frame/reg-sub + :communities/community-chats + :<- [:communities] + (fn [communities [_ id]] + (get-in communities [id :chats]))) + +(re-frame/reg-sub + :communities/community-members + :<- [:communities] + (fn [communities [_ id]] + (get-in communities [id :members]))) + +(re-frame/reg-sub + :communities/sorted-community-members + (fn [[_ community-id]] + (let [contacts (re-frame/subscribe [:contacts/contacts]) + multiaccount (re-frame/subscribe [:multiaccount]) + members (re-frame/subscribe [:communities/community-members community-id])] + [contacts multiaccount members])) + (fn [[contacts multiaccount members] _] + (let [names (reduce (fn [acc identity] + (let [me? (= (:public-key multiaccount) identity) + contact (when-not me? + (multiaccounts/contact-by-identity contacts identity)) + name (first (multiaccounts/contact-two-names-by-identity contact multiaccount identity))] + (assoc acc identity name))) + {} + (keys members))] + (->> members + (sort-by #(get names (get % 0))) + (sort-by #(visibility-status-utils/visibility-status-order (get % 0))))))) + +(re-frame/reg-sub + :communities/featured-communities + :<- [:communities/enabled?] + :<- [:search/home-filter] + :<- [:communities] + (fn [[communities-enabled? search-filter communities]] + (filterv + (fn [{:keys [name featured id]}] + (and (or featured (= name "Status")) ;; TO DO: remove once featured communities exist + (or communities-enabled? + (= id constants/status-community-id)) + (or (empty? search-filter) + (string/includes? (string/lower-case (str name)) search-filter)))) + (vals communities)))) + +(re-frame/reg-sub + :communities/communities + :<- [:communities/enabled?] + :<- [:search/home-filter] + :<- [:communities] + (fn [[communities-enabled? search-filter communities]] + (filterv + (fn [{:keys [name joined id]}] + (and joined + (or communities-enabled? + (= id constants/status-community-id)) + (or (empty? search-filter) + (string/includes? (string/lower-case (str name)) search-filter)))) + (vals communities)))) + +(re-frame/reg-sub + :communities/edited-community + :<- [:communities] + :<- [:communities/community-id-input] + (fn [[communities community-id]] + (get communities community-id))) + +(re-frame/reg-sub + :communities/current-community + :<- [:communities] + :<- [:chats/current-raw-chat] + (fn [[communities {:keys [community-id]}]] + (get communities community-id))) + +(re-frame/reg-sub + :communities/unviewed-count + (fn [[_ community-id]] + [(re-frame/subscribe [:chats/by-community-id community-id])]) + (fn [[chats]] + (reduce (fn [acc {:keys [unviewed-messages-count]}] + (+ acc (or unviewed-messages-count 0))) + 0 + chats))) + +(re-frame/reg-sub + :communities/unviewed-counts + (fn [[_ community-id]] + [(re-frame/subscribe [:chats/by-community-id community-id])]) + (fn [[chats]] + (reduce (fn [acc {:keys [unviewed-mentions-count unviewed-messages-count]}] + {:unviewed-messages-count (+ (:unviewed-messages-count acc) (or unviewed-messages-count 0)) + :unviewed-mentions-count (+ (:unviewed-mentions-count acc) (or unviewed-mentions-count 0))}) + {:unviewed-messages-count 0 + :unviewed-mentions-count 0} + chats))) + +(re-frame/reg-sub + :communities/requests-to-join-for-community + :<- [:communities/requests-to-join] + (fn [requests [_ community-id]] + (->> + (get requests community-id {}) + vals + (filter (fn [{:keys [state]}] + (= state constants/request-to-join-pending-state)))))) + +(re-frame/reg-sub + :community/categories + (fn [[_ community-id]] + [(re-frame/subscribe [:communities/community community-id])]) + (fn [[{:keys [categories]}] _] + categories)) + +(re-frame/reg-sub + :communities/sorted-categories + :<- [:communities] + (fn [communities [_ id]] + (->> (get-in communities [id :categories]) + (map #(assoc (get % 1) :community-id id)) + (sort-by :position) + (into [])))) \ No newline at end of file diff --git a/src/status_im/subs/contact.cljs b/src/status_im/subs/contact.cljs new file mode 100644 index 0000000000..8da21b79bc --- /dev/null +++ b/src/status_im/subs/contact.cljs @@ -0,0 +1,239 @@ +(ns status-im.subs.contact + (:require [re-frame.core :as re-frame] + [status-im.contact.db :as contact.db] + [status-im.utils.image-server :as image-server] + [status-im.ui.screens.profile.visibility-status.utils :as visibility-status-utils] + [clojure.string :as string] + [status-im.multiaccounts.core :as multiaccounts] + [status-im.utils.gfycat.core :as gfycat] + [status-im.ethereum.core :as ethereum])) + +(re-frame/reg-sub + ::query-current-chat-contacts + :<- [:chats/current-chat] + :<- [:contacts/contacts] + (fn [[chat contacts] [_ query-fn]] + (contact.db/query-chat-contacts chat contacts query-fn))) + +(re-frame/reg-sub + :multiaccount/profile-pictures-show-to + :<- [:multiaccount] + (fn [multiaccount] + (get multiaccount :profile-pictures-show-to))) + +(re-frame/reg-sub + :mutual-contact-requests/enabled? + :<- [:multiaccount] + (fn [settings] + (boolean (:mutual-contact-enabled? settings)))) + +(re-frame/reg-sub + ::profile-pictures-visibility + :<- [:multiaccount] + (fn [multiaccount] + (get multiaccount :profile-pictures-visibility))) + +(defn- replace-contact-image-uri [contact port identity] + (let [identicon (image-server/get-identicons-uri port identity) + contact-images (:images contact) + contact-images (reduce (fn [acc image] (let [image-name (:type image) + ; We pass the clock so that we reload the image if the image is updated + clock (:clock image) + uri (image-server/get-contact-image-uri port identity image-name clock)] + (assoc-in acc [(keyword image-name) :uri] uri))) + contact-images + (vals contact-images))] + (assoc contact :identicon identicon :images contact-images))) + +(defn- reduce-contacts-image-uri [contacts port] + (reduce-kv (fn [acc public-key contact] + (let [contact (replace-contact-image-uri contact port public-key)] + (assoc acc public-key contact))) + {} + contacts)) + +(re-frame/reg-sub + :contacts/contacts + :<- [:contacts/contacts-raw] + :<- [::profile-pictures-visibility] + :<- [:multiaccount/public-key] + :<- [:mediaserver/port] + (fn [[contacts profile-pictures-visibility public-key port]] + (let [contacts (contact.db/enrich-contacts contacts profile-pictures-visibility public-key)] + (reduce-contacts-image-uri contacts port)))) + +(re-frame/reg-sub + :contacts/active + :<- [:contacts/contacts] + (fn [contacts] + (contact.db/get-active-contacts contacts))) + +(re-frame/reg-sub + :contacts/sorted-contacts + :<- [:contacts/active] + (fn [active-contacts] + (->> active-contacts + (sort-by :alias) + (sort-by + #(visibility-status-utils/visibility-status-order (:public-key %)))))) + +(re-frame/reg-sub + :contacts/active-count + :<- [:contacts/active] + (fn [active-contacts] + (count active-contacts))) + +(re-frame/reg-sub + :contacts/blocked + :<- [:contacts/contacts] + (fn [contacts] + (->> contacts + (filter (fn [[_ contact]] + (:blocked contact))) + (contact.db/sort-contacts)))) + +(re-frame/reg-sub + :contacts/blocked-count + :<- [:contacts/blocked] + (fn [blocked-contacts] + (count blocked-contacts))) + +(defn filter-recipient-contacts + [search-filter {:keys [names]}] + (let [{:keys [nickname three-words-name ens-name]} names] + (or + (when ens-name + (string/includes? (string/lower-case (str ens-name)) search-filter)) + (string/includes? (string/lower-case three-words-name) search-filter) + (when nickname + (string/includes? (string/lower-case nickname) search-filter))))) + +(re-frame/reg-sub + :contacts/active-with-ens-names + :<- [:contacts/active] + :<- [:search/recipient-filter] + (fn [[contacts search-filter]] + (let [contacts (filter :ens-verified contacts)] + (if (string/blank? search-filter) + contacts + (filter (partial filter-recipient-contacts + (string/lower-case search-filter)) + contacts))))) + +(defn- enrich-contact [_ identity ens-name port] + (let [contact (contact.db/enrich-contact + (contact.db/public-key-and-ens-name->new-contact identity ens-name))] + (replace-contact-image-uri contact port identity))) + +(re-frame/reg-sub + :contacts/current-contact + :<- [:contacts/contacts] + :<- [:contacts/current-contact-identity] + :<- [:contacts/current-contact-ens-name] + :<- [:mediaserver/port] + (fn [[contacts identity ens-name port]] + (let [contact (get contacts identity)] + (cond-> contact + (nil? contact) + (enrich-contact identity ens-name port))))) + +(re-frame/reg-sub + :contacts/contact-by-identity + :<- [:contacts/contacts] + (fn [contacts [_ identity]] + (multiaccounts/contact-by-identity contacts identity))) + +(re-frame/reg-sub + :contacts/contact-added? + (fn [[_ identity] _] + [(re-frame/subscribe [:contacts/contact-by-identity identity])]) + (fn [[contact] _] + (:added contact))) + +(re-frame/reg-sub + :contacts/contact-blocked? + (fn [[_ identity] _] + [(re-frame/subscribe [:contacts/contact-by-identity identity])]) + (fn [[contact] _] + (:blocked contact))) + +(re-frame/reg-sub + :contacts/contact-two-names-by-identity + (fn [[_ identity] _] + [(re-frame/subscribe [:contacts/contact-by-identity identity]) + (re-frame/subscribe [:multiaccount])]) + (fn [[contact current-multiaccount] [_ identity]] + (multiaccounts/contact-two-names-by-identity contact current-multiaccount + identity))) + +(re-frame/reg-sub + :contacts/contact-name-by-identity + (fn [[_ identity] _] + [(re-frame/subscribe [:contacts/contact-two-names-by-identity identity])]) + (fn [[names] _] + (first names))) + +(re-frame/reg-sub + :messages/quote-info + :<- [:chats/messages] + :<- [:contacts/contacts] + :<- [:multiaccount] + (fn [[messages contacts current-multiaccount] [_ message-id]] + (when-let [message (get messages message-id)] + (let [identity (:from message) + me? (= (:public-key current-multiaccount) identity)] + (if me? + {:quote {:from identity + :text (get-in message [:content :text])} + :ens-name (:preferred-name current-multiaccount) + :alias (gfycat/generate-gfy identity)} + (let [contact (or (contacts identity) + (contact.db/public-key->new-contact identity))] + {:quote {:from identity + :text (get-in message [:content :text])} + :ens-name (when (:ens-verified contact) + (:name contact)) + :alias (or (:alias contact) + (gfycat/generate-gfy identity))})))))) + +(re-frame/reg-sub + :contacts/all-contacts-not-in-current-chat + :<- [::query-current-chat-contacts remove] + (fn [contacts] + (filter :added contacts))) + +(re-frame/reg-sub + :contacts/current-chat-contacts + :<- [:chats/current-chat] + :<- [:contacts/contacts] + :<- [:multiaccount] + (fn [[{:keys [contacts admins]} all-contacts current-multiaccount]] + (contact.db/get-all-contacts-in-group-chat contacts admins all-contacts current-multiaccount))) + +(re-frame/reg-sub + :contacts/contacts-by-chat + (fn [[_ _ chat-id] _] + [(re-frame/subscribe [:chats/chat chat-id]) + (re-frame/subscribe [:contacts/contacts])]) + (fn [[chat all-contacts] [_ query-fn]] + (contact.db/query-chat-contacts chat all-contacts query-fn))) + +(re-frame/reg-sub + :contacts/contact-by-address + :<- [:contacts/contacts] + :<- [:multiaccount/contact] + (fn [[contacts multiaccount] [_ address]] + (if (ethereum/address= address (:public-key multiaccount)) + multiaccount + (contact.db/find-contact-by-address contacts address)))) + +(re-frame/reg-sub + :contacts/contacts-by-address + :<- [:contacts/contacts] + (fn [contacts] + (reduce (fn [acc [_ {:keys [address] :as contact}]] + (if address + (assoc acc address contact) + acc)) + {} + contacts))) \ No newline at end of file diff --git a/src/status_im/subs/ens.cljs b/src/status_im/subs/ens.cljs new file mode 100644 index 0000000000..33f8279da4 --- /dev/null +++ b/src/status_im/subs/ens.cljs @@ -0,0 +1,92 @@ +(ns status-im.subs.ens + (:require [re-frame.core :as re-frame] + [status-im.ens.core :as ens] + [status-im.ethereum.core :as ethereum] + [status-im.utils.money :as money] + [clojure.string :as string])) + +(re-frame/reg-sub + :multiaccount/usernames + :<- [:multiaccount] + (fn [multiaccount] + (:usernames multiaccount))) + +(re-frame/reg-sub + :ens/preferred-name + :<- [:multiaccount] + (fn [multiaccount] + (:preferred-name multiaccount))) + +(re-frame/reg-sub + :ens/search-screen + :<- [:ens/registration] + (fn [{:keys [custom-domain? username state]}] + {:state state + :username username + :custom-domain? custom-domain?})) + +(defn- ens-amount-label + [chain-id] + (str (ens/registration-cost chain-id) + (case chain-id + 3 " STT" + 1 " SNT" + ""))) + +(re-frame/reg-sub + :ens/checkout-screen + :<- [:ens/registration] + :<- [:chain-keyword] + :<- [:multiaccount/default-account] + :<- [:multiaccount/public-key] + :<- [:chain-id] + :<- [:wallet] + (fn [[{:keys [custom-domain? username address]} + chain default-account public-key chain-id wallet]] + (let [address (or address (ethereum/normalized-hex (:address default-account))) + balance (get-in wallet [:accounts address :balance])] + {:address address + :username username + :public-key public-key + :custom-domain? custom-domain? + :chain chain + :amount-label (ens-amount-label chain-id) + :sufficient-funds? (money/sufficient-funds? + (money/formatted->internal (money/bignumber 10) (ethereum/chain-keyword->snt-symbol chain) 18) + (get balance (ethereum/chain-keyword->snt-symbol chain)))}))) + +(re-frame/reg-sub + :ens/confirmation-screen + :<- [:ens/registration] + (fn [{:keys [username state]}] + {:state state + :username username})) + +(re-frame/reg-sub + :ens.name/screen + :<- [:get-screen-params :ens-name-details] + :<- [:ens/names] + (fn [[name ens]] + (let [{:keys [address public-key expiration-date releasable?]} (get ens name) + pending? (nil? address)] + (cond-> {:name name + :custom-domain? (not (string/ends-with? name ".stateofus.eth"))} + pending? + (assoc :pending? true) + (not pending?) + (assoc :address address + :public-key public-key + :releasable? releasable? + :expiration-date expiration-date))))) + +(re-frame/reg-sub + :ens.main/screen + :<- [:multiaccount/usernames] + :<- [:multiaccount] + :<- [:ens/preferred-name] + :<- [:ens/registrations] + (fn [[names multiaccount preferred-name registrations]] + {:names names + :multiaccount multiaccount + :preferred-name preferred-name + :registrations registrations})) \ No newline at end of file diff --git a/src/status_im/subs/general.cljs b/src/status_im/subs/general.cljs new file mode 100644 index 0000000000..19b6a794e4 --- /dev/null +++ b/src/status_im/subs/general.cljs @@ -0,0 +1,298 @@ +(ns status-im.subs.general + (:require [re-frame.core :as re-frame] + [status-im.utils.build :as build] + [status-im.multiaccounts.model :as multiaccounts.model] + [status-im.ethereum.core :as ethereum] + [status-im.ethereum.tokens :as tokens] + [status-im.utils.mobile-sync :as mobile-network-utils])) + +(re-frame/reg-sub + :visibility-status-updates/visibility-status-update + :<- [:visibility-status-updates] + (fn [visibility-status-updates [_ public-key]] + (get visibility-status-updates public-key))) + +(re-frame/reg-sub + :multiaccount/logged-in? + (fn [db] + (multiaccounts.model/logged-in? {:db db}))) + +(re-frame/reg-sub + :hide-screen? + :<- [:app-state] + :<- [:multiaccount] + (fn [[state multiaccount]] + (and (= state "inactive") (:preview-privacy? multiaccount)))) + +(re-frame/reg-sub + :current-network + :<- [:networks/networks] + :<- [:networks/current-network] + (fn [[networks current-network]] + (when-let [network (get networks current-network)] + (assoc network :rpc-network? (get-in network [:config :UpstreamConfig :Enabled]))))) + +(re-frame/reg-sub + :custom-rpc-node + :<- [:current-network] + (fn [network] + (ethereum/custom-rpc-node? network))) + +(re-frame/reg-sub + :chain-keyword + :<- [:current-network] + (fn [network] + (ethereum/network->chain-keyword network))) + +(re-frame/reg-sub + :chain-name + :<- [:current-network] + (fn [network] + (ethereum/network->chain-name network))) + +(re-frame/reg-sub + :chain-id + :<- [:current-network] + (fn [network] + (ethereum/network->chain-id network))) + +(re-frame/reg-sub + :mainnet? + :<- [:chain-id] + (fn [chain-id] + (= 1 chain-id))) + +(re-frame/reg-sub + :ethereum-network? + :<- [:chain-id] + (fn [chain-id] + (< chain-id 6))) + +(re-frame/reg-sub + :network-name + :<- [:current-network] + (fn [network] + (:name network))) + +(re-frame/reg-sub + :disconnected? + :<- [:peers-count] + :<- [:waku/v2-flag] + :<- [:waku/v2-peer-stats] + (fn [[peers-count wakuv2-flag peer-stats]] + ;; If wakuv2 is enabled, + ;; then fetch connectivity status from + ;; peer-stats (populated from "wakuv2.peerstats" status-go signal) + ;; Otherwise use peers-count fetched from "discovery.summary" signal + (if wakuv2-flag (not (:isOnline peer-stats)) (zero? peers-count)))) + +(re-frame/reg-sub + :offline? + :<- [:network-status] + :<- [:sync-state] + :<- [:disconnected?] + (fn [[network-status sync-state disconnected?]] + (or disconnected? + (= network-status :offline) + (= sync-state :offline)))) + +(re-frame/reg-sub + :syncing? + :<- [:sync-state] + (fn [sync-state] + (#{:pending :in-progress} sync-state))) + +(re-frame/reg-sub + :dimensions/window-width + :<- [:dimensions/window] + :width) + +(re-frame/reg-sub + :dimensions/window-height + :<- [:dimensions/window] + :height) + +(re-frame/reg-sub + :dimensions/small-screen? + :<- [:dimensions/window-height] + (fn [height] + (< height 550))) + +(re-frame/reg-sub + :get-screen-params + :<- [:screen-params] + :<- [:view-id] + (fn [[params view-id-db] [_ view-id]] + (get params (or view-id view-id-db)))) + +(re-frame/reg-sub + :delete-swipe-position + :<- [:animations] + (fn [animations [_ type item-id]] + (get-in animations [type item-id :delete-swiped]))) + +(re-frame/reg-sub + :search/home-filter + :<- [:ui/search] + (fn [search] + (get search :home-filter))) + +(re-frame/reg-sub + :search/recipient-filter + :<- [:ui/search] + (fn [search] + (get search :recipient-filter))) + +(re-frame/reg-sub + :search/currency-filter + :<- [:ui/search] + (fn [search] + (get search :currency-filter))) + +(re-frame/reg-sub + :search/token-filter + :<- [:ui/search] + (fn [search] + (get search :token-filter))) + +(defn- node-version [web3-node-version] + (or web3-node-version "N/A")) + +(re-frame/reg-sub + :get-app-version + :<- [:web3-node-version] + (fn [web3-node-version] + (str build/app-short-version "; " (node-version web3-node-version)))) + +(re-frame/reg-sub + :get-app-short-version + build/app-short-version) + +(re-frame/reg-sub + :get-commit-hash + (fn [_] build/commit-hash)) + +(re-frame/reg-sub + :get-app-node-version + :<- [:web3-node-version] + node-version) + +(re-frame/reg-sub + :my-profile/recovery + :<- [:my-profile/seed] + (fn [seed] + (or seed {:step :intro}))) + +(re-frame/reg-sub + :bottom-sheet + :<- [:bottom-sheet/show?] + :<- [:bottom-sheet/view] + :<- [:bottom-sheet/options] + (fn [[show? view options]] + {:show? show? + :view view + :options options})) + +(re-frame/reg-sub + :is-contact-selected? + :<- [:group/selected-contacts] + (fn [selected-contacts [_ element]] + (-> selected-contacts + (contains? element)))) + +(re-frame/reg-sub + :is-participant-selected? + :<- [:selected-participants] + (fn [selected-participants [_ element]] + (-> selected-participants + (contains? element)))) + +(re-frame/reg-sub + :ethereum/chain-keyword + :<- [:current-network] + (fn [network] + (ethereum/network->chain-keyword network))) + +(re-frame/reg-sub + :ethereum/native-currency + :<- [:current-network] + (fn [network] + (tokens/native-currency network))) + +(re-frame/reg-sub + :information-box-closed? + :<- [:information-box-states] + (fn [states [_ id]] + (get states id))) + +(re-frame/reg-sub + :connectivity/state + :<- [:network-status] + :<- [:disconnected?] + :<- [:mailserver/connecting?] + :<- [:mailserver/connection-error?] + :<- [:mailserver/request-error?] + :<- [:network/type] + :<- [:multiaccount] + (fn [[network-status disconnected? mailserver-connecting? mailserver-connection-error? + mailserver-request-error? network-type {:keys [syncing-on-mobile-network? use-mailservers?]}]] + (merge {:mobile (mobile-network-utils/cellular? network-type) + :sync syncing-on-mobile-network? + :peers :online} + (cond (= network-status :offline) + {:peers :offline + :node :offline} + + (not use-mailservers?) + {:node :disabled} + + (or mailserver-connection-error? mailserver-connecting?) + {:node :connecting} + + mailserver-request-error? + {:node :error} + + disconnected? + {:peers :offline + :node :offline} + + :else + {:peers :online + :node :online})))) + +(re-frame/reg-sub + :mnemonic + :<- [:multiaccount] + (fn [{:keys [mnemonic]}] + mnemonic)) + +(re-frame/reg-sub + :get-profile-unread-messages-number + :<- [:multiaccount] + (fn [{:keys [mnemonic]}] + (if mnemonic 1 0))) + +;; NAVIGATION2 + +(re-frame/reg-sub + :navigation2/switcher-cards + :<- [:navigation2/navigation2-stacks] + (fn [stacks [_ toggle-switcher-screen]] + (sort-by :clock > + (reduce (fn [acc stack-vector] + (let [{:keys [type clock id]} (get stack-vector 1)] + (conj acc {:type type + :clock clock + :id id + :toggle-switcher-screen toggle-switcher-screen}))) + '() stacks)))) + +(re-frame/reg-sub + :mobile-network/syncing-allowed? + :<- [:network/type] + :<- [:multiaccount] + (fn [[network-type {:keys [syncing-on-mobile-network?]}]] + (or (= network-type "wifi") + (and + (= network-type "cellular") + syncing-on-mobile-network?)))) \ No newline at end of file diff --git a/src/status_im/subs/home.cljs b/src/status_im/subs/home.cljs new file mode 100644 index 0000000000..81048ac96e --- /dev/null +++ b/src/status_im/subs/home.cljs @@ -0,0 +1,39 @@ +(ns status-im.subs.home + (:require [re-frame.core :as re-frame] + [status-im.utils.config :as config])) + +(def memo-home-items (atom nil)) + +(re-frame/reg-sub + :home-items + :<- [:search/home-filter] + :<- [:search/filtered-chats] + :<- [:communities/communities] + :<- [:view-id] + :<- [:home-items-show-number] + (fn [[search-filter filtered-chats communities view-id home-items-show-number]] + (if (or (= view-id :home) + (and @config/new-ui-enabled? (= view-id :chat-stack))) + (let [communities-count (count communities) + chats-count (count filtered-chats) + ;; If we have both communities & chats we want to display + ;; a separator between them + + communities-with-separator (if (and (pos? communities-count) + (pos? chats-count)) + (update communities + (dec communities-count) + assoc :last? true) + communities) + res {:search-filter search-filter + :items (concat communities-with-separator (take home-items-show-number filtered-chats))}] + (reset! memo-home-items res) + res) + ;;we want to keep data unchanged so react doesn't change component when we leave screen + @memo-home-items))) + +(re-frame/reg-sub + :hide-home-tooltip? + :<- [:multiaccount] + (fn [multiaccount] + (:hide-home-tooltip? multiaccount))) diff --git a/src/status_im/ui/screens/keycard/subs.cljs b/src/status_im/subs/keycard.cljs similarity index 98% rename from src/status_im/ui/screens/keycard/subs.cljs rename to src/status_im/subs/keycard.cljs index 8194231653..058922dde2 100644 --- a/src/status_im/ui/screens/keycard/subs.cljs +++ b/src/status_im/subs/keycard.cljs @@ -1,4 +1,4 @@ -(ns status-im.ui.screens.keycard.subs +(ns status-im.subs.keycard (:require [re-frame.core :as re-frame] [status-im.keycard.common :as common] [status-im.utils.datetime :as utils.datetime] @@ -204,4 +204,4 @@ :keycard/frozen-card? (fn [db] (and (common/keycard-multiaccount? db) - (zero? (get-in db [:keycard :application-info :pin-retry-counter]))))) + (zero? (get-in db [:keycard :application-info :pin-retry-counter]))))) \ No newline at end of file diff --git a/src/status_im/subs/mailservers.cljs b/src/status_im/subs/mailservers.cljs new file mode 100644 index 0000000000..80b53a7549 --- /dev/null +++ b/src/status_im/subs/mailservers.cljs @@ -0,0 +1,88 @@ +(ns status-im.subs.mailservers + (:require [re-frame.core :as re-frame] + [status-im.mailserver.core :as mailserver] + [status-im.fleet.core :as fleet])) + +(re-frame/reg-sub + :mailserver/current-name + :<- [:mailserver/current-id] + :<- [:fleets/current-fleet] + :<- [:mailserver/mailservers] + (fn [[current-mailserver-id current-fleet mailservers]] + (get-in mailservers [current-fleet current-mailserver-id :name]))) + +(re-frame/reg-sub + :mailserver/connecting? + :<- [:mailserver/state] + (fn [state] + (#{:connecting :added} state))) + +(re-frame/reg-sub + :mailserver/connection-error? + :<- [:mailserver/state] + (fn [state] + (#{:error :disconnected} state))) + +(re-frame/reg-sub + :chats/fetching-gap-in-progress? + :<- [:mailserver/fetching-gaps-in-progress] + (fn [gaps [_ ids _]] + (seq (select-keys gaps ids)))) + +(re-frame/reg-sub + :mailserver/fetching? + :<- [:mailserver/state] + :<- [:mailserver/pending-requests] + :<- [:mailserver/connecting?] + :<- [:mailserver/connection-error?] + :<- [:mailserver/request-error?] + (fn [[state pending-requests connecting? connection-error? request-error?]] + (and pending-requests + (= state :connected) + (pos-int? pending-requests) + (not (or connecting? connection-error? request-error?))))) + +(re-frame/reg-sub + :mailserver/fleet-mailservers + :<- [:fleets/current-fleet] + :<- [:mailserver/mailservers] + (fn [[current-fleet mailservers]] + (current-fleet mailservers))) + +(re-frame/reg-sub + :mailserver.edit/connected? + :<- [:mailserver.edit/mailserver] + :<- [:mailserver/current-id] + (fn [[mailserver current-mailserver-id]] + (= (get-in mailserver [:id :value]) + current-mailserver-id))) + +(re-frame/reg-sub + :mailserver/use-status-nodes? + (fn [db _] + (boolean (mailserver/fetch-use-mailservers? {:db db})))) + +(re-frame/reg-sub + :mailserver.edit/validation-errors + :<- [:mailserver.edit/mailserver] + (fn [mailserver] + (set (keep + (fn [[k {:keys [error]}]] + (when error k)) + mailserver)))) + +(re-frame/reg-sub + :mailserver/connected? + :<- [:mailserver/state] + :<- [:disconnected?] + (fn [[mail-state disconnected?]] + (let [mailserver-connected? (= :connected mail-state)] + (and mailserver-connected? + (not disconnected?))))) + +(re-frame/reg-sub + :mailserver/preferred-id + :<- [:multiaccount] + (fn [multiaccount] + (get-in multiaccount + [:pinned-mailservers (fleet/current-fleet-sub multiaccount)]))) \ No newline at end of file diff --git a/src/status_im/subs/multiaccount.cljs b/src/status_im/subs/multiaccount.cljs new file mode 100644 index 0000000000..0c3ee54f55 --- /dev/null +++ b/src/status_im/subs/multiaccount.cljs @@ -0,0 +1,249 @@ +(ns status-im.subs.multiaccount + (:require [re-frame.core :as re-frame] + [status-im.multiaccounts.core :as multiaccounts] + [status-im.ethereum.core :as ethereum] + [status-im.utils.security :as security] + [cljs.spec.alpha :as spec] + [status-im.fleet.core :as fleet] + [clojure.string :as string] + [status-im.utils.image-server :as image-server] + [clojure.set :as clojure.set] + [status-im.multiaccounts.db :as multiaccounts.db])) + +(re-frame/reg-sub + :multiaccount/public-key + :<- [:multiaccount] + (fn [{:keys [public-key]}] + public-key)) + +(re-frame/reg-sub + :multiaccount/contact + :<- [:multiaccount] + (fn [current-account] + (some-> + current-account + (select-keys [:name :preferred-name :public-key :identicon :image :images]) + (clojure.set/rename-keys {:name :alias}) + (multiaccounts/contact-with-names)))) + +(re-frame/reg-sub + :multiaccount/preferred-name + :<- [:multiaccount] + (fn [{:keys [preferred-name]}] + preferred-name)) + +(re-frame/reg-sub + :multiaccount/default-account + :<- [:multiaccount/accounts] + (fn [accounts] + (ethereum/get-default-account accounts))) + +(re-frame/reg-sub + :multiaccount/visible-accounts + :<- [:multiaccount/accounts] + (fn [accounts] + (remove :hidden accounts))) + +(re-frame/reg-sub + :sign-in-enabled? + :<- [:multiaccounts/login] + (fn [{:keys [password]}] + (spec/valid? ::multiaccounts.db/password + (security/safe-unmask-data password)))) + +(re-frame/reg-sub + :fleets/current-fleet + :<- [:multiaccount] + (fn [multiaccount] + (fleet/current-fleet-sub multiaccount))) + +(re-frame/reg-sub + :opensea-enabled? + :<- [:multiaccount] + (fn [{:keys [opensea-enabled?]}] + (boolean opensea-enabled?))) + +(re-frame/reg-sub + :log-level/current-log-level + :<- [:multiaccount] + (fn [multiaccount] + (get multiaccount :log-level))) + +(re-frame/reg-sub + :waku/bloom-filter-mode + :<- [:multiaccount] + (fn [multiaccount] + (boolean (get multiaccount :waku-bloom-filter-mode)))) + +(re-frame/reg-sub + :waku/v2-flag + :<- [:fleets/current-fleet] + (fn [fleet] + (string/starts-with? (name fleet) "wakuv2"))) + +(re-frame/reg-sub + :dapps-address + :<- [:multiaccount] + (fn [acc] + (get acc :dapps-address))) + +(re-frame/reg-sub + :dapps-account + :<- [:multiaccount/accounts] + :<- [:dapps-address] + (fn [[accounts address]] + (some #(when (= (:address %) address) %) accounts))) + +(re-frame/reg-sub + :multiaccount/current-account + :<- [:multiaccount/accounts] + :<- [:get-screen-params :wallet-account] + (fn [[accounts acc]] + (some #(when (= (string/lower-case (:address %)) + (string/lower-case (:address acc))) %) accounts))) + +(re-frame/reg-sub + :account-by-address + :<- [:multiaccount/accounts] + (fn [accounts [_ address]] + (when (string? address) + (some #(when (= (string/lower-case (:address %)) + (string/lower-case address)) %) accounts)))) + +(re-frame/reg-sub + :multiple-multiaccounts? + :<- [:multiaccounts/multiaccounts] + (fn [multiaccounts] + (> (count multiaccounts) 1))) + +;; NOTE: this subscription only works on login +(re-frame/reg-sub + :multiaccounts.login/keycard-account? + :<- [:multiaccounts/multiaccounts] + :<- [:multiaccounts/login] + (fn [[multiaccounts {:keys [key-uid]}]] + (get-in multiaccounts [key-uid :keycard-pairing]))) + +(re-frame/reg-sub + :multiaccounts/keycard-account? + :<- [:multiaccount] + (fn [multiaccount] + (:keycard-pairing multiaccount))) + +(re-frame/reg-sub + :accounts-without-watch-only + :<- [:multiaccount/accounts] + (fn [accounts] + (filter #(not= (:type %) :watch) accounts))) + +(re-frame/reg-sub + :visible-accounts-without-watch-only + :<- [:multiaccount/accounts] + (fn [accounts] + (remove :hidden (filter #(not= (:type %) :watch) accounts)))) + +(defn filter-recipient-accounts + [search-filter {:keys [name]}] + (string/includes? (string/lower-case (str name)) search-filter)) + +(re-frame/reg-sub + :accounts-for-recipient + :<- [:multiaccount/visible-accounts] + :<- [:wallet/prepare-transaction] + :<- [:search/recipient-filter] + (fn [[accounts {:keys [from]} search-filter]] + (let [accounts (remove #(= (:address %) (:address from)) accounts)] + (if (string/blank? search-filter) + accounts + (filter (partial filter-recipient-accounts + (string/lower-case search-filter)) + accounts))))) + +(re-frame/reg-sub + :add-account-disabled? + :<- [:multiaccount/accounts] + :<- [:add-account] + (fn [[accounts {:keys [address type account seed private-key]}]] + (or (string/blank? (:name account)) + (case type + :generate + false + :watch + (or (not (ethereum/address? address)) + (some #(when (= (:address %) address) %) accounts)) + :key + (string/blank? (security/safe-unmask-data private-key)) + :seed + (string/blank? (security/safe-unmask-data seed)) + false)))) + +(re-frame/reg-sub + :multiaccount/current-user-visibility-status + :<- [:multiaccount] + (fn [{:keys [current-user-visibility-status]}] + current-user-visibility-status)) + +(re-frame/reg-sub + :multiaccount/reset-password-form-vals-and-errors + :<- [:multiaccount/reset-password-form-vals] + :<- [:multiaccount/reset-password-errors] + :<- [:multiaccount/resetting-password?] + (fn [[form-vals errors resetting?]] + (let [{:keys [current-password new-password confirm-new-password]} form-vals] + {:form-vals form-vals + :errors errors + :resetting? resetting? + :next-enabled? + (and (pos? (count current-password)) + (pos? (count new-password)) + (pos? (count confirm-new-password)) + (>= (count new-password) 6) + (>= (count current-password) 6) + (= new-password confirm-new-password))}))) + +(re-frame/reg-sub + :profile/has-picture + :<- [:multiaccount] + (fn [multiaccount] + (pos? (count (get multiaccount :images))))) + +(defn- replace-multiaccount-image-uri [multiaccount port] + (let [public-key (:public-key multiaccount) + identicon (image-server/get-identicons-uri port public-key) + multiaccount (assoc multiaccount :identicon identicon) + images (:images multiaccount) + images (reduce (fn [acc current] + (let [key-uid (:keyUid current) + image-name (:type current) + uri (image-server/get-account-image-uri port public-key image-name key-uid)] + (conj acc (assoc current :uri uri)))) + [] + images)] + (assoc multiaccount :images images))) + +(re-frame/reg-sub + :profile/multiaccount + :<- [:multiaccount] + :<- [:mediaserver/port] + (fn [[multiaccount port]] + (replace-multiaccount-image-uri multiaccount port))) + +;; LINK PREVIEW ======================================================================================================== + +(re-frame/reg-sub + :link-preview/cache + :<- [:multiaccount] + (fn [multiaccount] + (get multiaccount :link-previews-cache))) + +(re-frame/reg-sub + :link-preview/enabled-sites + :<- [:multiaccount] + (fn [multiaccount] + (get multiaccount :link-previews-enabled-sites))) + +(re-frame/reg-sub + :link-preview/link-preview-request-enabled + :<- [:multiaccount] + (fn [multiaccount] + (get multiaccount :link-preview-request-enabled))) \ No newline at end of file diff --git a/src/status_im/subs/networks.cljs b/src/status_im/subs/networks.cljs new file mode 100644 index 0000000000..534c3eefd4 --- /dev/null +++ b/src/status_im/subs/networks.cljs @@ -0,0 +1,35 @@ +(ns status-im.subs.networks + (:require [status-im.ethereum.core :as ethereum] + [re-frame.core :as re-frame] + [status-im.utils.config :as config])) + +(defn- filter-networks [network-type] + (fn [network] + (let [chain-id (ethereum/network->chain-id network) + testnet? (ethereum/testnet? chain-id) + custom? (:custom? network)] + (case network-type + :custom custom? + :mainnet (and (not custom?) (not testnet?)) + :testnet (and (not custom?) testnet?))))) + +(defn- label-networks [default-networks] + (fn [network] + (let [custom? (not (default-networks (:id network)))] + (assoc network :custom? custom?)))) + +(re-frame/reg-sub + :get-networks + :<- [:networks/networks] + (fn [networks] + (let [networks (map (label-networks (into #{} (map :id config/default-networks))) (sort-by :name (vals networks))) + types [:mainnet :testnet :custom]] + (zipmap + types + (map #(filter (filter-networks %) networks) types))))) + +(re-frame/reg-sub + :manage-network-valid? + :<- [:networks/manage] + (fn [manage] + (not-any? :error (vals manage)))) \ No newline at end of file diff --git a/src/status_im/subs/onboarding.cljs b/src/status_im/subs/onboarding.cljs new file mode 100644 index 0000000000..b66a845fea --- /dev/null +++ b/src/status_im/subs/onboarding.cljs @@ -0,0 +1,61 @@ +(ns status-im.subs.onboarding + (:require [re-frame.core :as re-frame] + [status-im.constants :as constants] + [status-im.multiaccounts.recover.core :as recover])) + +(re-frame/reg-sub + :intro-wizard + :<- [:intro-wizard-state] + :<- [:dimensions/window] + (fn [[wizard-state {:keys [width height]}]] + (assoc wizard-state :view-height height :view-width width))) + +(re-frame/reg-sub + :intro-wizard/choose-key + :<- [:intro-wizard] + (fn [wizard-state] + (select-keys wizard-state [:multiaccounts :selected-id :view-height]))) + +(re-frame/reg-sub + :intro-wizard/select-key-storage + :<- [:intro-wizard] + (fn [wizard-state] + (select-keys wizard-state [:selected-storage-type :recovering?]))) + +(re-frame/reg-sub + :intro-wizard/enter-phrase + :<- [:intro-wizard] + (fn [wizard-state] + (select-keys wizard-state [:processing? + :passphrase-word-count + :next-button-disabled? + :passphrase-error]))) + +(re-frame/reg-sub + :intro-wizard/recovery-success + :<- [:intro-wizard] + (fn [wizard-state] + {:pubkey (get-in wizard-state [:derived constants/path-whisper-keyword :public-key]) + :name (get-in wizard-state [:derived constants/path-whisper-keyword :name]) + :identicon (get-in wizard-state [:derived constants/path-whisper-keyword :identicon]) + :processing? (:processing? wizard-state)})) + +(re-frame/reg-sub + :intro-wizard/recover-existing-account? + :<- [:intro-wizard] + :<- [:multiaccounts/multiaccounts] + (fn [[intro-wizard multiaccounts]] + (recover/existing-account? (:root-key intro-wizard) multiaccounts))) + +(defn login-ma-keycard-pairing + "Compute the keycard-pairing value of the multiaccount selected for login" + [db _] + (when-let [acc-to-login (-> db :multiaccounts/login)] + (-> db + :multiaccounts/multiaccounts + (get (:key-uid acc-to-login)) + :keycard-pairing))) + +(re-frame/reg-sub + :intro-wizard/acc-to-login-keycard-pairing + login-ma-keycard-pairing) \ No newline at end of file diff --git a/src/status_im/subs/pairing.cljs b/src/status_im/subs/pairing.cljs new file mode 100644 index 0000000000..a72a07f82b --- /dev/null +++ b/src/status_im/subs/pairing.cljs @@ -0,0 +1,22 @@ +(ns status-im.subs.pairing + (:require [re-frame.core :as re-frame] + [status-im.pairing.core :as pairing])) + +(re-frame/reg-sub + :pairing/installations + :<- [:get-pairing-installations] + :<- [:pairing/installation-id] + (fn [[installations installation-id]] + (->> installations + vals + (pairing/sort-installations installation-id)))) + +(re-frame/reg-sub + :pairing/installation-id + :<- [:multiaccount] + (fn [multiaccount] (:installation-id multiaccount))) + +(re-frame/reg-sub + :pairing/installation-name + :<- [:multiaccount] + (fn [multiaccount] (:installation-name multiaccount))) \ No newline at end of file diff --git a/src/status_im/subs/root.cljs b/src/status_im/subs/root.cljs new file mode 100644 index 0000000000..ff4c70edd2 --- /dev/null +++ b/src/status_im/subs/root.cljs @@ -0,0 +1,254 @@ +(ns status-im.subs.root + (:require [re-frame.core :as re-frame] + status-im.subs.activity-center + status-im.subs.bootnodes + status-im.subs.browser + status-im.subs.communities + status-im.subs.contact + status-im.subs.chat.chats + status-im.subs.chat.messages + status-im.subs.ens + status-im.subs.general + status-im.subs.home + status-im.subs.keycard + status-im.subs.mailservers + status-im.subs.multiaccount + status-im.subs.networks + status-im.subs.onboarding + status-im.subs.pairing + status-im.subs.search + status-im.subs.stickers + status-im.subs.wallet.signing + status-im.subs.wallet.transactions + status-im.subs.wallet.wallet)) + +(defn reg-root-key-sub [sub-name db-key] + (re-frame/reg-sub sub-name (fn [db] (get db db-key)))) + +;;view +(reg-root-key-sub :view-id :view-id) +(reg-root-key-sub :screen-params :navigation/screen-params) + +;;bottom sheet +(reg-root-key-sub :bottom-sheet/show? :bottom-sheet/show?) +(reg-root-key-sub :bottom-sheet/view :bottom-sheet/view) +(reg-root-key-sub :bottom-sheet/options :bottom-sheet/options) + +;;general +(reg-root-key-sub :sync-state :sync-state) +(reg-root-key-sub :network-status :network-status) +(reg-root-key-sub :peers-count :peers-count) +(reg-root-key-sub :about-app/node-info :node-info) +(reg-root-key-sub :peers-summary :peers-summary) +(reg-root-key-sub :dimensions/window :dimensions/window) +(reg-root-key-sub :fleets/custom-fleets :custom-fleets) +(reg-root-key-sub :animations :animations) +(reg-root-key-sub :ui/search :ui/search) +(reg-root-key-sub :web3-node-version :web3-node-version) +(reg-root-key-sub :sync-data :sync-data) +(reg-root-key-sub :mobile-network/remember-choice? :mobile-network/remember-choice?) +(reg-root-key-sub :qr-modal :qr-modal) +(reg-root-key-sub :bootnodes/manage :bootnodes/manage) +(reg-root-key-sub :wakuv2-nodes/manage :wakuv2-nodes/manage) +(reg-root-key-sub :wakuv2-nodes/list :wakuv2-nodes/list) +(reg-root-key-sub :networks/current-network :networks/current-network) +(reg-root-key-sub :networks/networks :networks/networks) +(reg-root-key-sub :networks/manage :networks/manage) +(reg-root-key-sub :get-pairing-installations :pairing/installations) +(reg-root-key-sub :tooltips :tooltips) +(reg-root-key-sub :supported-biometric-auth :supported-biometric-auth) +(reg-root-key-sub :app-active-since :app-active-since) +(reg-root-key-sub :connectivity/ui-status-properties :connectivity/ui-status-properties) +(reg-root-key-sub :logged-in-since :logged-in-since) +(reg-root-key-sub :link-previews-whitelist :link-previews-whitelist) +(reg-root-key-sub :app-state :app-state) +(reg-root-key-sub :home-items-show-number :home-items-show-number) +(reg-root-key-sub :waku/v2-peer-stats :peer-stats) +(reg-root-key-sub :visibility-status-updates :visibility-status-updates) +(reg-root-key-sub :navigation2/navigation2-stacks :navigation2/navigation2-stacks) + +;;NOTE this one is not related to ethereum network +;; it is about cellular network/ wifi network +(reg-root-key-sub :network/type :network/type) + +;;profile +(reg-root-key-sub :my-profile/seed :my-profile/seed) +(reg-root-key-sub :my-profile/advanced? :my-profile/advanced?) +(reg-root-key-sub :my-profile/profile :my-profile/profile) + +;;multiaccount +(reg-root-key-sub :multiaccounts/multiaccounts :multiaccounts/multiaccounts) +(reg-root-key-sub :multiaccounts/login :multiaccounts/login) +(reg-root-key-sub :multiaccount :multiaccount) +(reg-root-key-sub :multiaccount/accounts :multiaccount/accounts) +(reg-root-key-sub :get-recover-multiaccount :multiaccounts/recover) +(reg-root-key-sub :multiaccounts/key-storage :multiaccounts/key-storage) +(reg-root-key-sub :multiaccount/reset-password-form-vals :multiaccount/reset-password-form-vals) +(reg-root-key-sub :multiaccount/reset-password-errors :multiaccount/reset-password-errors) +(reg-root-key-sub :multiaccount/resetting-password? :multiaccount/resetting-password?) + +;;chat +(reg-root-key-sub :chats/cooldown-enabled? :chat/cooldown-enabled?) +(reg-root-key-sub :chats/chats :chats) +(reg-root-key-sub :chats/current-chat-id :current-chat-id) +(reg-root-key-sub :public-group-topic :public-group-topic) +(reg-root-key-sub :chats/loading? :chats/loading?) +(reg-root-key-sub :new-chat-name :new-chat-name) +(reg-root-key-sub :group-chat-profile/editing? :group-chat-profile/editing?) +(reg-root-key-sub :group-chat-profile/profile :group-chat-profile/profile) +(reg-root-key-sub :selected-participants :selected-participants) +(reg-root-key-sub :chat/inputs :chat/inputs) +(reg-root-key-sub :chat/memberships :chat/memberships) +(reg-root-key-sub :camera-roll-photos :camera-roll-photos) +(reg-root-key-sub :group-chat/invitations :group-chat/invitations) +(reg-root-key-sub :chats/mention-suggestions :chats/mention-suggestions) +(reg-root-key-sub :chat/inputs-with-mentions :chat/inputs-with-mentions) +(reg-root-key-sub :chats-home-list :chats-home-list) + +;;messages +(reg-root-key-sub :messages/messages :messages) +(reg-root-key-sub :messages/reactions :reactions) +(reg-root-key-sub :messages/message-lists :message-lists) +(reg-root-key-sub :messages/pagination-info :pagination-info) +(reg-root-key-sub :messages/pin-message-lists :pin-message-lists) +(reg-root-key-sub :messages/pin-messages :pin-messages) + +;;browser +(reg-root-key-sub :browsers :browser/browsers) +(reg-root-key-sub :browser/options :browser/options) +(reg-root-key-sub :dapps/permissions :dapps/permissions) +(reg-root-key-sub :bookmarks :bookmarks/bookmarks) + +;;stickers +(reg-root-key-sub :stickers/selected-pack :stickers/selected-pack) +(reg-root-key-sub :stickers/packs :stickers/packs) +(reg-root-key-sub :stickers/recent-stickers :stickers/recent-stickers) + +;;mailserver +(reg-root-key-sub :mailserver/current-id :mailserver/current-id) +(reg-root-key-sub :mailserver/mailservers :mailserver/mailservers) +(reg-root-key-sub :mailserver.edit/mailserver :mailserver.edit/mailserver) +(reg-root-key-sub :mailserver/state :mailserver/state) +(reg-root-key-sub :mailserver/pending-requests :mailserver/pending-requests) +(reg-root-key-sub :mailserver/request-error? :mailserver/request-error) +(reg-root-key-sub :mailserver/fetching-gaps-in-progress :mailserver/fetching-gaps-in-progress) + +;;contacts +(reg-root-key-sub :contacts/contacts-raw :contacts/contacts) +(reg-root-key-sub :contacts/current-contact-identity :contacts/identity) +(reg-root-key-sub :contacts/current-contact-ens-name :contacts/ens-name) +(reg-root-key-sub :contacts/new-identity :contacts/new-identity) +(reg-root-key-sub :group/selected-contacts :group/selected-contacts) +(reg-root-key-sub :contacts/blocked-set :contacts/blocked) + +;;wallet +(reg-root-key-sub :wallet :wallet) +(reg-root-key-sub :prices :prices) +(reg-root-key-sub :prices-loading? :prices-loading?) +(reg-root-key-sub :wallet.transactions :wallet.transactions) +(reg-root-key-sub :wallet/custom-token-screen :wallet/custom-token-screen) +(reg-root-key-sub :wallet/prepare-transaction :wallet/prepare-transaction) +(reg-root-key-sub :wallet-service/manual-setting :wallet-service/manual-setting) +(reg-root-key-sub :wallet/recipient :wallet/recipient) +(reg-root-key-sub :wallet/favourites :wallet/favourites) +(reg-root-key-sub :wallet/refreshing-history? :wallet/refreshing-history?) +(reg-root-key-sub :wallet/fetching-error :wallet/fetching-error) +(reg-root-key-sub :wallet/non-archival-node :wallet/non-archival-node) +(reg-root-key-sub :wallet/current-base-fee :wallet/current-base-fee) +(reg-root-key-sub :wallet/slow-base-fee :wallet/slow-base-fee) +(reg-root-key-sub :wallet/normal-base-fee :wallet/normal-base-fee) +(reg-root-key-sub :wallet/fast-base-fee :wallet/fast-base-fee) +(reg-root-key-sub :wallet/current-priority-fee :wallet/current-priority-fee) +(reg-root-key-sub :wallet/transactions-management-enabled? :wallet/transactions-management-enabled?) +(reg-root-key-sub :wallet/all-tokens :wallet/all-tokens) +(reg-root-key-sub :wallet/collectible-collections :wallet/collectible-collections) +(reg-root-key-sub :wallet/fetching-collection-assets :wallet/fetching-collection-assets) +(reg-root-key-sub :wallet/collectible-assets :wallet/collectible-assets) +(reg-root-key-sub :wallet/selected-collectible :wallet/selected-collectible) +(reg-root-key-sub :wallet/modal-selecting-source-token? :wallet/modal-selecting-source-token?) +(reg-root-key-sub :wallet/swap-from-token :wallet/swap-from-token) +(reg-root-key-sub :wallet/swap-to-token :wallet/swap-to-token) +(reg-root-key-sub :wallet/swap-from-token-amount :wallet/swap-from-token-amount) +(reg-root-key-sub :wallet/swap-to-token-amount :wallet/swap-to-token-amount) +(reg-root-key-sub :wallet/swap-advanced-mode? :wallet/swap-advanced-mode?) + +;;commands +(reg-root-key-sub :commands/select-account :commands/select-account) + +;;ethereum +(reg-root-key-sub :ethereum/current-block :ethereum/current-block) + +;;ens +(reg-root-key-sub :ens/registration :ens/registration) +(reg-root-key-sub :ens/registrations :ens/registrations) +(reg-root-key-sub :ens/names :ens/names) + +;;signing +(reg-root-key-sub :signing/sign :signing/sign) +(reg-root-key-sub :signing/tx :signing/tx) +(reg-root-key-sub :signing/edit-fee :signing/edit-fee) + +;;intro-wizard +(reg-root-key-sub :intro-wizard-state :intro-wizard) + +(reg-root-key-sub :popover/popover :popover/popover) +(reg-root-key-sub :visibility-status-popover/popover :visibility-status-popover/popover) +(reg-root-key-sub :add-account :add-account) + +(reg-root-key-sub :keycard :keycard) + +(reg-root-key-sub :auth-method :auth-method) + +(reg-root-key-sub :multiaccounts/loading :multiaccounts/loading) + +(reg-root-key-sub :tos-accept-next-root :tos-accept-next-root) + +;; keycard +(reg-root-key-sub :keycard/banner-hidden :keycard/banner-hidden) + +;; delete profile +(reg-root-key-sub :delete-profile/error :delete-profile/error) +(reg-root-key-sub :delete-profile/keep-keys-on-keycard? :delete-profile/keep-keys-on-keycard?) + +;; push notifications +(reg-root-key-sub :push-notifications/servers :push-notifications/servers) +(reg-root-key-sub :push-notifications/preferences :push-notifications/preferences) + +(reg-root-key-sub :buy-crypto/on-ramps :buy-crypto/on-ramps) + +;; communities + +(reg-root-key-sub :raw-communities :communities) +(reg-root-key-sub :communities/create :communities/create) +(reg-root-key-sub :communities/create-channel :communities/create-channel) +(reg-root-key-sub :communities/requests-to-join :communities/requests-to-join) +(reg-root-key-sub :communities/community-id-input :communities/community-id-input) +(reg-root-key-sub :communities/enabled? :communities/enabled?) +(reg-root-key-sub :communities/resolve-community-info :communities/resolve-community-info) + +(reg-root-key-sub :activity.center/notifications :activity.center/notifications) +(reg-root-key-sub :activity.center/notifications-count :activity.center/notifications-count) + +(reg-root-key-sub :bug-report/description-error :bug-report/description-error) +(reg-root-key-sub :bug-report/details :bug-report/details) + +(reg-root-key-sub :backup/performing-backup :backup/performing-backup) + +;; wallet connect +(reg-root-key-sub :wallet-connect/proposal-metadata :wallet-connect/proposal-metadata) +(reg-root-key-sub :wallet-connect/enabled? :wallet-connect/enabled?) +(reg-root-key-sub :wallet-connect/session-connected :wallet-connect/session-connected) +(reg-root-key-sub :wallet-connect/showing-app-management-sheet? :wallet-connect/showing-app-management-sheet?) +(reg-root-key-sub :wallet-connect/sessions :wallet-connect/sessions) +(reg-root-key-sub :wallet-connect-legacy/sessions :wallet-connect-legacy/sessions) +(reg-root-key-sub :wallet-connect/session-managed :wallet-connect/session-managed) +(reg-root-key-sub :contact-requests/pending :contact-requests/pending) + +; media-server +(reg-root-key-sub :mediaserver/port :mediaserver/port) + +; Testing + +(reg-root-key-sub :messenger/started? :messenger/started?) + +(reg-root-key-sub :information-box-states :information-box-states) \ No newline at end of file diff --git a/src/status_im/subs/search.cljs b/src/status_im/subs/search.cljs new file mode 100644 index 0000000000..db521756c3 --- /dev/null +++ b/src/status_im/subs/search.cljs @@ -0,0 +1,95 @@ +(ns status-im.subs.search + (:require [clojure.string :as string]) + (:require [clojure.string :as string] + [status-im.utils.gfycat.core :as gfycat] + [re-frame.core :as re-frame] + [status-im.utils.currency :as currency])) + +(defn sort-by-timestamp + [coll] + (when (not-empty coll) + (sort-by #(-> % second :timestamp) > + (into {} coll)))) + +(defn apply-filter + "extract-attributes-fn is a function that take an element from the collection + and returns a vector of attributes which are strings + apply-filter returns the elements for which at least one attribute includes + the search-filter + apply-filter returns nil if there is no element that match the filter + apply-filter returns full collection if the search-filter is empty" + [search-filter coll extract-attributes-fn sort?] + (let [results (if (not-empty search-filter) + (let [search-filter (string/lower-case search-filter)] + (filter (fn [element] + (some (fn [v] + (let [s (cond (string? v) v + (keyword? v) (name v))] + (when (string? s) + (string/includes? (string/lower-case s) + search-filter)))) + (extract-attributes-fn element))) + coll)) + coll)] + (if sort? + (sort-by-timestamp results) + results))) + +(defn filter-chat + [contacts search-filter {:keys [group-chat alias name chat-id]}] + (let [alias (if-not group-chat + (string/lower-case (or alias + (get-in contacts [chat-id :alias]) + (gfycat/generate-gfy chat-id))) + "") + nickname (get-in contacts [chat-id :nickname])] + (or + (string/includes? (string/lower-case (str name)) search-filter) + (string/includes? (string/lower-case alias) search-filter) + (when nickname + (string/includes? (string/lower-case nickname) search-filter)) + (and + (get-in contacts [chat-id :ens-verified]) + (string/includes? (string/lower-case + (str (get-in contacts [chat-id :name]))) + search-filter))))) + +(re-frame/reg-sub + :search/filtered-chats + :<- [:chats/home-list-chats] + :<- [:contacts/contacts] + :<- [:search/home-filter] + (fn [[chats contacts search-filter]] + ;; Short-circuit if search-filter is empty + (let [filtered-chats (if (seq search-filter) + (filter + (partial filter-chat + contacts + (string/lower-case search-filter)) + chats) + chats)] + (sort-by :timestamp > filtered-chats)))) + +(defn extract-currency-attributes [currency] + (let [{:keys [code display-name]} (val currency)] + [code display-name])) + +(re-frame/reg-sub + :search/filtered-currencies + :<- [:search/currency-filter] + (fn [search-currency-filter] + {:search-filter search-currency-filter + :currencies (apply-filter search-currency-filter currency/currencies extract-currency-attributes false)})) + +(defn extract-token-attributes [token] + (let [{:keys [symbol name]} token] + [symbol name])) + +(re-frame/reg-sub + :wallet/filtered-grouped-chain-tokens + :<- [:wallet/grouped-chain-tokens] + :<- [:search/token-filter] + (fn [[{custom-tokens true default-tokens nil} search-token-filter]] + {:search-filter search-token-filter + :tokens {true (apply-filter search-token-filter custom-tokens extract-token-attributes false) + nil (apply-filter search-token-filter default-tokens extract-token-attributes false)}})) \ No newline at end of file diff --git a/src/status_im/subs/stickers.cljs b/src/status_im/subs/stickers.cljs new file mode 100644 index 0000000000..c48365d8d7 --- /dev/null +++ b/src/status_im/subs/stickers.cljs @@ -0,0 +1,27 @@ +(ns status-im.subs.stickers + (:require [re-frame.core :as re-frame] + [status-im.constants :as constants])) + +(re-frame/reg-sub + :stickers/installed-packs + :<- [:stickers/packs] + (fn [packs] + (filter #(= (:status %) constants/sticker-pack-status-installed) (vals packs)))) + +(re-frame/reg-sub + :stickers/all-packs + :<- [:stickers/packs] + (fn [packs] + (map (fn [{:keys [status] :as pack}] + (-> pack + (assoc :installed (= status constants/sticker-pack-status-installed)) + (assoc :pending (= status constants/sticker-pack-status-pending)) + (assoc :owned (= status constants/sticker-pack-status-owned)))) + (vals packs)))) + +(re-frame/reg-sub + :stickers/get-current-pack + :<- [:get-screen-params] + :<- [:stickers/all-packs] + (fn [[{:keys [id]} packs]] + (first (filter #(= (:id %) id) packs)))) \ No newline at end of file diff --git a/src/status_im/subs_test.cljs b/src/status_im/subs/subs_test.cljs similarity index 57% rename from src/status_im/subs_test.cljs rename to src/status_im/subs/subs_test.cljs index e0e2242413..dcca8cb9c5 100644 --- a/src/status_im/subs_test.cljs +++ b/src/status_im/subs/subs_test.cljs @@ -1,6 +1,9 @@ -(ns status-im.subs-test +(ns status-im.subs.subs-test (:require [cljs.test :refer [deftest is testing]] - status-im.subs)) + [status-im.subs.wallet.transactions :as wallet.transactions] + [status-im.subs.onboarding :as onboarding] + [status-im.utils.money :as money] + [status-im.subs.wallet.wallet :as wallet])) (def transactions [{:timestamp "1505912551000"} {:timestamp "1505764322000"} @@ -18,12 +21,12 @@ (deftest group-transactions-by-date (testing "Check if transactions are sorted by date" - (is (= (#'status-im.subs/group-transactions-by-date transactions) + (is (= (wallet.transactions/group-transactions-by-date transactions) grouped-transactions)))) (deftest login-ma-keycard-pairing (testing "returns nil when no :multiaccounts/login" - (let [res (status-im.subs/login-ma-keycard-pairing + (let [res (onboarding/login-ma-keycard-pairing {:multiaccounts/login nil :multiaccounts/multiaccounts {"0x1" {:keycard-pairing "keycard-pairing-code"}}} @@ -31,9 +34,23 @@ (is (nil? res)))) (testing "returns :keycard-pairing when :multiaccounts/login is present" - (let [res (status-im.subs/login-ma-keycard-pairing + (let [res (onboarding/login-ma-keycard-pairing {:multiaccounts/login {:key-uid "0x1"} :multiaccounts/multiaccounts {"0x1" {:keycard-pairing "keycard-pairing-code"}}} {})] (is (= res "keycard-pairing-code"))))) + +(deftest test-balance-total-value + (is (= (wallet/get-balance-total-value + {:ETH (money/bignumber 1000000000000000000) + :SNT (money/bignumber 100000000000000000000) + :AST (money/bignumber 10000)} + {:ETH {:USD {:from "ETH", :to "USD", :price 677.91, :last-day 658.688}} + :SNT {:USD {:from "SNT", :to "USD", :price 0.1562, :last-day 0.15}} + :AST {:USD {:from "AST", :to "USD", :price 4, :last-day 3}}} + :USD + {:ETH 18 + :SNT 18 + :AST 4}) + 697.53))) \ No newline at end of file diff --git a/src/status_im/subs/wallet/signing.cljs b/src/status_im/subs/wallet/signing.cljs new file mode 100644 index 0000000000..92e8d90ac3 --- /dev/null +++ b/src/status_im/subs/wallet/signing.cljs @@ -0,0 +1,220 @@ +(ns status-im.subs.wallet.signing + (:require [re-frame.core :as re-frame] + [status-im.ethereum.core :as ethereum] + [status-im.utils.money :as money] + [status-im.i18n.i18n :as i18n] + [status-im.ethereum.tokens :as tokens] + [clojure.string :as string] + [status-im.wallet.db :as wallet.db] + [status-im.signing.gas :as signing.gas])) + +(re-frame/reg-sub + ::send-transaction + :<- [:wallet] + (fn [wallet] + (:send-transaction wallet))) + +(re-frame/reg-sub + :wallet.send/symbol + :<- [::send-transaction] + (fn [send-transaction] + (:symbol send-transaction))) + +(re-frame/reg-sub + :wallet.send/camera-flashlight + :<- [::send-transaction] + (fn [send-transaction] + (:camera-flashlight send-transaction))) + +(re-frame/reg-sub + :wallet/settings + :<- [:wallet] + (fn [{:keys [settings]}] + (reduce-kv #(conj %1 %3) [] settings))) + +(re-frame/reg-sub + :wallet.request/transaction + :<- [:wallet] + :request-transaction) + +(re-frame/reg-sub + :wallet/binance-chain? + :<- [:current-network] + (fn [network] + (ethereum/binance-chain-id? (get-in network [:config :NetworkId])))) + +(re-frame/reg-sub + :signing/fee + :<- [:signing/tx] + (fn [{:keys [gas gasPrice maxFeePerGas]}] + (signing.gas/calculate-max-fee gas (or maxFeePerGas gasPrice)))) + +(re-frame/reg-sub + :signing/currencies + :<- [:prices] + :<- [:wallet/currency] + :<- [:ethereum/native-currency] + (fn [[prices {:keys [code]} {:keys [symbol]}]] + [(name symbol) + code + (get-in prices [symbol (keyword code) :price])])) + +(re-frame/reg-sub + :signing/priority-fee-suggestions-range + :<- [:wallet/current-priority-fee] + :<- [:wallet/slow-base-fee] + :<- [:wallet/normal-base-fee] + :<- [:wallet/fast-base-fee] + (fn [[latest-tip slow normal fast]] + (reduce + (fn [acc [k fees]] + (assoc acc k (reduce + (fn [acc [k fee]] + (assoc acc k (-> fee + money/wei->gwei + (money/to-fixed 2)))) + {} + fees))) + {} + (signing.gas/get-fee-options latest-tip slow normal fast)))) + +(re-frame/reg-sub + :signing/phrase + :<- [:multiaccount] + (fn [{:keys [signing-phrase]}] + signing-phrase)) + +(re-frame/reg-sub + :signing/sign-message + :<- [:signing/sign] + :<- [:multiaccount/accounts] + :<- [:prices] + (fn [[sign wallet-accounts prices]] + (if (= :pinless (:type sign)) + (let [message (get-in sign [:formatted-data :message]) + wallet-acc (some #(when (= (:address %) (:receiver message)) %) wallet-accounts)] + (cond-> sign + (and (:amount message) (:currency message)) + (assoc :fiat-amount + (money/fiat-amount-value (:amount message) + (:currency message) + :USD prices) + :fiat-currency "USD") + (and (:receiver message) wallet-acc) + (assoc :account wallet-acc))) + sign))) + +(defn- too-precise-amount? + "Checks if number has any extra digit beyond the allowed number of decimals. + It does so by checking the number against its rounded value." + [amount decimals] + (let [^js bn (money/bignumber amount)] + (not (.eq bn (.round bn decimals))))) + +(defn get-amount-error [amount decimals] + (when (and (seq amount) decimals) + (let [normalized-amount (money/normalize amount) + value (money/bignumber normalized-amount)] + (cond + (not (money/valid? value)) + {:amount-error (i18n/label :t/validation-amount-invalid-number)} + + (too-precise-amount? normalized-amount decimals) + {:amount-error (i18n/label :t/validation-amount-is-too-precise {:decimals decimals})} + + :else nil)))) + +(defn get-sufficient-funds-error + [balance symbol amount] + (when-not (money/sufficient-funds? amount (get balance symbol)) + {:amount-error (i18n/label :t/wallet-insufficient-funds)})) + +(defn gas-required-exceeds-allowance? [gas-error-message] + (and gas-error-message (string/starts-with? + gas-error-message + "gas required exceeds allowance"))) + +(defn get-sufficient-gas-error + [gas-error-message balance symbol amount ^js gas ^js gasPrice] + (if (and gas gasPrice) + (let [^js fee (.times gas gasPrice) + ^js available-ether (money/bignumber (get balance :ETH 0)) + ^js available-for-gas (if (= :ETH symbol) + (.minus available-ether (money/bignumber amount)) + available-ether)] + (merge {:gas-error-state (when gas-error-message :gas-is-set)} + (when-not (money/sufficient-funds? fee (money/bignumber available-for-gas)) + {:gas-error (i18n/label :t/wallet-insufficient-gas)}))) + (let [insufficient-balance? (gas-required-exceeds-allowance? gas-error-message)] + {:gas-error-state (when gas-error-message :gas-isnt-set) + :insufficient-balalce? insufficient-balance? + :gas-error (if insufficient-balance? + (i18n/label :t/insufficient-balance-to-cover-fee) + (or gas-error-message + (i18n/label :t/invalid-number)))}))) + +(re-frame/reg-sub + :signing/amount-errors + (fn [[_ address] _] + [(re-frame/subscribe [:signing/tx]) + (re-frame/subscribe [:balance address])]) + (fn [[{:keys [amount token gas gasPrice maxFeePerGas approve? gas-error-message]} balance]] + (let [gas-price (or maxFeePerGas gasPrice)] + (if (and amount token (not approve?)) + (let [amount-bn (money/formatted->internal (money/bignumber amount) (:symbol token) (:decimals token)) + amount-error (or (get-amount-error amount (:decimals token)) + (get-sufficient-funds-error balance (:symbol token) amount-bn))] + (merge amount-error (get-sufficient-gas-error gas-error-message balance (:symbol token) amount-bn gas gas-price))) + (get-sufficient-gas-error gas-error-message balance nil nil gas gas-price))))) + +(re-frame/reg-sub + :wallet.send/prepare-transaction-with-balance + :<- [:wallet/prepare-transaction] + :<- [:wallet] + :<- [:offline?] + :<- [:wallet/all-tokens] + :<- [:current-network] + (fn [[{:keys [symbol from to amount-text] :as transaction} + wallet offline? all-tokens current-network]] + (let [balance (get-in wallet [:accounts (:address from) :balance]) + {:keys [decimals] :as token} (tokens/asset-for all-tokens current-network symbol) + {:keys [value error]} (wallet.db/parse-amount amount-text decimals) + amount (money/formatted->internal value symbol decimals) + {:keys [amount-error] :as transaction-new} + (merge transaction + {:amount-error error} + (when amount + (get-sufficient-funds-error balance symbol amount)))] + (assoc transaction-new + :amount amount + :balance balance + :token (assoc token :amount (get balance (:symbol token))) + :sign-enabled? (and to + (nil? amount-error) + (not (nil? amount)) + (not offline?)))))) + +(re-frame/reg-sub + :wallet.request/prepare-transaction-with-balance + :<- [:wallet/prepare-transaction] + :<- [:wallet] + :<- [:offline?] + :<- [:wallet/all-tokens] + :<- [:current-network] + (fn [[{:keys [symbol from to amount-text] :as transaction} + wallet offline? all-tokens current-network]] + (let [balance (get-in wallet [:accounts (:address from) :balance]) + {:keys [decimals] :as token} (tokens/asset-for all-tokens current-network symbol) + {:keys [value error]} (wallet.db/parse-amount amount-text decimals) + amount (money/formatted->internal value symbol decimals) + {:keys [amount-error] :as transaction-new} + (assoc transaction :amount-error error)] + (assoc transaction-new + :amount amount + :balance balance + :token (assoc token :amount (get balance (:symbol token))) + :sign-enabled? (and to + from + (nil? amount-error) + (not (nil? amount)) + (not offline?)))))) \ No newline at end of file diff --git a/src/status_im/subs/wallet/transactions.cljs b/src/status_im/subs/wallet/transactions.cljs new file mode 100644 index 0000000000..8a639873d6 --- /dev/null +++ b/src/status_im/subs/wallet/transactions.cljs @@ -0,0 +1,238 @@ +(ns status-im.subs.wallet.transactions + (:require [re-frame.core :as re-frame] + [status-im.utils.money :as money] + [status-im.i18n.i18n :as i18n] + [status-im.ethereum.transactions.core :as transactions] + [status-im.wallet.db :as wallet.db] + [status-im.utils.datetime :as datetime] + [status-im.wallet.utils :as wallet.utils] + [status-im.notifications.core :as notifications])) + +(re-frame/reg-sub + :wallet/accounts + :<- [:wallet] + (fn [wallet] + (get wallet :accounts))) + +(re-frame/reg-sub + :wallet/account-by-transaction-hash + :<- [:wallet/accounts] + (fn [accounts [_ hash]] + (some (fn [[address account]] + (when-let [transaction (get-in account [:transactions hash])] + (assoc transaction :address address))) + accounts))) + +(re-frame/reg-sub + :wallet/transactions + :<- [:wallet] + (fn [wallet [_ address]] + (get-in wallet [:accounts address :transactions]))) + +(re-frame/reg-sub + :wallet/filters + :<- [:wallet] + (fn [wallet] + (get wallet :filters))) + +(defn enrich-transaction + [{:keys [type to from value token] :as transaction} + contacts native-currency] + (let [[contact-address key-contact key-wallet] + (if (= type :inbound) + [from :from-contact :to-wallet] + [to :to-contact :from-wallet]) + wallet (i18n/label :main-wallet) + contact (get contacts contact-address) + {:keys [symbol-display symbol decimals] :as asset} + (or token native-currency) + amount-text (if value + (wallet.utils/format-amount value decimals) + "...") + currency-text (when asset + (clojure.core/name (or symbol-display symbol)))] + (cond-> transaction + contact (assoc key-contact (:name contact)) + :always (assoc key-wallet wallet + :amount-text amount-text + :currency-text currency-text)))) + +(re-frame/reg-sub + :wallet.transactions/transactions + (fn [[_ address] _] + [(re-frame/subscribe [:wallet/transactions address]) + (re-frame/subscribe [:contacts/contacts-by-address]) + (re-frame/subscribe [:ethereum/native-currency])]) + (fn [[transactions contacts native-currency]] + (reduce (fn [acc [hash transaction]] + (assoc acc + hash + (enrich-transaction transaction contacts native-currency))) ;;TODO this doesn't look good for performance, we need to calculate this only once for each transaction + {} + transactions))) + +(re-frame/reg-sub + :wallet.transactions/all-filters? + :<- [:wallet/filters] + (fn [filters] + (= wallet.db/default-wallet-filters + filters))) + +(def filters-labels + {:inbound (i18n/label :t/incoming) + :outbound (i18n/label :t/outgoing) + :pending (i18n/label :t/pending) + :failed (i18n/label :t/failed)}) + +(re-frame/reg-sub + :wallet.transactions/filters + :<- [:wallet/filters] + (fn [filters] + (map (fn [id] + (let [checked? (filters id)] + {:id id + :label (filters-labels id) + :checked? checked? + :on-touch #(if checked? + (re-frame/dispatch [:wallet.transactions/remove-filter id]) + (re-frame/dispatch [:wallet.transactions/add-filter id]))})) + wallet.db/default-wallet-filters))) + +(re-frame/reg-sub + :wallet.transactions.filters/screen + :<- [:wallet.transactions/filters] + :<- [:wallet.transactions/all-filters?] + (fn [[filters all-filters?]] + {:all-filters? all-filters? + :filters filters + :on-touch-select-all (when-not all-filters? + #(re-frame/dispatch + [:wallet.transactions/add-all-filters]))})) + +(defn- enrich-transaction-for-list + [filters + {:keys [type from-contact from to-contact to hash timestamp] :as transaction} + address] + (when (filters type) + (assoc (case type + :inbound + (assoc transaction + :label (i18n/label :t/from) + :contact-accessibility-label :sender-text + :address-accessibility-label :sender-address-text + :contact from-contact + :address from) + (assoc transaction + :label (i18n/label :t/to) + :contact-accessibility-label :recipient-name-text + :address-accessibility-label :recipient-address-text + :contact to-contact + :address to)) + :time-formatted (datetime/timestamp->time timestamp) + :on-touch-fn #(re-frame/dispatch [:wallet.ui/show-transaction-details hash address])))) + +(defn group-transactions-by-date + [transactions] + (->> transactions + (group-by #(datetime/timestamp->date-key (:timestamp %))) + (sort-by key >) + (map (fn [[date-key transactions]] + {:title (datetime/timestamp->mini-date (:timestamp (first transactions))) + :key date-key + :data (sort-by :timestamp > transactions)})))) + +(re-frame/reg-sub + :wallet.transactions.history/screen + (fn [[_ address] _] + [(re-frame/subscribe [:wallet.transactions/transactions address]) + (re-frame/subscribe [:wallet/filters]) + (re-frame/subscribe [:wallet.transactions/all-filters?])]) + (fn [[transactions filters all-filters?] [_ address]] + {:all-filters? all-filters? + :total (count transactions) + :transaction-history-sections + (->> transactions + vals + (keep #(enrich-transaction-for-list filters % address)) + (group-transactions-by-date))})) + +(re-frame/reg-sub + :wallet/recipient-recent-txs + (fn [[_ address] _] + [(re-frame/subscribe [:wallet.transactions/transactions address])]) + (fn [[transactions] _] + (->> transactions + vals + (sort-by :timestamp >) + (remove #(= (:type %) :pending)) + (take 3)))) + +(re-frame/reg-sub + :wallet.transactions.details/current-transaction + (fn [[_ _ address] _] + [(re-frame/subscribe [:wallet.transactions/transactions address]) + (re-frame/subscribe [:ethereum/native-currency]) + (re-frame/subscribe [:chain-id])]) + (fn [[transactions native-currency chain-id] [_ hash _]] + (let [{:keys [gas-used gas-price fee-cap tip-cap hash timestamp type] + :as transaction} + (get transactions hash) + native-currency-text (name (or (:symbol-display native-currency) + (:symbol native-currency)))] + (when transaction + (merge transaction + {:gas-price-eth (if gas-price + (money/wei->str :eth + gas-price + native-currency-text) + "-") + :gas-price-gwei (if gas-price + (money/wei->str :gwei + gas-price) + "-") + :fee-cap-gwei (if fee-cap + (money/wei->str :gwei + fee-cap) + "-") + :tip-cap-gwei (if tip-cap + (money/wei->str :gwei + tip-cap) + "-") + :date (datetime/timestamp->long-date timestamp)} + (if (= type :unsigned) + {:block (i18n/label :not-applicable) + :cost (i18n/label :not-applicable) + :gas-limit (i18n/label :not-applicable) + :gas-used (i18n/label :not-applicable) + :nonce (i18n/label :not-applicable) + :hash (i18n/label :not-applicable)} + {:cost (when gas-used + (money/wei->str :eth + (money/fee-value gas-used gas-price) + native-currency-text)) + :url (transactions/get-transaction-details-url + chain-id + hash)})))))) + +(re-frame/reg-sub + :wallet.transactions.details/screen + (fn [[_ hash address] _] + [(re-frame/subscribe [:wallet.transactions.details/current-transaction hash address]) + (re-frame/subscribe [:ethereum/current-block])]) + (fn [[transaction current-block]] + (let [confirmations (wallet.db/get-confirmations transaction + current-block)] + (assoc transaction + :confirmations confirmations + :confirmations-progress + (if (>= confirmations transactions/confirmations-count-threshold) + 100 + (* 100 (/ confirmations transactions/confirmations-count-threshold))))))) + +(re-frame/reg-sub + :notifications/wallet-transactions + :<- [:push-notifications/preferences] + (fn [pref] + (first (filter #(notifications/preference= % {:service "wallet" + :event "transaction" + :identifier "all"}) pref)))) \ No newline at end of file diff --git a/src/status_im/subs/wallet/wallet.cljs b/src/status_im/subs/wallet/wallet.cljs new file mode 100644 index 0000000000..a17df09304 --- /dev/null +++ b/src/status_im/subs/wallet/wallet.cljs @@ -0,0 +1,283 @@ +(ns status-im.subs.wallet.wallet + (:require [re-frame.core :as re-frame] + [status-im.ethereum.core :as ethereum] + [status-im.utils.money :as money] + [status-im.i18n.i18n :as i18n] + [status-im.ethereum.tokens :as tokens] + [status-im.utils.config :as config] + [status-im.utils.currency :as currency] + [clojure.string :as string])) + +(re-frame/reg-sub + :balance + :<- [:wallet] + (fn [wallet [_ address]] + (get-in wallet [:accounts address :balance]))) + +(re-frame/reg-sub + :balance-default + :<- [:wallet] + :<- [:multiaccount/accounts] + (fn [[wallet accounts]] + (get-in wallet [:accounts (:address (ethereum/get-default-account accounts)) :balance]))) + +(re-frame/reg-sub + :balances + :<- [:wallet] + :<- [:multiaccount/visible-accounts] + (fn [[wallet accounts]] + (let [accounts (map :address accounts)] + (map :balance (vals (select-keys (:accounts wallet) accounts)))))) + +(re-frame/reg-sub + :empty-balances? + :<- [:balances] + (fn [balances] + (every? + (fn [balance] + (every? + (fn [^js asset] + (or (nil? asset) (.isZero asset))) + (vals balance))) + balances))) + +(re-frame/reg-sub + :price + :<- [:prices] + (fn [prices [_ fsym tsym]] + (get-in prices [fsym tsym :price]))) + +(re-frame/reg-sub + :last-day + :<- [:prices] + (fn [prices [_ fsym tsym]] + (get-in prices [fsym tsym :last-day]))) + +(re-frame/reg-sub + :wallet.settings/currency + :<- [:multiaccount] + (fn [settings] + (or (get settings :currency) :usd))) + +(defn get-balance-total-value + [balance prices currency token->decimals] + (reduce-kv (fn [acc symbol value] + (if-let [price (get-in prices [symbol currency :price])] + (+ acc (or (some-> (money/internal->formatted value symbol (token->decimals symbol)) + ^js (money/crypto->fiat price) + .toNumber) + 0)) + acc)) 0 balance)) + +(re-frame/reg-sub + :wallet/token->decimals + :<- [:wallet/all-tokens] + (fn [all-tokens] + (into {} (map #(vector (:symbol %) (:decimals %)) (vals all-tokens))))) + +(re-frame/reg-sub + :portfolio-value + :<- [:balances] + :<- [:prices] + :<- [:wallet/currency] + :<- [:wallet/token->decimals] + (fn [[balances prices currency token->decimals]] + (if (and balances prices) + (let [currency-key (-> currency :code keyword) + balance-total-value (apply + (map #(get-balance-total-value % prices currency-key token->decimals) balances))] + (if (pos? balance-total-value) + (-> balance-total-value + (money/with-precision 2) + str + (i18n/format-currency (:code currency))) + "0")) + "..."))) + +(re-frame/reg-sub + :account-portfolio-value + (fn [[_ address] _] + [(re-frame/subscribe [:balance address]) + (re-frame/subscribe [:prices]) + (re-frame/subscribe [:wallet/currency]) + (re-frame/subscribe [:wallet/token->decimals])]) + (fn [[balance prices currency token->decimals]] + (if (and balance prices) + (let [currency-key (-> currency :code keyword) + balance-total-value (get-balance-total-value balance prices currency-key token->decimals)] + (if (pos? balance-total-value) + (-> balance-total-value + (money/with-precision 2) + str + (i18n/format-currency (:code currency))) + "0")) + "..."))) + +(re-frame/reg-sub + :wallet/sorted-tokens + :<- [:wallet/all-tokens] + (fn [all-tokens] + (tokens/sorted-tokens-for all-tokens))) + +(re-frame/reg-sub + :wallet/grouped-chain-tokens + :<- [:wallet/sorted-tokens] + :<- [:wallet/visible-tokens-symbols] + (fn [[all-tokens visible-tokens]] + (let [vt-set (set visible-tokens)] + (group-by :custom? (map #(assoc % :checked? (boolean (get vt-set (keyword (:symbol %))))) all-tokens))))) + +(re-frame/reg-sub + :wallet/fetching-tx-history? + :<- [:wallet] + (fn [wallet [_ address]] + (get-in wallet [:fetching address :history?]))) + +(re-frame/reg-sub + :wallet/fetching-recent-tx-history? + :<- [:wallet] + (fn [wallet [_ address]] + (get-in wallet [:fetching address :recent?]))) + +(re-frame/reg-sub + :wallet/tx-history-fetched? + :<- [:wallet] + (fn [wallet [_ address]] + (get-in wallet [:fetching address :all-fetched?]))) + +(re-frame/reg-sub + :wallet/chain-explorer-link + (fn [db [_ address]] + (let [network (:networks/current-network db) + link (get-in config/default-networks-by-id + [network :chain-explorer-link])] + (when link + (str link address))))) + +(re-frame/reg-sub + :wallet/error-message + :<- [:wallet] + (fn [wallet] + (or (get-in wallet [:errors :balance-update]) + (get-in wallet [:errors :prices-update])))) + +(re-frame/reg-sub + :wallet/visible-tokens-symbols + :<- [:ethereum/chain-keyword] + :<- [:multiaccount] + (fn [[chain current-multiaccount]] + (get-in current-multiaccount [:wallet/visible-tokens chain]))) + +(re-frame/reg-sub + :wallet/visible-assets + :<- [:current-network] + :<- [:wallet/visible-tokens-symbols] + :<- [:wallet/sorted-tokens] + (fn [[network visible-tokens-symbols all-tokens-sorted]] + (conj (filter #(contains? visible-tokens-symbols (:symbol %)) all-tokens-sorted) + (tokens/native-currency network)))) + +(re-frame/reg-sub + :wallet/visible-assets-with-amount + (fn [[_ address] _] + [(re-frame/subscribe [:balance address]) + (re-frame/subscribe [:wallet/visible-assets])]) + (fn [[balance visible-assets]] + (map #(assoc % :amount (get balance (:symbol %))) visible-assets))) + +(defn update-value [prices currency] + (fn [{:keys [symbol decimals amount] :as token}] + (let [price (get-in prices [symbol (-> currency :code keyword) :price])] + (assoc token + :price price + :value (when (and amount price) + (-> (money/internal->formatted amount symbol decimals) + (money/crypto->fiat price) + (money/with-precision 2) + str + (i18n/format-currency (:code currency)))))))) + +(re-frame/reg-sub + :wallet/visible-assets-with-values + (fn [[_ address] _] + [(re-frame/subscribe [:wallet/visible-assets-with-amount address]) + (re-frame/subscribe [:prices]) + (re-frame/subscribe [:wallet/currency])]) + (fn [[assets prices currency]] + (let [{:keys [tokens nfts]} (group-by #(if (:nft? %) :nfts :tokens) assets) + tokens-with-values (map (update-value prices currency) tokens)] + {:tokens tokens-with-values + :nfts nfts}))) + +(defn get-asset-amount [balances sym] + (reduce #(if-let [^js bl (get %2 sym)] + (.plus ^js (or ^js %1 ^js (money/bignumber 0)) bl) + %1) + nil + balances)) + +(re-frame/reg-sub + :wallet/all-visible-assets-with-amount + :<- [:balances] + :<- [:wallet/visible-assets] + (fn [[balances visible-assets]] + (map #(assoc % :amount (get-asset-amount balances (:symbol %))) visible-assets))) + +(re-frame/reg-sub + :wallet/all-visible-assets-with-values + :<- [:wallet/all-visible-assets-with-amount] + :<- [:prices] + :<- [:wallet/currency] + (fn [[assets prices currency]] + (let [{:keys [tokens nfts]} (group-by #(if (:nft? %) :nfts :tokens) assets) + tokens-with-values (map (update-value prices currency) tokens)] + {:tokens tokens-with-values + :nfts nfts}))) + +(re-frame/reg-sub + :wallet/transferrable-assets-with-amount + (fn [[_ address]] + (re-frame/subscribe [:wallet/visible-assets-with-amount address])) + (fn [all-assets] + (filter #(not (:nft? %)) all-assets))) + +(re-frame/reg-sub + :wallet/currency + :<- [:wallet.settings/currency] + (fn [currency-id] + (get currency/currencies currency-id))) + +(defn filter-recipient-favs + [search-filter {:keys [name]}] + (string/includes? (string/lower-case (str name)) search-filter)) + +(re-frame/reg-sub + :wallet/favourites-filtered + :<- [:wallet/favourites] + :<- [:search/recipient-filter] + (fn [[favs search-filter]] + (let [favs (vals favs)] + (if (string/blank? search-filter) + favs + (filter (partial filter-recipient-favs + (string/lower-case search-filter)) + favs))))) + +(re-frame/reg-sub + :wallet/collectible-collection + :<- [:wallet/collectible-collections] + (fn [all-collections [_ address]] + (when address + (let [all-collections (get all-collections (string/lower-case address) [])] + (sort-by :name all-collections))))) + +(re-frame/reg-sub + :wallet/collectible-assets-by-collection-and-address + :<- [:wallet/collectible-assets] + (fn [all-assets [_ address collectible-slug]] + (get-in all-assets [address collectible-slug] []))) + +(re-frame/reg-sub + :wallet/fetching-assets-by-collectible-slug + :<- [:wallet/fetching-collection-assets] + (fn [fetching-collection-assets [_ collectible-slug]] + (get fetching-collection-assets collectible-slug false))) \ No newline at end of file diff --git a/src/status_im/ui/screens/chat/components/input.cljs b/src/status_im/ui/screens/chat/components/input.cljs index 310b761a9f..05f35a0d3d 100644 --- a/src/status_im/ui/screens/chat/components/input.cljs +++ b/src/status_im/ui/screens/chat/components/input.cljs @@ -255,7 +255,7 @@ (re-frame/dispatch [::mentions/calculate-suggestions mentionable-users])))) (defn text-input-old [{:keys [set-active-panel refs chat-id sending-image]}] - (let [cooldown-enabled? @(re-frame/subscribe [:chats/cooldown-enabled?]) + (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) @@ -294,7 +294,7 @@ (get @input-texts chat-id))])) (defn text-input [{:keys [set-active-panel refs chat-id sending-image on-content-size-change]}] - (let [cooldown-enabled? @(re-frame/subscribe [:chats/cooldown-enabled?]) + (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) diff --git a/src/status_im/ui/screens/rpc_usage_info.cljs b/src/status_im/ui/screens/rpc_usage_info.cljs index 2c556a6bc8..6a28167820 100644 --- a/src/status_im/ui/screens/rpc_usage_info.cljs +++ b/src/status_im/ui/screens/rpc_usage_info.cljs @@ -35,7 +35,7 @@ (re-frame/reg-fx ::get-stats (fn [] - (status-im.ethereum.json-rpc/call + (json-rpc/call {:method "rpcstats_getStats" :params [] :on-success #(re-frame/dispatch [::handle-stats %])}))) @@ -43,7 +43,7 @@ (re-frame/reg-fx ::reset (fn [] - (status-im.ethereum.json-rpc/call + (json-rpc/call {:method "rpcstats_reset" :params [] :on-success #(log/debug "rpcstats_reset success")}))) diff --git a/src/status_im/wallet/core.cljs b/src/status_im/wallet/core.cljs index 36c4c4998e..78187309f3 100644 --- a/src/status_im/wallet/core.cljs +++ b/src/status_im/wallet/core.cljs @@ -989,7 +989,7 @@ {::async-storage/set! {:transactions-management-enabled? enabled?} :db (assoc db :wallet/transactions-management-enabled? enabled?)}) -(fx/defn update-curent-block +(fx/defn update-current-block {:events [::update-current-block]} [{:keys [db]} block] {:db (assoc db :ethereum/current-block block)}) diff --git a/src/status_im/wallet/subs_test.cljs b/src/status_im/wallet/subs_test.cljs deleted file mode 100644 index 1a1cc6f1c2..0000000000 --- a/src/status_im/wallet/subs_test.cljs +++ /dev/null @@ -1,18 +0,0 @@ -(ns status-im.wallet.subs-test - (:require [cljs.test :refer-macros [deftest is]] - [status-im.utils.money :as money] - [status-im.subs :as s])) - -(deftest test-balance-total-value - (is (= (#'status-im.subs/get-balance-total-value - {:ETH (money/bignumber 1000000000000000000) - :SNT (money/bignumber 100000000000000000000) - :AST (money/bignumber 10000)} - {:ETH {:USD {:from "ETH", :to "USD", :price 677.91, :last-day 658.688}} - :SNT {:USD {:from "SNT", :to "USD", :price 0.1562, :last-day 0.15}} - :AST {:USD {:from "AST", :to "USD", :price 4, :last-day 3}}} - :USD - {:ETH 18 - :SNT 18 - :AST 4}) - 697.53)))