fix(wallet): Collectibles not fetched for new and recovered accounts (#20961)

This commit fixes collectibles not fetched for new and recovered accounts until the user re-login.

Signed-off-by: Mohamed Javid <19339952+smohamedjavid@users.noreply.github.com>
This commit is contained in:
Mohamed Javid 2024-08-31 01:18:00 +05:30 committed by GitHub
parent 1663848b7e
commit 97d4edcf30
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 267 additions and 99 deletions

View File

@ -91,7 +91,7 @@
:color-index gradient-color-index}]])
(defn- card-details
[{:keys [community? avatar-image-src collectible-name theme state set-state]}]
[{:keys [community? avatar-image-src collectible-name theme state set-state loading?]}]
(let [loader-opacity (reanimated/use-shared-value 1)
avatar-opacity (reanimated/use-shared-value 0)
[load-time set-load-time] (rn/use-state (datetime/now))
@ -102,7 +102,7 @@
:avatar-opacity avatar-opacity}))
empty-name? (string/blank? collectible-name)]
(rn/use-mount (fn []
(when (string/blank? avatar-image-src)
(when (and (string/blank? avatar-image-src) (not loading?))
(set-avatar-loaded))))
[rn/view {:style style/card-details-container}
[reanimated/view {:style (style/avatar-container avatar-opacity)}
@ -137,7 +137,7 @@
(defn- card-view
[{:keys [avatar-image-src collectible-name community? counter state set-state
gradient-color-index image-src supported-file?]}]
gradient-color-index image-src supported-file? loading?]}]
(let [theme (quo.theme/use-theme)
loader-opacity (reanimated/use-shared-value (if supported-file? 1 0))
image-opacity (reanimated/use-shared-value (if supported-file? 0 1))
@ -189,6 +189,7 @@
:community? community?
:avatar-image-src avatar-image-src
:collectible-name collectible-name
:loading? loading?
:theme theme}]]))
(defn- image-view
@ -276,6 +277,7 @@
[:supported-file? {:optional true} [:maybe boolean?]]
[:native-ID {:optional true} [:maybe [:or string? keyword?]]]
[:community? {:optional true} [:maybe boolean?]]
[:loading? {:optional true} [:maybe boolean?]]
[:counter {:optional true} [:maybe [:or :string :int]]]
[:gradient-color-index {:optional true}
[:maybe [:enum :gradient-1 :gradient-2 :gradient-3 :gradient-4 :gradient-5]]]

View File

@ -25,23 +25,29 @@
[]
(rf/dispatch [:wallet/request-collectibles-for-current-viewing-account]))
(defn view
[{:keys [selected-tab]}]
(let [collectible-list (rf/sub
(defn- collectibles-tab
[]
(let [updating? (rf/sub [:wallet/current-viewing-account-collectibles-updating?])
collectible-list (rf/sub
[:wallet/current-viewing-account-collectibles-in-selected-networks])
current-account-address (rf/sub [:wallet/current-viewing-account-address])]
[rn/view {:style {:flex 1}}
(case selected-tab
:assets [assets/view]
:collectibles [collectibles/view
{:collectibles collectible-list
:current-account-address current-account-address
:on-end-reached on-end-reached
:on-collectible-press on-collectible-press
:on-collectible-long-press on-collectible-long-press}]
:activity [activity/view]
:permissions [empty-tab/view
{:title (i18n/label :t/no-permissions)
:description (i18n/label :t/no-collectibles-description)
:placeholder? true}]
[about/view])]))
[collectibles/view
{:loading? updating?
:collectibles collectible-list
:current-account-address current-account-address
:on-end-reached on-end-reached
:on-collectible-press on-collectible-press
:on-collectible-long-press on-collectible-long-press}]))
(defn view
[{:keys [selected-tab]}]
[rn/view {:style {:flex 1}}
(case selected-tab
:assets [assets/view]
:collectibles [collectibles-tab]
:activity [activity/view]
:permissions [empty-tab/view
{:title (i18n/label :t/no-permissions)
:description (i18n/label :t/no-collectibles-description)
:placeholder? true}]
[about/view])])

View File

@ -1,11 +1,15 @@
(ns status-im.contexts.wallet.collectible.events
(:require [camel-snake-kebab.extras :as cske]
[clojure.set]
[clojure.string :as string]
[react-native.platform :as platform]
[status-im.contexts.network.data-store :as network.data-store]
[status-im.contexts.wallet.collectible.utils :as collectible-utils]
[status-im.contexts.wallet.data-store :as data-store]
[taoensso.timbre :as log]
[utils.collection]
[utils.ethereum.chain :as chain]
[utils.number :as utils.number]
[utils.re-frame :as rf]
[utils.transforms :as transforms]))
@ -24,6 +28,12 @@
(def max-cache-age-seconds 3600)
(def collectibles-request-batch-size 25)
(def ownership-state
{:idle 1
:delayed 2
:updating 3
:error 4})
(defn- move-collectibles-to-accounts
[accounts new-collectibles-per-account]
(reduce-kv (fn [acc account new-collectibles]
@ -84,12 +94,18 @@
(rf/reg-event-fx
:wallet/request-collectibles-for-all-accounts
(fn [{:keys [db]} [{:keys [new-request?]}]]
(let [accounts (->> (get-in db [:wallet :accounts])
(let [updating-addresses (-> (get-in db [:wallet :ui :collectibles :updating])
keys
set)
accounts (->> (get-in db [:wallet :accounts])
(filter (fn [[_ {:keys [has-more-collectibles?]}]]
(or (nil? has-more-collectibles?)
(true? has-more-collectibles?))))
(keys))
num-accounts (count accounts)
;; filter the addresses which are requested before and the collectibles are updating
requestable-addresses (clojure.set/difference (set accounts)
updating-addresses)
num-accounts (count requestable-addresses)
collectibles-per-account (quot collectibles-request-batch-size num-accounts)
;; We need to pass unique IDs for simultaneous requests, otherwise they'll fail
request-ids (get-unique-collectible-request-id num-accounts)
@ -100,13 +116,13 @@
:account account
:amount collectibles-per-account}]])
request-ids
accounts)]
requestable-addresses)]
{:db (cond-> db
:always (assoc-in [:wallet :ui :collectibles :pending-requests] num-accounts)
new-request? (update-in [:wallet :accounts] update-vals #(dissoc % :collectibles)))
:fx collectible-requests})))
(defn request-new-collectibles-for-account-from-signal
(defn request-collectibles-for-account
[{:keys [db]} [address]]
(let [pending-requests (get-in db [:wallet :ui :collectibles :pending-requests] 0)
[request-id] (get-unique-collectible-request-id 1)]
@ -117,49 +133,138 @@
:account address
:amount collectibles-request-batch-size}]]]}))
(rf/reg-event-fx :wallet/request-new-collectibles-for-account-from-signal
request-new-collectibles-for-account-from-signal)
(rf/reg-event-fx :wallet/request-collectibles-for-account request-collectibles-for-account)
(rf/reg-event-fx
:wallet/request-collectibles-for-current-viewing-account
(fn [{:keys [db]} _]
(when (network.data-store/online? db)
(let [current-viewing-account (-> db :wallet :current-viewing-account-address)
[request-id] (get-unique-collectible-request-id 1)]
{:db (assoc-in db [:wallet :ui :collectibles :pending-requests] 1)
:fx [[:dispatch
[:wallet/request-new-collectibles-for-account
{:request-id request-id
:account current-viewing-account
:amount collectibles-request-batch-size}]]]}))))
(let [current-viewing-account (-> db :wallet :current-viewing-account-address)]
{:fx [[:dispatch [:wallet/request-collectibles-for-account current-viewing-account]]]}))))
(rf/reg-event-fx
:wallet/collectible-ownership-update-finished-with-error
(fn [{:keys [db]} [{:keys [accounts message chainId]}]]
(let [address (first accounts)
pending-chain-ids (get-in db [:wallet :ui :collectibles :updating address])
updated-chain-ids (disj pending-chain-ids chainId)
all-chain-updated? (and (some? pending-chain-ids) (empty? updated-chain-ids))]
{:db (cond-> db
(some? pending-chain-ids)
(assoc-in [:wallet :ui :collectibles :updating address] updated-chain-ids))
:fx [[:dispatch
[:wallet/log-rpc-error
{:event :wallet/collectible-ownership-update-finished-with-error
:params {:address address
:chain-id chainId}}
message]]
(when all-chain-updated?
[:dispatch [:wallet/request-collectibles-for-account address]])]})))
(rf/reg-event-fx
:wallet/collectible-ownership-update-finished
(fn [{:keys [db]} [{:keys [accounts chainId]}]]
(let [address (first accounts)
pending-chain-ids (get-in db [:wallet :ui :collectibles :updating address])
updated-chain-ids (disj pending-chain-ids chainId)
all-chain-updated? (and (some? pending-chain-ids) (empty? updated-chain-ids))]
{:db (cond-> db
(some? pending-chain-ids)
(assoc-in [:wallet :ui :collectibles :updating address] updated-chain-ids))
:fx [(when all-chain-updated?
[:dispatch [:wallet/request-collectibles-for-account address]])]})))
(defn- update-collectibles-in-account
[existing-collectibles updated-collectibles]
(let [indexed-existing (utils.collection/index-by
:unique-id
existing-collectibles)
existing-ids (-> indexed-existing keys vec)
;; pick collectibles only in the app-db
indexed-updated (-> (utils.collection/index-by
:unique-id
updated-collectibles)
(select-keys existing-ids))]
(-> (merge-with
merge
indexed-existing
indexed-updated)
vals
vec)))
(rf/reg-event-fx
:wallet/collectibles-data-updated
(fn [{:keys [db]} [{:keys [message]}]]
(let [collectibles-by-address (->> message
transforms/json->clj
data-store/rpc->collectibles
(group-by #(-> % :ownership first :address)))]
{:db (update-in db
[:wallet :accounts]
#(reduce-kv
(fn [accounts address updated-collectibles]
(if (contains? accounts address)
(update-in accounts
[address :collectibles]
update-collectibles-in-account
updated-collectibles)
accounts))
%
collectibles-by-address))})))
(rf/reg-event-fx
:wallet/set-collectibles-updating-status
(fn [{:keys [db]} [address chain-ids]]
{:db (assoc-in db [:wallet :ui :collectibles :updating address] chain-ids)}))
(defn- update-fetched-collectibles-progress
[db owner-address collectibles offset has-more?]
(-> db
(update-in [:wallet :ui :collectibles :updating] dissoc owner-address)
(assoc-in [:wallet :ui :collectibles :fetched owner-address] collectibles)
(assoc-in [:wallet :accounts owner-address :current-collectible-idx]
(+ offset (count collectibles)))
(assoc-in [:wallet :accounts owner-address :has-more-collectibles?] has-more?)))
(defn- updating-collectibles?
[status]
(and (= (:state status) (ownership-state :updating))
(= (:timestamp status) -1)))
(rf/reg-event-fx
:wallet/owned-collectibles-filtering-done
(fn [{:keys [db]} [{:keys [message]}]]
(let [{:keys [offset ownershipStatus collectibles
hasMore]} (transforms/json->clj message)
collectibles (cske/transform-keys transforms/->kebab-case-keyword collectibles)
pending-requests (dec (get-in db [:wallet :ui :collectibles :pending-requests]))
owner-address (some->> ownershipStatus
first
key
name)]
hasMore]} (transforms/json->clj message)
ownership-status-by-address (-> ownershipStatus
(update-keys name)
(update-vals #(update-keys %
(comp utils.number/parse-int name))))
owner-address (some-> ownership-status-by-address
first
key)
ownership-status (get ownership-status-by-address owner-address)
collectibles (data-store/rpc->collectibles collectibles)
pending-requests (dec (get-in db [:wallet :ui :collectibles :pending-requests]))
;; check if collectibles are updating (never fetched and cached before) for this address
updating-chains (-> (select-keys
ownership-status
(for [[k v] ownership-status
:when (updating-collectibles? v)]
k))
keys
set)
updating? (-> updating-chains count pos?)]
{:db (cond-> db
:always (assoc-in [:wallet :ui :collectibles :pending-requests] pending-requests)
owner-address (update-fetched-collectibles-progress owner-address
collectibles
offset
hasMore))
(when-not updating? hasMore)))
:fx [(when (zero? pending-requests)
[:dispatch [:wallet/flush-collectibles-fetched]])]})))
[:dispatch [:wallet/flush-collectibles-fetched]])
(when updating?
[:dispatch [:wallet/set-collectibles-updating-status owner-address updating-chains]])]})))
(rf/reg-event-fx
:wallet/navigate-to-collectible-details

View File

@ -43,8 +43,8 @@
(is (match? result-db expected-db))))))
(deftest request-new-collectibles-for-account-from-signal-test
(testing "request new collectibles for account from signal"
(deftest request-collectibles-for-account-test
(testing "request collectibles for account"
(let [db {:wallet {}}
address "0x1"
expected {:db {:wallet {:ui {:collectibles {:pending-requests 1}}}}
@ -53,6 +53,6 @@
{:request-id 0
:account address
:amount events/collectibles-request-batch-size}]]]}
effects (events/request-new-collectibles-for-account-from-signal {:db db}
[address])]
effects (events/request-collectibles-for-account {:db db}
[address])]
(is (match? expected effects)))))

