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
This commit is contained in:
Ulises Manuel 2024-06-12 14:37:06 -06:00 committed by GitHub
parent 256c9f7b10
commit 799bd1d4dc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 277 additions and 154 deletions

View File

@ -59,26 +59,36 @@
:height 32 :height 32
:margin-top 8}) :margin-top 8})
(def content-container (def container
{:margin-left 8}) {:flex-direction :row
:column-gap 8})
(def content-line (def content-line
{:flex-direction :row {:flex-direction :row
:margin-top 2 :margin-top 2
:align-items :center}) :align-items :center})
(defn icon-hole-view (def icon-hole-view
[theme blur?]
{:width 32 {:width 32
:height 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 :align-items :center
:justify-content :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 (defn icon-color
[theme] [theme]
(colors/theme-colors colors/neutral-100 colors/white theme)) (colors/theme-colors colors/neutral-100 colors/white theme))

View File

@ -70,20 +70,21 @@
status :pending}} status :pending}}
theme] theme]
[rn/view {:style style/icon-container} [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 [hole-view/hole-view
{:style (style/icon-hole-view theme blur?) {:style style/icon-hole-view
:holes [{:x 20 :holes [{:x 20
:y 20 :y 20
:right 0 :right 0
:width 12 :width 12
:height 12 :height 12
:borderRadius 6}]} :borderRadius 6}]}
[icon/icon (transaction-icon transaction) [icon/icon (transaction-icon transaction :i/placeholder)
{:color (style/icon-color theme)}]] {:color (style/icon-color theme)}]
[rn/view {:style style/icon-status-container} [rn/view {:style (style/icon-circle-border theme blur?)}]]])
[icon/icon (status-icon status)
{:size 12
:no-color :true}]]])
(defn prop-text (defn prop-text
[label theme] [label theme]
@ -116,11 +117,9 @@
:on-press on-press :on-press on-press
:on-press-in on-press-in :on-press-in on-press-in
:on-press-out on-press-out} :on-press-out on-press-out}
[rn/view [rn/view {:style style/container}
{:style {:flex-direction :row}}
[transaction-icon-view props theme] [transaction-icon-view props theme]
[rn/view [rn/view
{:style style/content-container}
[transaction-header props theme] [transaction-header props theme]
[rn/view {:style style/content-line} [rn/view {:style style/content-line}
(when first-tag [prop-tag first-tag blur?]) (when first-tag [prop-tag first-tag blur?])

View File

@ -24,11 +24,12 @@
(defn view (defn view
[] []
(let [selected-tab (or (rf/sub [:wallet/account-tab]) first-tab-id) (let [selected-tab (or (rf/sub [:wallet/account-tab]) first-tab-id)
{:keys [name color formatted-balance {:keys [name color formatted-balance watch-only?
watch-only?]} (rf/sub [:wallet/current-viewing-account]) address]} (rf/sub [:wallet/current-viewing-account])
customization-color (rf/sub [:profile/customization-color])] customization-color (rf/sub [:profile/customization-color])]
(rn/use-unmount #(rf/dispatch [:wallet/close-account-page])) (rn/use-mount
#(rf/dispatch [:wallet/fetch-activities-for-current-account address]))
[rn/view {:style {:flex 1}} [rn/view {:style {:flex 1}}
[account-switcher/view [account-switcher/view
{:type :wallet-networks {:type :wallet-networks

View File

@ -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)})))

View File

@ -1,61 +1,42 @@
(ns status-im.contexts.wallet.common.activity-tab.view (ns status-im.contexts.wallet.common.activity-tab.view
(:require (:require
[legacy.status-im.utils.hex :as utils.hex]
[native-module.core :as native-module]
[quo.core :as quo] [quo.core :as quo]
[quo.foundations.resources :as quo.resources]
[quo.theme] [quo.theme]
[react-native.core :as rn] [react-native.core :as rn]
[status-im.common.resources :as resources] [status-im.common.resources :as resources]
[status-im.contexts.shell.jump-to.constants :as jump-to.constants] [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] [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.i18n :as i18n]
[utils.money :as money]
[utils.re-frame :as rf])) [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 (defn activity-item
[{:keys [activity-type activity-status timestamp symbol-out symbol-in token-in token-out amount-in [{:keys [transaction] :as activity}]
amount-out sender recipient]}] (case transaction
(let [chain-id (or (:chain-id token-in) (:chain-id token-out)) (:send :receive) [send-and-receive-activity activity]
amount-in-units (native-module/hex-to-number nil))
(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}]))
(defn view (defn view
[] []
@ -69,7 +50,8 @@
[rn/section-list [rn/section-list
{:sections activity-list {:sections activity-list
:sticky-section-headers-enabled false :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} :content-container-style {:padding-bottom jump-to.constants/floating-shell-button-height}
:render-fn activity-item :render-fn activity-item
:render-section-header-fn (fn [{:keys [title]}] [quo/divider-date title])}]))) :render-section-header-fn (fn [{:keys [title]}] [quo/divider-date title])}])))

View File

@ -6,6 +6,7 @@
[status-im.constants :as constants] [status-im.constants :as constants]
[status-im.contexts.settings.wallet.effects] [status-im.contexts.settings.wallet.effects]
[status-im.contexts.settings.wallet.events] [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.external-links :as external-links]
[status-im.contexts.wallet.common.utils.networks :as network-utils] [status-im.contexts.wallet.common.utils.networks :as network-utils]
[status-im.contexts.wallet.data-store :as data-store] [status-im.contexts.wallet.data-store :as data-store]
@ -35,7 +36,7 @@
(fn [{:keys [db]} [address]] (fn [{:keys [db]} [address]]
{:db (assoc-in db [:wallet :current-viewing-account-address] address) {:db (assoc-in db [:wallet :current-viewing-account-address] address)
:fx [[:dispatch [:navigate-to :screen/wallet.accounts 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 (rf/reg-event-fx :wallet/navigate-to-account-within-stack
(fn [{:keys [db]} [address]] (fn [{:keys [db]} [address]]
@ -506,49 +507,6 @@
(rf/reg-event-fx :wallet/update-selected-networks update-selected-networks) (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 (rf/reg-event-fx
:wallet/get-crypto-on-ramps-success :wallet/get-crypto-on-ramps-success
(fn [{:keys [db]} [data]] (fn [{:keys [db]} [data]]

View File

@ -37,7 +37,8 @@
"wallet-blockchain-status-changed" {:fx [[:dispatch "wallet-blockchain-status-changed" {:fx [[:dispatch
[:wallet/blockchain-status-changed [:wallet/blockchain-status-changed
(transforms/js->clj event-js)]]]} (transforms/js->clj event-js)]]]}
"wallet-activity-filtering-done" {:fx [[:dispatch "wallet-activity-filtering-done" {:fx
[:wallet/activity-filtering-done [[:dispatch
(transforms/js->clj event-js)]]]} [:wallet/activity-filtering-for-current-account-done
(transforms/js->clj event-js)]]]}
(log/debug ::unknown-wallet-event :type event-type))))) (log/debug ::unknown-wallet-event :type event-type)))))

View File

@ -1,26 +1,82 @@
(ns status-im.subs.wallet.activities (ns status-im.subs.wallet.activities
(:require (: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] [re-frame.core :as rf]
[status-im.contexts.wallet.common.activity-tab.constants :as constants] [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 (rf/reg-sub
:wallet/all-activities :wallet/all-activities
:<- [:wallet] :<- [:wallet]
:-> :activities) :-> :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/all-activities]
:<- [:wallet/current-viewing-account-address] :<- [:wallet/current-viewing-account-address]
(fn [[activities current-viewing-account-address]] :<- [:wallet/network-details]
(->> activities (fn [[activities current-viewing-account-address network-details]]
(filter (fn [{:keys [sender recipient activity-type]}] (let [chain-id->network-name (update-vals (group-by :chain-id network-details)
(let [receiving-activity? (= activity-type constants/wallet-activity-type-receive) (comp :network-name first))]
relevant-address (if receiving-activity? recipient sender)] (->> current-viewing-account-address
(= relevant-address current-viewing-account-address)))) (get activities)
(distinct) (keep #(process-activity-by-type chain-id->network-name %))
(group-by (fn [{:keys [timestamp]}] (group-by (fn [{:keys [timestamp]}]
(datetime/timestamp->relative-short-date (* timestamp 1000)))) (datetime/timestamp->relative-short-date (* timestamp 1000))))
(map (fn [[date activities]] (map (fn [[date activities]]
{:title date :data activities :timestamp (:timestamp (first activities))})) {:title date
(sort-by (fn [{:keys [timestamp]}] (- timestamp)))))) :data activities
:timestamp (:timestamp (first activities))}))
(sort-by (fn [{:keys [timestamp]}] (- timestamp)))))))

View File

@ -2,8 +2,9 @@
(:require (:require
[cljs.test :refer [is testing]] [cljs.test :refer [is testing]]
[re-frame.db :as rf-db] [re-frame.db :as rf-db]
status-im.subs.root [status-im.contexts.wallet.common.activity-tab.constants :as constants]
status-im.subs.wallet.collectibles [status-im.subs.root]
[status-im.subs.wallet.collectibles]
[test-helpers.unit :as h] [test-helpers.unit :as h]
[utils.re-frame :as rf])) [utils.re-frame :as rf]))
@ -14,7 +15,7 @@
[:wallet :activities] [:wallet :activities]
[{:id 1 :name "Transaction1"} [{:id 1 :name "Transaction1"}
{:id 2 :name "Transaction2"}]) {: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 (h/deftest-sub :wallet/activities-for-current-viewing-account
[sub-name] [sub-name]
@ -23,11 +24,62 @@
(fn [db] (fn [db]
(-> db (-> db
(assoc-in [:wallet :activities] (assoc-in [:wallet :activities]
[{:sender "acc1" :recipient "acc2" :timestamp 1588291200} {"acc1" [{:activity-type constants/wallet-activity-type-send
{:sender "acc2" :recipient "acc1" :timestamp 1588377600} :amount-out "0x1"
{:sender "acc3" :recipient "acc4" :timestamp 1588464000}]) :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")))) (assoc-in [:wallet :current-viewing-account-address] "acc1"))))
(is (= [{:title "May 1, 2020" (is
:data [{:sender "acc1" :recipient "acc2" :timestamp 1588291200}] (match? [{:title "May 3, 2020"
:timestamp 1588291200}] :timestamp 1588464000
(rf/sub [sub-name]))))) :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])))))

View File

@ -1,6 +1,7 @@
(ns status-im.subs.wallet.send (ns status-im.subs.wallet.send
(:require (:require
[re-frame.core :as rf] [re-frame.core :as rf]
[status-im.contexts.wallet.common.activity-tab.constants :as constants]
[utils.number])) [utils.number]))
(rf/reg-sub (rf/reg-sub
@ -40,14 +41,15 @@
(rf/reg-sub (rf/reg-sub
:wallet/recent-recipients :wallet/recent-recipients
:<- [:wallet/activities-for-current-viewing-account] :<- [:wallet/all-activities]
:<- [:wallet/current-viewing-account-address] :<- [:wallet/current-viewing-account-address]
(fn [[sections current-viewing-account-address]] (fn [[all-activities current-viewing-account-address]]
(let [all-transactions (mapcat :data sections) (let [address-activity (get all-activities current-viewing-account-address)]
users-sent-transactions (filter (fn [{:keys [sender]}] (->> address-activity
(= sender current-viewing-account-address)) (keep (fn [{:keys [activity-type recipient]}]
all-transactions)] (when (= constants/wallet-activity-type-send activity-type)
(set (map :recipient users-sent-transactions))))) recipient)))
(distinct)))))
(rf/reg-sub (rf/reg-sub
:wallet/send-token-not-supported-in-receiver-networks? :wallet/send-token-not-supported-in-receiver-networks?

View File

@ -2,8 +2,9 @@
(:require (:require
[cljs.test :refer [is testing]] [cljs.test :refer [is testing]]
[re-frame.db :as rf-db] [re-frame.db :as rf-db]
status-im.subs.root [status-im.contexts.wallet.common.activity-tab.constants :as constants]
status-im.subs.wallet.send [status-im.subs.root]
[status-im.subs.wallet.send]
[test-helpers.unit :as h] [test-helpers.unit :as h]
[utils.re-frame :as rf])) [utils.re-frame :as rf]))
@ -61,8 +62,25 @@
(fn [db] (fn [db]
(-> db (-> db
(assoc-in [:wallet :activities] (assoc-in [:wallet :activities]
[{:sender "acc1" :recipient "acc2" :timestamp 1588291200} {"acc1" [{:activity-type constants/wallet-activity-type-send
{:sender "acc2" :recipient "acc1" :timestamp 1588377600} :amount-out "0x1"
{:sender "acc3" :recipient "acc4" :timestamp 1588464000}]) :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")))) (assoc-in [:wallet :current-viewing-account-address] "acc1"))))
(is (= #{"acc2"} (rf/sub [sub-name]))))) (is (match? ["acc2" "acc4"] (rf/sub [sub-name])))))