diff --git a/.env b/.env index 4298c79082..0c268547f8 100644 --- a/.env +++ b/.env @@ -31,3 +31,4 @@ DATABASE_MANAGEMENT_ENABLED=1 METRICS_ENABLED=0 EIP1559_ENABLED=1 DELETE_MESSAGE_ENABLED=1 +COLLECTIBLES_ENABLED=1 diff --git a/.env.release b/.env.release index 0a3ac89d2a..6d14adedac 100644 --- a/.env.release +++ b/.env.release @@ -21,3 +21,5 @@ ENABLE_REFERRAL_INVITE=0 METRICS_ENABLED=0 EIP1559_ENABLED=1 DELETE_MESSAGE_ENABLED=0 +COLLECTIBLES_ENABLED=0 + diff --git a/src/status_im/ethereum/json_rpc.cljs b/src/status_im/ethereum/json_rpc.cljs index e35c144d11..829ea49374 100644 --- a/src/status_im/ethereum/json_rpc.cljs +++ b/src/status_im/ethereum/json_rpc.cljs @@ -182,6 +182,8 @@ "wallet_getFavourites" {} "wallet_deleteCustomToken" {} "wallet_getCryptoOnRamps" {} + "wallet_getOpenseaCollectionsByOwner" {} + "wallet_getOpenseaAssetsByOwnerAndCollection" {} "browsers_getBrowsers" {} "browsers_addBrowser" {} "browsers_deleteBrowser" {} diff --git a/src/status_im/multiaccounts/login/core.cljs b/src/status_im/multiaccounts/login/core.cljs index 0ce0bbbbfb..11de7c644e 100644 --- a/src/status_im/multiaccounts/login/core.cljs +++ b/src/status_im/multiaccounts/login/core.cljs @@ -106,6 +106,7 @@ (wallet/initialize-tokens custom-tokens) (wallet/initialize-favourites favourites) (wallet/get-pending-transactions) + (wallet/fetch-collectibles-collection) (cond (and new-account? (not scan-all-tokens?)) (wallet/set-zero-balances (first accounts)) diff --git a/src/status_im/multiaccounts/update/core.cljs b/src/status_im/multiaccounts/update/core.cljs index ee4e61a011..86b6f81fb7 100644 --- a/src/status_im/multiaccounts/update/core.cljs +++ b/src/status_im/multiaccounts/update/core.cljs @@ -46,3 +46,12 @@ {:db (if setting-value (assoc-in db [:multiaccount setting] setting-value) (update db :multiaccount dissoc setting))})) + +(fx/defn toggle-opensea-nfts-visibility + {:events [::toggle-opensea-nfts-visiblity]} + [cofx visible?] + (fx/merge cofx + {:db (assoc-in (:db cofx) [:multiaccount :opensea-enabled?] visible?) + ;; need to add fully qualified namespace to counter circular deps + :dispatch [:status-im.wallet.core/fetch-collectibles-collection]} + (multiaccount-update :opensea-enabled? visible? {}))) diff --git a/src/status_im/subs.cljs b/src/status_im/subs.cljs index e1ae6a9fb2..b3a0fe1f2a 100644 --- a/src/status_im/subs.cljs +++ b/src/status_im/subs.cljs @@ -152,8 +152,6 @@ ;;wallet (reg-root-key-sub :wallet :wallet) (reg-root-key-sub :prices :prices) -(reg-root-key-sub :collectibles :collectibles) -(reg-root-key-sub :wallet/all-tokens :wallet/all-tokens) (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) @@ -170,6 +168,10 @@ (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/collectible-assets :wallet/collectible-assets) +(reg-root-key-sub :wallet/current-collectible-asset :wallet/current-collectible-asset) ;;commands (reg-root-key-sub :commands/select-account :commands/select-account) @@ -645,6 +647,12 @@ (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] @@ -761,12 +769,6 @@ ;;CHAT ============================================================================================================== -(re-frame/reg-sub - :get-collectible-token - :<- [:collectibles] - (fn [collectibles [_ {:keys [symbol token]}]] - (get-in collectibles [(keyword symbol) (js/parseInt token)]))) - (re-frame/reg-sub :chats/chat :<- [:chats/active-chats] @@ -1812,6 +1814,19 @@ (string/lower-case search-filter)) favs))))) +(re-frame/reg-sub + :wallet/collectible-collection + :<- [:wallet/collectible-collections] + (fn [all-collections [_ address]] + (when address + (get all-collections (string/lower-case address) [])))) + +(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] []))) + ;;ACTIVITY CENTER NOTIFICATIONS ======================================================================================== (defn- group-notifications-by-date @@ -2094,14 +2109,6 @@ :<- [:wallet] :request-transaction) -(re-frame/reg-sub - :screen-collectibles - :<- [:collectibles] - :<- [:get-screen-params] - (fn [[collectibles {:keys [symbol]}]] - (when-let [v (get collectibles symbol)] - (mapv #(assoc (second %) :id (first %)) v)))) - ;;UI ============================================================================================================== (re-frame/reg-sub diff --git a/src/status_im/ui/components/accordion.cljs b/src/status_im/ui/components/accordion.cljs index 5b1eb98b7d..62f099e683 100644 --- a/src/status_im/ui/components/accordion.cljs +++ b/src/status_im/ui/components/accordion.cljs @@ -5,11 +5,11 @@ [status-im.ui.components.react :as react] [status-im.ui.components.icons.icons :as icons])) -(defn drop-down-icon [opened?] +(defn drop-down-icon [{:keys [opened? dropdown-margin-left]}] [react/view {:flex-direction :row :align-items :center} [icons/icon (if opened? :main-icons/dropdown-up :main-icons/dropdown) {:container-style {:align-items :center - :margin-left 8 + :margin-left dropdown-margin-left :justify-content :center} :resize-mode :center :color colors/black}]]) @@ -18,20 +18,30 @@ "Render collapsible section" [_props] (let [opened? (reagent/atom false)] - (fn [{:keys [title content icon opened disabled]}] - [react/view {:padding-vertical 8} - (if (string? title) - [quo/list-item - {:title title - :icon icon - :on-press #(swap! opened? not) - :accessory [drop-down-icon (or @opened? opened)]}] - [react/touchable-opacity {:on-press #(swap! opened? not) :disabled disabled} - [react/view {:flex-direction :row - :margin-right 14 - :justify-content :space-between} - title - [drop-down-icon (or @opened? opened)]]]) - (when (or @opened? opened) - content)]))) + (fn [{:keys [title content icon opened disabled + padding-vertical dropdown-margin-left + on-open on-close] + :or {padding-vertical 8 + dropdown-margin-left 8 + on-open #() + on-close #()}}] + (let [on-press #(do + (apply (if @opened? on-close on-open) []) + (swap! opened? not))] + [react/view {:padding-vertical padding-vertical} + (if (string? title) + [quo/list-item + {:title title + :icon icon + :on-press on-press + :accessory [drop-down-icon (or @opened? opened)]}] + [react/touchable-opacity {:on-press on-press :disabled disabled} + [react/view {:flex-direction :row + :margin-right 14 + :justify-content :space-between} + title + [drop-down-icon {:opened? (or @opened? opened) + :dropdown-margin-left dropdown-margin-left}]]]) + (when (or @opened? opened) + content)])))) diff --git a/src/status_im/ui/screens/privacy_and_security_settings/views.cljs b/src/status_im/ui/screens/privacy_and_security_settings/views.cljs index 1c84e83859..1cdc0d5f81 100644 --- a/src/status_im/ui/screens/privacy_and_security_settings/views.cljs +++ b/src/status_im/ui/screens/privacy_and_security_settings/views.cljs @@ -7,6 +7,7 @@ [status-im.ui.components.common.common :as components.common] [status-im.ui.components.react :as react] [status-im.utils.config :as config] + [status-im.multiaccounts.update.core :as multiaccounts.update] [status-im.multiaccounts.biometric.core :as biometric] [status-im.utils.platform :as platform]) (:require-macros [status-im.utils.views :as views])) @@ -18,7 +19,8 @@ (views/letsubs [{:keys [mnemonic preview-privacy? messages-from-contacts-only - webview-allow-permission-requests?]} [:multiaccount] + webview-allow-permission-requests? + opensea-enabled?]} [:multiaccount] supported-biometric-auth [:supported-biometric-auth] keycard? [:keycard-multiaccount?] auth-method [:auth-method]] @@ -57,6 +59,13 @@ :on-press #(re-frame/dispatch [:multiaccounts.ui/preview-privacy-mode-switched ((complement boolean) preview-privacy?)])}] + (when config/collectibles-enabled? + [quo/list-item {:size :small + :title (i18n/label :t/enable-opensea-nfts) + :container-margin-bottom 8 + :active opensea-enabled? + :accessory :switch + :on-press #(re-frame/dispatch [::multiaccounts.update/toggle-opensea-nfts-visiblity (not opensea-enabled?)])}]) [quo/list-item {:size :small :title (i18n/label :t/chat-link-previews) :chevron true diff --git a/src/status_im/ui/screens/screens.cljs b/src/status_im/ui/screens/screens.cljs index aff62f2d94..71e143952d 100644 --- a/src/status_im/ui/screens/screens.cljs +++ b/src/status_im/ui/screens/screens.cljs @@ -11,7 +11,7 @@ [quo.previews.main :as quo.preview] [status-im.ui.screens.profile.contact.views :as contact] [status-im.ui.screens.notifications-settings.views :as notifications-settings] - [status-im.ui.screens.wallet.send.views :as wallet] + [status-im.ui.screens.wallet.send.views :as wallet.send] [status-im.ui.screens.status.new.views :as status.new] [status-im.ui.screens.browser.bookmarks.views :as bookmarks] [status-im.ui.screens.communities.invite :as communities.invite] @@ -653,7 +653,7 @@ :options {:topBar {:title {:text (i18n/label :t/send-transaction)}} :swipeToDismiss false :hardwareBackButton {:dismissModalOnPress false}} - :component wallet/prepare-send-transaction} + :component wallet.send/prepare-send-transaction} ;[Wallet] Request Transaction {:name :request-transaction @@ -662,7 +662,7 @@ :options {:topBar {:title {:text (i18n/label :t/request-transaction)}} :swipeToDismiss false :hardwareBackButton {:dismissModalOnPress false}} - :component wallet/request-transaction} + :component wallet.send/request-transaction} ;[Wallet] Buy crypto {:name :buy-crypto @@ -676,6 +676,12 @@ :options {:topBar {:visible false}} :component wallet.buy-crypto/website} + {:name :nft-details + :insets {:bottom true} + ;;TODO dynamic title + :options {:topBar {:visible false}} + :component wallet.account/nft-details} + ;My Status {:name :my-status :insets {:bottom true} diff --git a/src/status_im/ui/screens/wallet/account/views.cljs b/src/status_im/ui/screens/wallet/account/views.cljs index 0a28aaf95a..8f63f69d27 100644 --- a/src/status_im/ui/screens/wallet/account/views.cljs +++ b/src/status_im/ui/screens/wallet/account/views.cljs @@ -6,19 +6,22 @@ [status-im.ui.components.animation :as animation] [status-im.ui.components.colors :as colors] [status-im.ui.components.icons.icons :as icons] + [status-im.ui.components.accordion :as accordion] + [status-im.react-native.resources :as resources] [quo.core :as quo] [status-im.ui.components.react :as react] [status-im.ui.components.topbar :as topbar] + [status-im.utils.config :as config] [status-im.ui.screens.wallet.account.styles :as styles] [status-im.ui.screens.wallet.accounts.sheets :as sheets] [status-im.ui.screens.wallet.accounts.views :as accounts] [status-im.ui.screens.wallet.buy-crypto.views :as buy-crypto] [status-im.ui.screens.wallet.transactions.views :as history] - [status-im.utils.money :as money] - [status-im.wallet.utils :as wallet.utils] + [status-im.wallet.core :as wallet] [status-im.ui.components.tabs :as tabs] [quo.design-system.colors :as quo-colors] - [status-im.ui.screens.wallet.components.views :as wallet.components]) + [status-im.multiaccounts.update.core :as multiaccounts.update] + [status-im.utils.handlers :refer [ + (for [i (range num-assets)] + ^{:key i} + [react/view {:style {:width "48%" + :margin-bottom 16}} + [react/view {:style {:flex 1 + :aspect-ratio 1 + :border-width 1 + :background-color colors/gray-transparent-10 + :border-color colors/gray-lighter + :border-radius 16}}]])]) + +(defn nft-assets [{:keys [num-assets address collectible-slug]}] + (let [assets ( + (for [collectible collection] + ^{:key (:slug collectible)} + [accordion/section + {:title + [react/view {:flex 1} + [quo/list-item + {:title (:name collectible) + :text-size :large + :icon + [list/item-image {:style {:border-radius 40 + :overflow :hidden + :border-width 1 + :border-color "#EEF2F5"} + :source {:uri (:image_url collectible)}}] + :accessory :text + :accessory-text (:owned_asset_count collectible)}]] + :padding-vertical 0 + :dropdown-margin-left -12 + :on-open #(re-frame/dispatch [::wallet/fetch-collectible-assets-by-owner-and-collection + address + (:slug collectible) + (:owned_asset_count collectible)]) + :content [nft-assets {:address address + :num-assets (:owned_asset_count collectible) + :collectible-slug (:slug collectible)}]}])])) + +(defn nft-traits [traits] + [react/view {:flex 1 + :margin-bottom 24 + :flex-direction :row + :flex-wrap :wrap} + (for [trait traits] + ^{:key (:trait_type trait)} + [react/view {:style {:border-width 1 + :border-radius 12 + :padding-horizontal 8 + :padding-vertical 4 + :margin-right 8 + :margin-bottom 8 + :border-color colors/gray-lighter}} + [quo/text {:size :small + :color :secondary} + (:trait_type trait)] + [quo/text {} + (:value trait)]])]) + +(defn nft-details [] + (let [nft ( + [topbar/topbar + {:title (:name nft) + :subtitle (-> nft :collection :name) + :border-bottom false + :right-accessories + [{:icon :main-icons/browser + :on-press #(re-frame/dispatch [:browser.ui/open-url (:permalink nft)])}]}] + [react/scroll-view {:padding 16} + [react/image {:source {:uri (:image_url nft)} + :style {:width "100%" + :aspect-ratio 1 + :border-radius 4 + :border-width 1 + :border-color "#EEF2F5"}}] + [react/text {:style {:margin-top 24 + :margin-bottom 16}} + (:description nft)] + [nft-traits (:traits nft)]]])) + +(defn enable-opensea-view [] + [react/view {:style {:border-width 1 + :border-color colors/gray-lighter + :border-radius 8 + :margin 16}} + [react/view {:style {:padding 16}} + [react/image {:source (resources/get-theme-image :unfurl) + :style {:align-self :center + :width 132 + :height 94}}] + [quo/text {:size :small + :align :center + :style {:margin-top 6}} + (i18n/label :t/enable-opensea-nfts)] + [quo/text {:size :small + :color :secondary + :align :center + :style {:margin-top 2}} + (i18n/label :t/opensea-nfts-leak-metadata)]] + [quo/separator] + [quo/button {:on-press #(re-frame/dispatch [::multiaccounts.update/toggle-opensea-nfts-visiblity true]) + :type :secondary} + (i18n/label :enable)]]) + (views/defview assets-and-collections [address] - (views/letsubs [{:keys [tokens nfts]} [:wallet/visible-assets-with-values address] - currency [:wallet/currency]] + (views/letsubs [{:keys [tokens]} [:wallet/visible-assets-with-values address] + currency [:wallet/currency] + opensea-enabled? [:opensea-enabled?] + collectible-collection [:wallet/collectible-collection address]] (let [{:keys [tab]} @state] [react/view {:flex 1} [react/view {:flex-direction :row :margin-bottom 8 :padding-horizontal 4} @@ -125,16 +251,21 @@ ^{:key (:name item)} [accounts/render-asset item nil nil (:code currency)])] (= tab :nft) - [react/view - [collectibles-link] - (if (seq nfts) - [:<> - (for [item nfts] - ^{:key (:name item)} - [render-collectible item nil nil (:code currency)])] - [react/view {:align-items :center :margin-top 32} - [react/text {:style {:color colors/gray}} - (i18n/label :t/no-collectibles)]])] + [:<> + [opensea-link address] + ;; Hide collectibles behind a feature flag + (when config/collectibles-enabled? + (cond + (not opensea-enabled?) + [enable-opensea-view] + + (and opensea-enabled? (seq collectible-collection)) + [nft-collections address] + + :else + [react/view {:align-items :center :margin-top 32} + [react/text {:style {:color colors/gray}} + (i18n/label :t/no-collectibles)]]))] (= tab :history) [transactions address])]))) diff --git a/src/status_im/utils/config.cljs b/src/status_im/utils/config.cljs index 64e2ad8570..d14730d5a4 100644 --- a/src/status_im/utils/config.cljs +++ b/src/status_im/utils/config.cljs @@ -51,6 +51,7 @@ (def metrics-enabled? (enabled? (get-config :METRICS_ENABLED "0"))) (def eip1559-enabled? (enabled? (get-config :EIP1559_ENABLED "0"))) (def delete-message-enabled? (enabled? (get-config :DELETE_MESSAGE_ENABLED "0"))) +(def collectibles-enabled? (enabled? (get-config :COLLECTIBLES_ENABLED "1"))) ;; CONFIG VALUES (def log-level @@ -76,16 +77,17 @@ (def verify-ens-contract-address (get-config :VERIFY_ENS_CONTRACT_ADDRESS ((ethereum/chain-id->chain-keyword verify-ens-chain-id) ens/ens-registries))) (def default-multiaccount - {:preview-privacy? blank-preview? - :wallet/visible-tokens {:mainnet #{:SNT}} - :currency :usd - :appearance 0 - :profile-pictures-visibility 1 - :log-level log-level + {:preview-privacy? blank-preview? + :wallet/visible-tokens {:mainnet #{:SNT}} + :currency :usd + :appearance 0 + :profile-pictures-visibility 1 + :log-level log-level :webview-allow-permission-requests? false - :anon-metrics/should-send? false - :link-previews-enabled-sites #{} - :link-preview-request-enabled true}) + :anon-metrics/should-send? false + :opensea-enabled? false + :link-previews-enabled-sites #{} + :link-preview-request-enabled true}) (defn default-visible-tokens [chain] (get-in default-multiaccount [:wallet/visible-tokens chain])) diff --git a/src/status_im/wallet/accounts/core.cljs b/src/status_im/wallet/accounts/core.cljs index 4a58fe73f1..b920b9fa33 100644 --- a/src/status_im/wallet/accounts/core.cljs +++ b/src/status_im/wallet/accounts/core.cljs @@ -210,6 +210,7 @@ (if (utils.mobile-sync/syncing-allowed? cofx) (wallet/set-max-block address 0) (wallet/update-balances nil true)) + (wallet/fetch-collectibles-collection) (prices/update-prices) (navigation/navigate-back))))) diff --git a/src/status_im/wallet/core.cljs b/src/status_im/wallet/core.cljs index 30b948cea0..2473ec0ee6 100644 --- a/src/status_im/wallet/core.cljs +++ b/src/status_im/wallet/core.cljs @@ -211,6 +211,46 @@ :wallet/get-tokens-balances get-token-balances) +(fx/defn collectibles-collection-fetch-success + {:events [::collectibles-collection-fetch-success]} + [{:keys [db]} address collection] + {:db (assoc-in db [:wallet/collectible-collections address] collection)}) + +(fx/defn fetch-collectibles-collection + {:events [::fetch-collectibles-collection]} + [{:keys [db]}] + (let [addresses (map (comp string/lower-case :address) + (get db :multiaccount/accounts))] + (when (get-in db [:multiaccount :opensea-enabled?]) + {::json-rpc/call (map (fn [address] + {:method "wallet_getOpenseaCollectionsByOwner" + :params [address] + :on-error (fn [error] + (log/error "Unable to get Opensea collections" address error)) + :on-success #(re-frame/dispatch [::collectibles-collection-fetch-success address %])}) + addresses)}))) + +(fx/defn collectible-assets-fetch-success + {:events [::collectible-assets-fetch-success]} + [{:keys [db]} address collectible-slug assets] + {:db (assoc-in db [:wallet/collectible-assets address collectible-slug] assets)}) + +(fx/defn fetch-collectible-assets-by-owner-and-collection + {:events [::fetch-collectible-assets-by-owner-and-collection]} + [_ address collectible-slug limit] + {::json-rpc/call [{:method "wallet_getOpenseaAssetsByOwnerAndCollection" + :params [address collectible-slug limit] + :on-error (fn [error] + (log/error "Unable to get collectible assets" address error)) + :on-success #(re-frame/dispatch [::collectible-assets-fetch-success address collectible-slug %])}]}) + +(fx/defn show-nft-details + {:events [::show-nft-details]} + [cofx asset] + (fx/merge cofx + {:db (assoc (:db cofx) :wallet/current-collectible-asset asset)} + (navigation/navigate-to :nft-details {}))) + (defn rpc->token [tokens] (reduce (fn [acc {:keys [address] :as token}] (assoc acc diff --git a/status-go-version.json b/status-go-version.json index 716ffd951f..135c1aa221 100644 --- a/status-go-version.json +++ b/status-go-version.json @@ -2,7 +2,7 @@ "_comment": "DO NOT EDIT THIS FILE BY HAND. USE 'scripts/update-status-go.sh ' instead", "owner": "status-im", "repo": "status-go", - "version": "v0.86.2", - "commit-sha1": "d8d5d797dafdb5d042f0ec9eb27d0395840c57e6", - "src-sha256": "0pl75amwyydj58l2ff8f4nv148vnsz19nhpdas6y60g145h2nfqw" + "version": "v0.86.3", + "commit-sha1": "778d95778032be907967290e9cecc5571534a1fc", + "src-sha256": "02627x2ab6l2xcm8in2m761nhnafspfv06b55gnnf8ljwbdrms3k" } diff --git a/translations/en.json b/translations/en.json index 763c63105f..994079da6a 100644 --- a/translations/en.json +++ b/translations/en.json @@ -1652,5 +1652,7 @@ "normal": "Normal", "fee-options": "Suggested fee options", "fee-cap": "Fee cap", - "tip-cap": "Tip cap" + "tip-cap": "Tip cap", + "enable-opensea-nfts": "Enable loading NFTs from OpenSea ?", + "opensea-nfts-leak-metadata": "Loading NFTs from OpenSea shares metadata with third party services" }