View File

@ -70,3 +70,10 @@
test-networks-enabled?
is-goerli-enabled?)]
(str base-link "/assets/" opensea-network-name "/" contract-address "/" token-id)))
(defn get-collectible-unique-id
[{:keys [id]}]
(let [chain-id (-> id :contract-id :chain-id)
contract-address (-> id :contract-id :address)
token-id (-> id :token-id)]
(str chain-id contract-address token-id)))

View File

@ -10,6 +10,16 @@
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn- loading-collectible-item
[_ index]
(let [gradient-color (keyword (str "gradient-" (inc (mod index 5))))]
[quo/collectible-list-item
{:type :card
:gradient-color-index gradient-color
:supported-file? true
:loading? true
:container-style style/collectible-container}]))
(defn- collectible-item
[{:keys [preview-url collection-data collectible-data total-owned on-press on-long-press]
:as collectible}
@ -41,10 +51,18 @@
(defn view
[{:keys [collectibles filtered? on-end-reached on-collectible-press
current-account-address on-collectible-long-press]}]
current-account-address on-collectible-long-press loading?]}]
(let [theme (quo.theme/use-theme)
no-results-match-query? (and filtered? (empty? collectibles))]
(cond
loading?
[rn/flat-list
{:data (repeat 8 {})
:style {:flex 1}
:content-container-style style/list-container-style
:num-columns 2
:render-fn loading-collectible-item}]
no-results-match-query?
[rn/view {:style {:flex 1 :justify-content :center}}
[quo/empty-state
@ -62,15 +80,14 @@
;; TODO: https://github.com/status-im/status-mobile/issues/20137
;; 1. If possible, move `collectibles-data` calculation to a subscription
;; 2. Optimization: do not recalculate all the collectibles, process only the new ones
(let [collectibles-data (map-indexed
(fn [index {:keys [ownership] :as collectible}]
(let [collectibles-data (map
(fn [{:keys [ownership] :as collectible}]
(let [total-owned (rf/sub [:wallet/total-owned-collectible ownership
current-account-address])]
(assoc collectible
:total-owned total-owned
:on-long-press on-collectible-long-press
:on-press on-collectible-press
:collectible-index index)))
:total-owned total-owned
:on-long-press on-collectible-long-press
:on-press on-collectible-press)))
collectibles)]
[rn/flat-list
{:data collectibles-data
@ -80,5 +97,5 @@
:num-columns 2
:render-fn collectible-item
:on-end-reached on-end-reached
:key-fn :collectible-index
:key-fn :unique-id
:on-end-reached-threshold 4}]))))

View File

@ -4,6 +4,7 @@
[clojure.set :as set]
[clojure.string :as string]
[status-im.constants :as constants]
[status-im.contexts.wallet.collectible.utils :as collectible-utils]
[status-im.contexts.wallet.common.utils.networks :as network-utils]
[status-im.contexts.wallet.send.utils :as send-utils]
[utils.collection :as utils.collection]
@ -266,3 +267,10 @@
(defn tokens-never-loaded?
[db]
(nil? (get-in db [:wallet :ui :tokens-loading])))
(defn rpc->collectibles
[collectibles]
(->> collectibles
(cske/transform-keys transforms/->kebab-case-keyword)
(map #(assoc % :unique-id (collectible-utils/get-collectible-unique-id %)))
vec))

View File

@ -105,7 +105,7 @@
:wallet/fetch-assets-for-address
(fn [_ [address]]
{:fx [[:dispatch [:wallet/get-wallet-token-for-account address]]
[:dispatch [:wallet/request-new-collectibles-for-account-from-signal address]]
[:dispatch [:wallet/request-collectibles-for-account address]]
[:dispatch [:wallet/check-recent-history-for-account address]]]}))
(defn- reconcile-accounts
@ -151,11 +151,6 @@
:on-success [:wallet/get-accounts-success]
:on-error [:wallet/log-rpc-error {:event :wallet/get-accounts}]}]]]}))
(rf/reg-event-fx :wallet/process-account-from-signal
(fn [{:keys [db]} [{:keys [address] :as account}]]
{:db (assoc-in db [:wallet :accounts address] (data-store/rpc->account account))
:fx [[:dispatch [:wallet/fetch-assets-for-address address]]]}))
(rf/reg-event-fx
:wallet/save-account
(fn [_ [{:keys [account on-success]}]]
@ -584,25 +579,13 @@
(rf/reg-event-fx
:wallet/process-keypair-from-backup
(fn [{:keys [db]} [{:keys [backedUpKeypair]}]]
(let [{:keys [key-uid accounts]} backedUpKeypair
accounts-fx
(mapv (fn [{:keys [chat] :as account}]
;; We exclude the chat account from the profile keypair for fetching the assets
(when-not chat
[:dispatch
[:wallet/process-account-from-signal
account]]))
accounts)]
{:db (assoc-in db
[:wallet :keypairs key-uid]
(data-store/rpc->keypair backedUpKeypair))
:fx accounts-fx})))
(fn [_ [{:keys [backedUpKeypair]}]]
{:fx [[:dispatch [:wallet/reconcile-keypairs [backedUpKeypair]]]]}))
(rf/reg-event-fx
:wallet/process-watch-only-account-from-backup
(fn [_ [{:keys [backedUpWatchOnlyAccount]}]]
{:fx [[:dispatch [:wallet/process-account-from-signal backedUpWatchOnlyAccount]]]}))
{:fx [[:dispatch [:wallet/reconcile-watch-only-accounts [backedUpWatchOnlyAccount]]]]}))
(defn reconcile-watch-only-accounts
[{:keys [db]} [watch-only-accounts]]

View File

@ -143,13 +143,6 @@
address-2 {:address address-2}}}})
(is (match? expected-fx (:fx (dispatch [event-id])))))))
(h/deftest-event :wallet/process-account-from-signal
[event-id dispatch]
(let [expected-effects {:db {:wallet {:accounts {address account}}}
:fx [[:dispatch [:wallet/fetch-assets-for-address address]]]}]
(reset! rf-db/app-db {:wallet {:accounts {}}})
(is (match? expected-effects (dispatch [event-id raw-account])))))
(h/deftest-event :wallet/reconcile-keypairs
[event-id dispatch]
(let [keypair-key-uid (:key-uid raw-account)]

View File

@ -19,17 +19,23 @@
(def on-collectible-press #(rf/dispatch [:wallet/navigate-to-collectible-details %]))
(def request-collectibles #(rf/dispatch [:wallet/request-collectibles-for-all-accounts {}]))
(defn- collectibles-tab
[]
(let [updating? (rf/sub [:wallet/home-tab-collectibles-updating?])
collectible-list (rf/sub [:wallet/owned-collectibles-list-in-selected-networks])]
[collectibles/view
{:loading? updating?
:collectibles collectible-list
:on-collectible-long-press on-collectible-long-press
:on-end-reached request-collectibles
:on-collectible-press on-collectible-press}]))
(defn view
[{:keys [selected-tab]}]
(let [collectible-list (rf/sub [:wallet/owned-collectibles-list-in-selected-networks])
request-collectibles #(rf/dispatch
[:wallet/request-collectibles-for-all-accounts {}])]
[rn/view {:style style/container}
(case selected-tab
:assets [assets/view]
:collectibles [collectibles/view
{:collectibles collectible-list
:on-collectible-long-press on-collectible-long-press
:on-end-reached request-collectibles
:on-collectible-press on-collectible-press}]
[activity/view {:activities []}])]))
[rn/view {:style style/container}
(case selected-tab
:assets [assets/view]
:collectibles [collectibles-tab]
[activity/view {:activities []}])])

View File

@ -29,6 +29,21 @@
[:wallet/pending-transaction-status-changed-received
(transforms/js->clj event-js)]]]}
"wallet-collectibles-ownership-update-finished"
{:fx [[:dispatch
[:wallet/collectible-ownership-update-finished
(transforms/js->clj event-js)]]]}
"wallet-collectibles-ownership-update-finished-with-error"
{:fx [[:dispatch
[:wallet/collectible-ownership-update-finished-with-error
(transforms/js->clj event-js)]]]}
"wallet-collectibles-data-updated"
{:fx [[:dispatch
[:wallet/collectibles-data-updated
(transforms/js->clj event-js)]]]}
"wallet-owned-collectibles-filtering-done"
{:fx [[:dispatch
[:wallet/owned-collectibles-filtering-done

View File

@ -137,3 +137,29 @@
acc))
0
ownership))))
(re-frame/reg-sub
:wallet/collectibles
:<- [:wallet/ui]
:-> :collectibles)
(re-frame/reg-sub
:wallet/collectibles-updating
:<- [:wallet/collectibles]
:-> :updating)
(re-frame/reg-sub
:wallet/current-viewing-account-collectibles-updating?
:<- [:wallet/collectibles-updating]
:<- [:wallet/current-viewing-account-address]
(fn [[updating-addresses address]]
(contains? updating-addresses address)))
(re-frame/reg-sub
:wallet/home-tab-collectibles-updating?
:<- [:wallet/collectibles-updating]
:<- [:wallet/accounts-without-watched-accounts]
(fn [[updating-addresses accounts]]
(->> accounts
(map :address)
(every? #(contains? updating-addresses %)))))

View File

@ -3,7 +3,7 @@
"_comment": "Instead use: scripts/update-status-go.sh <rev>",
"owner": "status-im",
"repo": "status-go",
"version": "v0.184.51",
"commit-sha1": "9e722ed09544cc798bc8aa5cdb3b7b58241fbf95",
"src-sha256": "1r8w9309gffb52myfgnqyv8cpn372qkq14hpwk2zmz86qphw9jvf"
"version": "v0.184.52",
"commit-sha1": "81cfce709e8d123eb1956b87f8e0f19dc47c122c",
"src-sha256": "0hna30ms4ccxmi20l5v36y84pva1s4jjkqg11whwmdjgw75d0fan"
}