Skip to content

Commit 93e0c1f

Browse files
[#21483] fix: update collectible amounts after sending (#21867)
* [#21483] fix: update collectible amounts after sending * [#21483] test: cover funcs with unit test * [#21483] fix: add condition to update only pending collectible * [#21483] fix: use parse int for conversion * [#21483] fix: round up dispatch delay
1 parent 9c7d215 commit 93e0c1f

File tree

7 files changed

+161
-29
lines changed

7 files changed

+161
-29
lines changed

src/status_im/contexts/wallet/collectible/events.cljs

Lines changed: 64 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@
7070
(let [current-collectible-idx (get-in db [:wallet :accounts account :current-collectible-idx] 0)
7171
collectibles-filter nil
7272
data-type (collectible-data-types :header)
73-
fetch-criteria {:fetch-type (fetch-type :fetch-if-not-cached)
73+
fetch-criteria {:fetch-type (fetch-type :fetch-if-cache-old)
7474
:max-cache-age-seconds max-cache-age-seconds}
7575
chain-ids (chain/chain-ids db)
7676
request-params [request-id
@@ -171,16 +171,33 @@
171171

172172
(rf/reg-event-fx
173173
:wallet/collectible-ownership-update-finished
174-
(fn [{:keys [db]} [{:keys [accounts chainId]}]]
174+
(fn [{:keys [db]} [{:keys [accounts chainId message]}]]
175175
(let [address (first accounts)
176176
pending-chain-ids (get-in db [:wallet :ui :collectibles :updating address])
177177
updated-chain-ids (disj pending-chain-ids chainId)
178-
all-chain-updated? (and (some? pending-chain-ids) (empty? updated-chain-ids))]
178+
all-chain-updated? (and (some? pending-chain-ids) (empty? updated-chain-ids))
179+
collectible-id (data-store/rpc->collectible-id message)]
179180
{:db (cond-> db
180181
(some? pending-chain-ids)
181182
(assoc-in [:wallet :ui :collectibles :updating address] updated-chain-ids))
182183
:fx [(when all-chain-updated?
183-
[:dispatch [:wallet/request-collectibles-for-account address]])]})))
184+
[:dispatch [:wallet/request-collectibles-for-account address]])
185+
(when collectible-id
186+
(let [collectible-unique-id (collectible-utils/get-collectible-unique-id {:id
187+
collectible-id})
188+
pending-collectible? (-> db
189+
(get-in [:wallet :ui :collectibles :pending])
190+
(contains? collectible-unique-id))]
191+
(when pending-collectible?
192+
[:dispatch
193+
[:wallet/update-pending-collectible-details collectible-id
194+
collectible-unique-id]])))]})))
195+
196+
(rf/reg-event-fx
197+
:wallet/update-pending-collectible-details
198+
(fn [{:keys [db]} [collectible-id collectible-unique-id]]
199+
{:db (update-in db [:wallet :ui :collectibles :pending] dissoc collectible-unique-id)
200+
:fx [[:dispatch [:wallet/get-collectibles-by-unique-id-async collectible-id]]]}))
184201

185202
(defn- update-collectibles-in-account
186203
[existing-collectibles updated-collectibles]
@@ -282,28 +299,33 @@
282299
[{{collectible-id :id :as collectible} :collectible
283300
aspect-ratio :aspect-ratio
284301
gradient-color :gradient-color}]]
302+
{:db (assoc-in db
303+
[:wallet :ui :collectible]
304+
{:details collectible
305+
:aspect-ratio aspect-ratio
306+
:gradient-color gradient-color})
307+
:fx [[:dispatch [:wallet/get-collectibles-by-unique-id-async collectible-id]]
308+
;; We delay the navigation because we need re-frame to update the DB on time.
309+
;; By doing it, we skip a blink while visiting the collectible detail page.
310+
[:dispatch-later
311+
{:ms 20
312+
:dispatch [:open-modal :screen/wallet.collectible]}]]}))
313+
314+
(rf/reg-event-fx
315+
:wallet/get-collectibles-by-unique-id-async
316+
(fn [_ [collectible-id]]
285317
(let [request-id 0
286318
collectible-id-converted (cske/transform-keys transforms/->PascalCaseKeyword collectible-id)
287319
data-type (collectible-data-types :details)
288320
request-params [request-id [collectible-id-converted] data-type]]
289-
{:db (assoc-in db
290-
[:wallet :ui :collectible]
291-
{:details collectible
292-
:aspect-ratio aspect-ratio
293-
:gradient-color gradient-color})
294-
:fx [[:json-rpc/call
321+
{:fx [[:json-rpc/call
295322
[{:method "wallet_getCollectiblesByUniqueIDAsync"
296323
:params request-params
297324
:on-error (fn [error]
298325
(log/error "failed to request collectible"
299326
{:event :wallet/navigate-to-collectible-details
300327
:error error
301-
:params request-params}))}]]
302-
;; We delay the navigation because we need re-frame to update the DB on time.
303-
;; By doing it, we skip a blink while visiting the collectible detail page.
304-
[:dispatch-later
305-
{:ms 17
306-
:dispatch [:open-modal :screen/wallet.collectible]}]]})))
328+
:params request-params}))}]]]})))
307329

