From 799bd1d4dcca10979ef25c7648ebf936d0d667a6 Mon Sep 17 00:00:00 2001 From: Ulises Manuel <90291778+ulisesmac@users.noreply.github.com> Date: Wed, 12 Jun 2024 14:37:06 -0600 Subject: [PATCH] fix(wallet): Multiple activity tab fixes (#20367) * Add missing circle in wallet-activity component * Move activity calculations to subscription and fix wrong data * Fetch activities when visiting an account --- .../wallet/wallet_activity/style.cljs | 28 +++++-- .../wallet/wallet_activity/view.cljs | 19 ++--- .../contexts/wallet/account/view.cljs | 11 +-- .../wallet/common/activity_tab/events.cljs | 44 ++++++++++ .../wallet/common/activity_tab/view.cljs | 74 +++++++--------- src/status_im/contexts/wallet/events.cljs | 46 +--------- src/status_im/contexts/wallet/signals.cljs | 7 +- src/status_im/subs/wallet/activities.cljs | 84 +++++++++++++++---- .../subs/wallet/activities_test.cljs | 72 +++++++++++++--- src/status_im/subs/wallet/send.cljs | 16 ++-- src/status_im/subs/wallet/send_test.cljs | 30 +++++-- 11 files changed, 277 insertions(+), 154 deletions(-) create mode 100644 src/status_im/contexts/wallet/common/activity_tab/events.cljs diff --git a/src/quo/components/wallet/wallet_activity/style.cljs b/src/quo/components/wallet/wallet_activity/style.cljs index 688f16f87f..76d3c89e20 100644 --- a/src/quo/components/wallet/wallet_activity/style.cljs +++ b/src/quo/components/wallet/wallet_activity/style.cljs @@ -59,26 +59,36 @@ :height 32 :margin-top 8}) -(def content-container - {:margin-left 8}) +(def container + {:flex-direction :row + :column-gap 8}) (def content-line {:flex-direction :row :margin-top 2 :align-items :center}) -(defn icon-hole-view - [theme blur?] +(def icon-hole-view {:width 32 :height 32 - :border-width 1 - :border-color (if-not blur? - (colors/theme-colors colors/neutral-20 colors/neutral-80 theme) - colors/white-opa-5) - :border-radius 16 :align-items :center :justify-content :center}) +(defn icon-circle-border + [theme blur?] + {:position :absolute + :top 0 + :left 0 + :right 0 + :bottom 0 + :width 32 + :height 32 + :border-width 1 + :border-radius 16 + :border-color (if-not blur? + (colors/theme-colors colors/neutral-20 colors/neutral-80 theme) + colors/white-opa-5)}) + (defn icon-color [theme] (colors/theme-colors colors/neutral-100 colors/white theme)) diff --git a/src/quo/components/wallet/wallet_activity/view.cljs b/src/quo/components/wallet/wallet_activity/view.cljs index aa675dd9fa..6c1aa26477 100644 --- a/src/quo/components/wallet/wallet_activity/view.cljs +++ b/src/quo/components/wallet/wallet_activity/view.cljs @@ -70,20 +70,21 @@ status :pending}} theme] [rn/view {:style style/icon-container} + [rn/view {:style style/icon-status-container} + [icon/icon (status-icon status) + {:size 12 + :no-color :true}]] [hole-view/hole-view - {:style (style/icon-hole-view theme blur?) + {:style style/icon-hole-view :holes [{:x 20 :y 20 :right 0 :width 12 :height 12 :borderRadius 6}]} - [icon/icon (transaction-icon transaction) - {:color (style/icon-color theme)}]] - [rn/view {:style style/icon-status-container} - [icon/icon (status-icon status) - {:size 12 - :no-color :true}]]]) + [icon/icon (transaction-icon transaction :i/placeholder) + {:color (style/icon-color theme)}] + [rn/view {:style (style/icon-circle-border theme blur?)}]]]) (defn prop-text [label theme] @@ -116,11 +117,9 @@ :on-press on-press :on-press-in on-press-in :on-press-out on-press-out} - [rn/view - {:style {:flex-direction :row}} + [rn/view {:style style/container} [transaction-icon-view props theme] [rn/view - {:style style/content-container} [transaction-header props theme] [rn/view {:style style/content-line} (when first-tag [prop-tag first-tag blur?]) diff --git a/src/status_im/contexts/wallet/account/view.cljs b/src/status_im/contexts/wallet/account/view.cljs index f76fb0ad31..09a4ab9cfe 100644 --- a/src/status_im/contexts/wallet/account/view.cljs +++ b/src/status_im/contexts/wallet/account/view.cljs @@ -24,11 +24,12 @@ (defn view [] - (let [selected-tab (or (rf/sub [:wallet/account-tab]) first-tab-id) - {:keys [name color formatted-balance - watch-only?]} (rf/sub [:wallet/current-viewing-account]) - customization-color (rf/sub [:profile/customization-color])] - (rn/use-unmount #(rf/dispatch [:wallet/close-account-page])) + (let [selected-tab (or (rf/sub [:wallet/account-tab]) first-tab-id) + {:keys [name color formatted-balance watch-only? + address]} (rf/sub [:wallet/current-viewing-account]) + customization-color (rf/sub [:profile/customization-color])] + (rn/use-mount + #(rf/dispatch [:wallet/fetch-activities-for-current-account address])) [rn/view {:style {:flex 1}} [account-switcher/view {:type :wallet-networks diff --git a/src/status_im/contexts/wallet/common/activity_tab/events.cljs b/src/status_im/contexts/wallet/common/activity_tab/events.cljs new file mode 100644 index 0000000000..0ee358e4b6 --- /dev/null +++ b/src/status_im/contexts/wallet/common/activity_tab/events.cljs @@ -0,0 +1,44 @@ +(ns status-im.contexts.wallet.common.activity-tab.events + (:require [camel-snake-kebab.extras :as cske] + [utils.ethereum.chain :as chain] + [utils.re-frame :as rf] + [utils.transforms :as transforms])) + +(rf/reg-event-fx + :wallet/fetch-activities-for-current-account + (fn [{:keys [db]}] + (let [address (-> db :wallet :current-viewing-account-address) + chain-ids (chain/chain-ids db) + request-id 0 + filters {:period {:startTimestamp 0 + :endTimestamp 0} + :types [] + :statuses [] + :counterpartyAddresses [] + :assets [] + :collectibles [] + :filterOutAssets false + :filterOutCollectibles false} + offset 0 + limit 35 + request-params [request-id [address] chain-ids filters offset limit]] + {:fx [[:json-rpc/call + [{;; This method is deprecated and will be replaced by + ;; "wallet_startActivityFilterSession" + ;; https://github.com/status-im/status-mobile/issues/19864 + :method "wallet_filterActivityAsync" + :params request-params + :on-error [:wallet/log-rpc-error + {:event :wallet/fetch-activities-for-current-account + :params request-params}]}]]]}))) + +(rf/reg-event-fx + :wallet/activity-filtering-for-current-account-done + (fn [{:keys [db]} [{:keys [message]}]] + (let [address (-> db :wallet :current-viewing-account-address) + activities (->> message + (transforms/json->clj) + (:activities) + (cske/transform-keys transforms/->kebab-case-keyword)) + sorted-activities (sort :timestamp activities)] + {:db (assoc-in db [:wallet :activities address] sorted-activities)}))) diff --git a/src/status_im/contexts/wallet/common/activity_tab/view.cljs b/src/status_im/contexts/wallet/common/activity_tab/view.cljs index 8a29a37e64..b10f9d0b52 100644 --- a/src/status_im/contexts/wallet/common/activity_tab/view.cljs +++ b/src/status_im/contexts/wallet/common/activity_tab/view.cljs @@ -1,61 +1,42 @@ (ns status-im.contexts.wallet.common.activity-tab.view (:require - [legacy.status-im.utils.hex :as utils.hex] - [native-module.core :as native-module] [quo.core :as quo] - [quo.foundations.resources :as quo.resources] [quo.theme] [react-native.core :as rn] [status-im.common.resources :as resources] [status-im.contexts.shell.jump-to.constants :as jump-to.constants] - [status-im.contexts.wallet.common.activity-tab.constants :as constants] [status-im.contexts.wallet.common.empty-tab.view :as empty-tab] - [utils.datetime :as datetime] - [utils.ethereum.chain :as chain] [utils.i18n :as i18n] - [utils.money :as money] [utils.re-frame :as rf])) -(def precision 6) +(defn send-and-receive-activity + [{:keys [transaction relative-date status sender recipient token amount network-name + network-logo]}] + [quo/wallet-activity + {:transaction transaction + :timestamp relative-date + :status status + :counter 1 + :first-tag {:size 24 + :type :token + :token token + :amount amount} + :second-tag-prefix :t/from + :second-tag {:type :address :address sender} + :third-tag-prefix :t/to + :third-tag {:type :address :address recipient} + :fourth-tag-prefix :t/via + :fourth-tag {:size 24 + :type :network + :network-name network-name + :network-logo network-logo} + :blur? false}]) (defn activity-item - [{:keys [activity-type activity-status timestamp symbol-out symbol-in token-in token-out amount-in - amount-out sender recipient]}] - (let [chain-id (or (:chain-id token-in) (:chain-id token-out)) - amount-in-units (native-module/hex-to-number - (utils.hex/normalize-hex amount-in)) - amount-in-value (str (money/with-precision - (money/wei->ether amount-in-units) - precision)) - amount-out-units (native-module/hex-to-number - (utils.hex/normalize-hex amount-out)) - amount-out-value (str (money/with-precision - (money/wei->ether amount-out-units) - precision)) - relative-date (datetime/timestamp->relative (* timestamp 1000)) - receiving-activity? (= activity-type constants/wallet-activity-type-receive)] - [quo/wallet-activity - {:transaction (constants/wallet-activity-id->name activity-type) - :timestamp relative-date - :status (constants/wallet-activity-status->name activity-status) - :counter 1 - :first-tag {:size 24 - :type :token - :token (or symbol-out symbol-in) - :amount (if receiving-activity? amount-in-value amount-out-value)} - :second-tag-prefix (constants/second-tag-prefix activity-type) - :second-tag {:type :address - :address (if receiving-activity? recipient sender)} - :third-tag-prefix (constants/third-tag-prefix activity-type) - :third-tag {:type :address - :address (if receiving-activity? sender recipient)} - :fourth-tag-prefix (constants/fourth-tag-prefix activity-type) - :fourth-tag {:size 24 - :type :network - :network-logo (quo.resources/get-network (chain/chain-id->chain-keyword - chain-id)) - :network-name (chain/chain-id->chain-name chain-id)} - :blur? false}])) + [{:keys [transaction] :as activity}] + (case transaction + (:send :receive) [send-and-receive-activity activity] + nil)) (defn view [] @@ -69,7 +50,8 @@ [rn/section-list {:sections activity-list :sticky-section-headers-enabled false - :style {:flex 1} + :style {:flex 1 + :padding-horizontal 8} :content-container-style {:padding-bottom jump-to.constants/floating-shell-button-height} :render-fn activity-item :render-section-header-fn (fn [{:keys [title]}] [quo/divider-date title])}]))) diff --git a/src/status_im/contexts/wallet/events.cljs b/src/status_im/contexts/wallet/events.cljs index 9cb344b5d6..6c692e0764 100644 --- a/src/status_im/contexts/wallet/events.cljs +++ b/src/status_im/contexts/wallet/events.cljs @@ -6,6 +6,7 @@ [status-im.constants :as constants] [status-im.contexts.settings.wallet.effects] [status-im.contexts.settings.wallet.events] + [status-im.contexts.wallet.common.activity-tab.events] [status-im.contexts.wallet.common.utils.external-links :as external-links] [status-im.contexts.wallet.common.utils.networks :as network-utils] [status-im.contexts.wallet.data-store :as data-store] @@ -35,7 +36,7 @@ (fn [{:keys [db]} [address]] {:db (assoc-in db [:wallet :current-viewing-account-address] address) :fx [[:dispatch [:navigate-to :screen/wallet.accounts address]] - [:dispatch [:wallet/fetch-activities]]]})) + [:dispatch [:wallet/fetch-activities-for-current-account]]]})) (rf/reg-event-fx :wallet/navigate-to-account-within-stack (fn [{:keys [db]} [address]] @@ -506,49 +507,6 @@ (rf/reg-event-fx :wallet/update-selected-networks update-selected-networks) -(rf/reg-event-fx - :wallet/fetch-activities - (fn [{:keys [db]}] - (let [addresses (->> (get-in db [:wallet :accounts]) - vals - (map :address)) - chain-ids (chain/chain-ids db) - request-id 0 - filters {:period {:startTimestamp 0 - :endTimestamp 0} - :types [] - :statuses [] - :counterpartyAddresses [] - :assets [] - :collectibles [] - :filterOutAssets false - :filterOutCollectibles false} - offset 0 - limit 20 - request-params [request-id - addresses - chain-ids - filters - offset - limit]] - {:fx [[:json-rpc/call - [{;; This method is deprecated and will be replaced by - ;; "wallet_startActivityFilterSession" - ;; https://github.com/status-im/status-mobile/issues/19864 - :method "wallet_filterActivityAsync" - :params request-params - :on-error [:wallet/log-rpc-error - {:event :wallet/fetch-activities - :params request-params}]}]]]}))) - -(rf/reg-event-fx - :wallet/activity-filtering-done - (fn [{:keys [db]} [{:keys [message]}]] - (let [{:keys [activities]} (transforms/json->clj message) - activities (cske/transform-keys transforms/->kebab-case-keyword activities) - sorted-activities (sort :timestamp activities)] - {:db (assoc-in db [:wallet :activities] sorted-activities)}))) - (rf/reg-event-fx :wallet/get-crypto-on-ramps-success (fn [{:keys [db]} [data]] diff --git a/src/status_im/contexts/wallet/signals.cljs b/src/status_im/contexts/wallet/signals.cljs index 04208e1f90..3b503d80b9 100644 --- a/src/status_im/contexts/wallet/signals.cljs +++ b/src/status_im/contexts/wallet/signals.cljs @@ -37,7 +37,8 @@ "wallet-blockchain-status-changed" {:fx [[:dispatch [:wallet/blockchain-status-changed (transforms/js->clj event-js)]]]} - "wallet-activity-filtering-done" {:fx [[:dispatch - [:wallet/activity-filtering-done - (transforms/js->clj event-js)]]]} + "wallet-activity-filtering-done" {:fx + [[:dispatch + [:wallet/activity-filtering-for-current-account-done + (transforms/js->clj event-js)]]]} (log/debug ::unknown-wallet-event :type event-type))))) diff --git a/src/status_im/subs/wallet/activities.cljs b/src/status_im/subs/wallet/activities.cljs index 7973ee0049..d636ceb88a 100644 --- a/src/status_im/subs/wallet/activities.cljs +++ b/src/status_im/subs/wallet/activities.cljs @@ -1,26 +1,82 @@ (ns status-im.subs.wallet.activities (:require + [legacy.status-im.utils.hex :as utils.hex] + [native-module.core :as native-module] + [quo.foundations.resources :as quo.resources] + [quo.foundations.resources] [re-frame.core :as rf] [status-im.contexts.wallet.common.activity-tab.constants :as constants] - [utils.datetime :as datetime])) + [utils.datetime :as datetime] + [utils.money :as money])) + +(def precision 6) (rf/reg-sub :wallet/all-activities :<- [:wallet] :-> :activities) -(rf/reg-sub :wallet/activities-for-current-viewing-account +(defn- activity-amount + [amount] + (-> amount + (utils.hex/normalize-hex) + (native-module/hex-to-number) + (money/wei->ether) + (money/with-precision precision) + (str))) + +(defn- process-send-activity + [{:keys [symbol-out chain-id-out amount-out]} activity chain-id->network-name] + (let [network-name (chain-id->network-name chain-id-out)] + (assoc activity + :transaction :send + :token symbol-out + :amount (activity-amount amount-out) + :network-name network-name + :network-logo (quo.resources/get-network network-name)))) + +(defn- process-receive-activity + [{:keys [symbol-in amount-in chain-id-in]} activity chain-id->network-name] + (let [network-name (chain-id->network-name chain-id-in)] + (assoc activity + :transaction :receive + :token symbol-in + :amount (activity-amount amount-in) + :network-name network-name + :network-logo (quo.resources/get-network network-name)))) + +(defn- process-activity-by-type + [chain-id->network-name + {:keys [activity-type activity-status timestamp sender recipient] :as data}] + (let [activity {:relative-date (datetime/timestamp->relative (* timestamp 1000)) + :timestamp timestamp + :status (constants/wallet-activity-status->name activity-status) + :sender sender + :recipient recipient}] + (condp = activity-type + constants/wallet-activity-type-send + (process-send-activity data activity chain-id->network-name) + + constants/wallet-activity-type-receive + (process-receive-activity data activity chain-id->network-name) + + nil))) + +(rf/reg-sub + :wallet/activities-for-current-viewing-account :<- [:wallet/all-activities] :<- [:wallet/current-viewing-account-address] - (fn [[activities current-viewing-account-address]] - (->> activities - (filter (fn [{:keys [sender recipient activity-type]}] - (let [receiving-activity? (= activity-type constants/wallet-activity-type-receive) - relevant-address (if receiving-activity? recipient sender)] - (= relevant-address current-viewing-account-address)))) - (distinct) - (group-by (fn [{:keys [timestamp]}] - (datetime/timestamp->relative-short-date (* timestamp 1000)))) - (map (fn [[date activities]] - {:title date :data activities :timestamp (:timestamp (first activities))})) - (sort-by (fn [{:keys [timestamp]}] (- timestamp)))))) + :<- [:wallet/network-details] + (fn [[activities current-viewing-account-address network-details]] + (let [chain-id->network-name (update-vals (group-by :chain-id network-details) + (comp :network-name first))] + (->> current-viewing-account-address + (get activities) + (keep #(process-activity-by-type chain-id->network-name %)) + (group-by (fn [{:keys [timestamp]}] + (datetime/timestamp->relative-short-date (* timestamp 1000)))) + (map (fn [[date activities]] + {:title date + :data activities + :timestamp (:timestamp (first activities))})) + (sort-by (fn [{:keys [timestamp]}] (- timestamp))))))) diff --git a/src/status_im/subs/wallet/activities_test.cljs b/src/status_im/subs/wallet/activities_test.cljs index 8cef0e7355..b949598910 100644 --- a/src/status_im/subs/wallet/activities_test.cljs +++ b/src/status_im/subs/wallet/activities_test.cljs @@ -2,8 +2,9 @@ (:require [cljs.test :refer [is testing]] [re-frame.db :as rf-db] - status-im.subs.root - status-im.subs.wallet.collectibles + [status-im.contexts.wallet.common.activity-tab.constants :as constants] + [status-im.subs.root] + [status-im.subs.wallet.collectibles] [test-helpers.unit :as h] [utils.re-frame :as rf])) @@ -14,7 +15,7 @@ [:wallet :activities] [{:id 1 :name "Transaction1"} {:id 2 :name "Transaction2"}]) - (is (= [{:id 1 :name "Transaction1"} {:id 2 :name "Transaction2"}] (rf/sub [sub-name]))))) + (is (match? [{:id 1 :name "Transaction1"} {:id 2 :name "Transaction2"}] (rf/sub [sub-name]))))) (h/deftest-sub :wallet/activities-for-current-viewing-account [sub-name] @@ -23,11 +24,62 @@ (fn [db] (-> db (assoc-in [:wallet :activities] - [{:sender "acc1" :recipient "acc2" :timestamp 1588291200} - {:sender "acc2" :recipient "acc1" :timestamp 1588377600} - {:sender "acc3" :recipient "acc4" :timestamp 1588464000}]) + {"acc1" [{:activity-type constants/wallet-activity-type-send + :amount-out "0x1" + :sender "acc1" + :recipient "acc2" + :timestamp 1588291200} + {:activity-type constants/wallet-activity-type-receive + :amount-in "0x1" + :sender "acc2" + :recipient "acc1" + :timestamp 1588377600} + {:activity-type constants/wallet-activity-type-send + :amount-out "0x1" + :sender "acc1" + :recipient "acc4" + :timestamp 1588464000}] + "acc3" [{:activity-type constants/wallet-activity-type-receive + :amount-in "0x1" + :sender "acc4" + :recipient "acc3" + :timestamp 1588464000}]}) (assoc-in [:wallet :current-viewing-account-address] "acc1")))) - (is (= [{:title "May 1, 2020" - :data [{:sender "acc1" :recipient "acc2" :timestamp 1588291200}] - :timestamp 1588291200}] - (rf/sub [sub-name]))))) + (is + (match? [{:title "May 3, 2020" + :timestamp 1588464000 + :data [{:relative-date "May 3, 2020" + :amount "0" + :network-logo nil + :recipient "acc4" + :transaction :send + :token nil + :network-name nil + :status nil + :sender "acc1" + :timestamp 1588464000}]} + {:title "May 2, 2020" + :timestamp 1588377600 + :data [{:relative-date "May 2, 2020" + :amount "0" + :network-logo nil + :recipient "acc1" + :transaction :receive + :token nil + :network-name nil + :status nil + :sender "acc2" + :timestamp 1588377600}]} + {:title "May 1, 2020" + :timestamp 1588291200 + :data [{:relative-date "May 1, 2020" + :amount "0" + :network-logo nil + :recipient "acc2" + :transaction :send + :token nil + :network-name nil + :status nil + :sender "acc1" + :timestamp 1588291200}]}] + (rf/sub [sub-name]))))) diff --git a/src/status_im/subs/wallet/send.cljs b/src/status_im/subs/wallet/send.cljs index 38a818076a..31c8eae142 100644 --- a/src/status_im/subs/wallet/send.cljs +++ b/src/status_im/subs/wallet/send.cljs @@ -1,6 +1,7 @@ (ns status-im.subs.wallet.send (:require [re-frame.core :as rf] + [status-im.contexts.wallet.common.activity-tab.constants :as constants] [utils.number])) (rf/reg-sub @@ -40,14 +41,15 @@ (rf/reg-sub :wallet/recent-recipients - :<- [:wallet/activities-for-current-viewing-account] + :<- [:wallet/all-activities] :<- [:wallet/current-viewing-account-address] - (fn [[sections current-viewing-account-address]] - (let [all-transactions (mapcat :data sections) - users-sent-transactions (filter (fn [{:keys [sender]}] - (= sender current-viewing-account-address)) - all-transactions)] - (set (map :recipient users-sent-transactions))))) + (fn [[all-activities current-viewing-account-address]] + (let [address-activity (get all-activities current-viewing-account-address)] + (->> address-activity + (keep (fn [{:keys [activity-type recipient]}] + (when (= constants/wallet-activity-type-send activity-type) + recipient))) + (distinct))))) (rf/reg-sub :wallet/send-token-not-supported-in-receiver-networks? diff --git a/src/status_im/subs/wallet/send_test.cljs b/src/status_im/subs/wallet/send_test.cljs index 328b62159a..0cb307b41b 100644 --- a/src/status_im/subs/wallet/send_test.cljs +++ b/src/status_im/subs/wallet/send_test.cljs @@ -2,8 +2,9 @@ (:require [cljs.test :refer [is testing]] [re-frame.db :as rf-db] - status-im.subs.root - status-im.subs.wallet.send + [status-im.contexts.wallet.common.activity-tab.constants :as constants] + [status-im.subs.root] + [status-im.subs.wallet.send] [test-helpers.unit :as h] [utils.re-frame :as rf])) @@ -61,8 +62,25 @@ (fn [db] (-> db (assoc-in [:wallet :activities] - [{:sender "acc1" :recipient "acc2" :timestamp 1588291200} - {:sender "acc2" :recipient "acc1" :timestamp 1588377600} - {:sender "acc3" :recipient "acc4" :timestamp 1588464000}]) + {"acc1" [{:activity-type constants/wallet-activity-type-send + :amount-out "0x1" + :sender "acc1" + :recipient "acc2" + :timestamp 1588291200} + {:activity-type constants/wallet-activity-type-receive + :amount-in "0x1" + :sender "acc2" + :recipient "acc1" + :timestamp 1588377600} + {:activity-type constants/wallet-activity-type-send + :amount-out "0x1" + :sender "acc1" + :recipient "acc4" + :timestamp 1588464000}] + "acc3" [{:activity-type constants/wallet-activity-type-receive + :amount-in "0x1" + :sender "acc4" + :recipient "acc3" + :timestamp 1588464000}]}) (assoc-in [:wallet :current-viewing-account-address] "acc1")))) - (is (= #{"acc2"} (rf/sub [sub-name]))))) + (is (match? ["acc2" "acc4"] (rf/sub [sub-name])))))