308330
(defn- keep-not-empty-value
309331
[old-value new-value]
@@ -318,19 +340,43 @@
318340
(transforms/json->clj message))
319341
{[collectible] :collectibles} response
320342
known-collectible-info (get-in db [:wallet :ui :collectible :details])
343+
current-account-address (get-in db [:wallet :current-viewing-account-address])
344+
total-owned (collectible-utils/collectible-balance collectible
345+
current-account-address)
321346
merged-collectible (as-> known-collectible-info c
322347
(merge-with keep-not-empty-value
323348
c
324349
collectible)
325350
(update c
326351
:ownership
327-
collectible-utils/remove-duplicates-in-ownership))]
352+
collectible-utils/remove-duplicates-in-ownership))
353+
updated-collectible (assoc merged-collectible
354+
:total-owned
355+
total-owned)]
328356
(if collectible
329-
{:db (assoc-in db [:wallet :ui :collectible :details] merged-collectible)}
357+
{:db (assoc-in db [:wallet :ui :collectible :details] updated-collectible)
358+
:fx [[:dispatch [:wallet/update-collectibles-data updated-collectible]]]}
330359
(log/error "failed to get collectible details"
331360
{:event :wallet/get-collectible-details-done
332361
:response response})))))
333362

363+
(rf/reg-event-fx
364+
:wallet/update-collectibles-data
365+
(fn [{:keys [db]} [collectible]]
366+
(let [collectibles-by-address (collectible-utils/group-collectibles-by-ownership-address collectible)]
367+
{:db (update-in db
368+
[:wallet :accounts]
369+
#(reduce-kv
370+
(fn [accounts address updated-collectibles]
371+
(if (contains? accounts address)
372+
(update-in accounts
373+
[address :collectibles]
374+
update-collectibles-in-account
375+
[updated-collectibles])
376+
accounts))
377+
%
378+
collectibles-by-address))})))
379+
334380
(rf/reg-event-fx
335381
:wallet/clear-collectible-details
336382
(fn [{:keys [db]}]

src/status_im/contexts/wallet/collectible/utils.cljs

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,21 @@
66
[taoensso.timbre :as log]
77
[utils.number :as utils.number]))
88

9+
(defn- total-collectible-balance
10+
([ownership]
11+
(reduce (fn [total {:keys [balance]}]
12+
(+ total (or (utils.number/parse-int balance) 0)))
13+
0
14+
ownership)))
15+
916
(defn collectible-balance
1017
([{:keys [ownership]} address]
11-
(->> ownership
12-
(some #(when (= address (:address %))
13-
(:balance %)))
14-
utils.number/parse-int)))
18+
(let [balance (if address
19+
(some #(when (= address (:address %))
20+
(:balance %))
21+
ownership)
22+
(total-collectible-balance ownership))]
23+
(utils.number/parse-int balance))))
1524

1625
(def supported-collectible-types
1726
#{"image/jpeg"
@@ -81,6 +90,13 @@
8190
{})
8291
vals))
8392

93+
(defn group-collectibles-by-ownership-address
94+
[collectible]
95+
(->> (:ownership collectible)
96+
(map (fn [{:keys [address]}]
97+
[address collectible]))
98+
(into {})))
99+
84100
(defn sort-collectibles-by-name
85101
[collectibles]
86102
(sort-by (fn [collectible]

src/status_im/contexts/wallet/collectible/utils_test.cljs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,49 @@
4545
:test-networks-enabled? true})
4646
"https://testnets.opensea.io/assets/optimism-sepolia/0xC/0xT"))))
4747

48+
(deftest collectible-balance-test
49+
(testing "Returns balance for a specific address"
50+
(let [collectible {:ownership [{:address "0x123" :balance "10"}
51+
{:address "0x456" :balance "20"}]}]
52+
(is (= 10 (utils/collectible-balance collectible "0x123")))
53+
(is (= 20 (utils/collectible-balance collectible "0x456")))
54+
(is (= 0 (utils/collectible-balance collectible "0x789")))))
55+
56+
(testing "Returns total balance when no address is provided"
57+
(let [collectible {:ownership [{:address "0x123" :balance "10"}
58+
{:address "0x456" :balance "20"}]}]
59+
(is (= 30 (utils/collectible-balance collectible nil)))))
60+
61+
(testing "Returns 0 when ownership is empty"
62+
(let [collectible {:ownership []}]
63+
(is (= 0 (utils/collectible-balance collectible nil)))))
64+
65+
(testing "Handles nil balance values correctly"
66+
(let [collectible {:ownership [{:address "0x123" :balance nil}
67+
{:address "0x456" :balance nil}]}]
68+
(is (= 0 (utils/collectible-balance collectible nil))))))
69+
70+
(deftest group-collectibles-by-ownership-address-test
71+
(testing "Groups collectibles by ownership address correctly"
72+
(let [collectible {:id {:contract-id {:chain-id 10 :address "0xContract"} :token-id "1"}
73+
:ownership [{:address "0x123" :balance 10}
74+
{:address "0x456" :balance 20}]}
75+
expected {"0x123" collectible
76+
"0x456" collectible}]
77+
(is (= expected (utils/group-collectibles-by-ownership-address collectible)))))
78+
79+
(testing "Returns an empty map when there is no ownership data"
80+
(let [collectible {:id {:contract-id {:chain-id 10 :address "0xContract"} :token-id "1"}
81+
:ownership []}]
82+
(is (= {} (utils/group-collectibles-by-ownership-address collectible)))))
83+
84+
(testing "Handles duplicate ownership addresses"
85+
(let [collectible {:id {:contract-id {:chain-id 10 :address "0xContract"} :token-id "1"}
86+
:ownership [{:address "0x123" :balance 10}
87+
{:address "0x123" :balance 5}]}
88+
expected {"0x123" collectible}]
89+
(is (= expected (utils/group-collectibles-by-ownership-address collectible))))))
90+
4891
(deftest sort-collectibles-by-name-test
4992
(testing "Sorts collectibles by name, moving nil or empty names to the end"
5093
(let [collectibles [{:collectible-data {:name "Alpha"}}

src/status_im/contexts/wallet/common/collectibles_tab/view.cljs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,7 @@
77
[status-im.contexts.wallet.collectible.utils :as utils]
88
[status-im.contexts.wallet.common.collectibles-tab.style :as style]
99
[status-im.contexts.wallet.common.empty-tab.view :as empty-tab]
10-
[utils.i18n :as i18n]
11-
[utils.re-frame :as rf]))
10+
[utils.i18n :as i18n]))
1211

1312
(defn- loading-collectible-item
1413
[_ index]
@@ -82,9 +81,9 @@
8281
;; 1. If possible, move `collectibles-data` calculation to a subscription
8382
;; 2. Optimization: do not recalculate all the collectibles, process only the new ones
8483
(let [collectibles-data (map
85-
(fn [{:keys [ownership] :as collectible}]
86-
(let [total-owned (rf/sub [:wallet/total-owned-collectible ownership
87-
current-account-address])]
84+
(fn [collectible]
85+
(let [total-owned (utils/collectible-balance collectible
86+
current-account-address)]
8887
(assoc collectible
8988
:total-owned total-owned
9089
:on-long-press on-collectible-long-press

src/status_im/contexts/wallet/data_store.cljs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,3 +299,17 @@
299299
selected-keypair-uid (get-in db [:wallet :ui :create-account :selected-keypair-uid])
300300
keypair (get keypairs selected-keypair-uid)]
301301
(boolean (seq (:keycards keypair)))))
302+
303+
(defn- transform-collectible
304+
[{:keys [added updated removed]}]
305+
(let [entry (first (remove nil? (concat added updated removed)))]
306+
(when entry
307+
{:contract-id {:chain-id (:chainID (:contractID entry))
308+
:address (:address (:contractID entry))}
309+
:token-id (str (:tokenID entry))})))
310+
311+
(defn rpc->collectible-id
312+
[collectible]
313+
(-> collectible
314+
transforms/json->clj
315+
transform-collectible))

src/status_im/contexts/wallet/data_store_test.cljs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,3 +265,13 @@
265265
(testing "returns false when db does not contain wallet data"
266266
(let [db {}]
267267
(is (false? (sut/selected-keypair-keycard? db))))))
268+
269+
(deftest rpc->collectible-id-test
270+
(testing "Transforms JSON with `updated` key into collectible ID"
271+
(let
272+
[json
273+
"{\"updated\": [{\"contractID\": {\"chainID\": 222, \"address\": \"0x123456\"}, \"tokenID\": 77}]}"
274+
expected {:contract-id {:chain-id 222
275+
:address "0x123456"}
276+
:token-id "77"}]
277+
(is (= expected (sut/rpc->collectible-id json))))))

src/status_im/contexts/wallet/send/events.cljs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -398,8 +398,12 @@
398398
(rf/reg-event-fx
399399
:wallet/build-transaction-for-collectible-route
400400
(fn [{:keys [db]}]
401-
(let [last-request-uuid (get-in db [:wallet :ui :send :last-request-uuid])]
402-
{:db (update-in db [:wallet :ui :send] dissoc :transaction-for-signing)
401+
(let [last-request-uuid (get-in db [:wallet :ui :send :last-request-uuid])
402+
collectible-unique-id (get-in db [:wallet :ui :send :collectible :unique-id])]
403+
{:db (->
404+
db
405+
(update-in [:wallet :ui :send] dissoc :transaction-for-signing)
406+
(assoc-in [:wallet :ui :collectibles :pending collectible-unique-id] true))
403407
:fx [[:dispatch [:wallet/build-transactions-from-route {:request-uuid last-request-uuid}]]]})))
404408

405409
(rf/reg-event-fx

0 commit comments

Comments
 (0)