NFT UI 🖼
New UI and re-frame handlers for setting pfp from url Add fake chain-id, connect the pfp upload backend to UI Add toast on setting pfp, new-new-new UI Show assets based on selected network Add horzontal padding to traits card a11y ids and pr review changes Added emoji in toast, fix chain id nill issue Restore Podfile Fix fix podfile Add placeholder images for collections, hide option to set pfp when image url is empty or ends in mp4 or svg Improvise missing designs Fix paddings New nu placeholders Sort collections by name Kinda fix issue 4 Fix lint Signed-off-by: Shivek Khurana <shivek@status.im>
This commit is contained in:
parent
84d3b562a4
commit
389ce6695a
Binary file not shown.
After Width: | Height: | Size: 8.0 KiB |
Binary file not shown.
After Width: | Height: | Size: 12 KiB |
Binary file not shown.
After Width: | Height: | Size: 7.1 KiB |
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
|
@ -134,6 +134,7 @@
|
|||
"multiaccounts_getIdentityImages" {}
|
||||
"multiaccounts_getIdentityImage" {}
|
||||
"multiaccounts_storeIdentityImage" {}
|
||||
"multiaccounts_storeIdentityImageFromURL" {}
|
||||
"multiaccounts_deleteIdentityImage" {}
|
||||
"wakuext_changeIdentityImageShowTo" {}
|
||||
"wakuext_createCommunity" {}
|
||||
|
|
|
@ -202,6 +202,20 @@
|
|||
:type (name photo-quality-large)}])
|
||||
(bottom-sheet/hide-bottom-sheet))))
|
||||
|
||||
(fx/defn save-profile-picture-from-url
|
||||
{:events [::save-profile-picture-from-url]}
|
||||
[cofx url]
|
||||
(let [key-uid (get-in cofx [:db :multiaccount :key-uid])]
|
||||
(fx/merge cofx
|
||||
{::json-rpc/call [{:method "multiaccounts_storeIdentityImageFromURL"
|
||||
:params [key-uid url]
|
||||
:on-error log/warn
|
||||
:on-success #(re-frame/dispatch [::update-local-picture %])}]}
|
||||
(bottom-sheet/hide-bottom-sheet))))
|
||||
|
||||
(comment
|
||||
(re-frame/dispatch [::save-profile-picture-from-url "https://lh3.googleusercontent.com/XuKjNm3HydsaxbPkbpGs9YyCKhn5QQk5oDC8XF2jzmPyYXeZofxFtfUDZuQ3EVmacS_BlBKzbX2ypm37YNX3n1fDJA3WndeFcPsp7Z0=w600"]))
|
||||
|
||||
(fx/defn delete-profile-picture
|
||||
{:events [::delete-profile-picture]}
|
||||
[cofx name]
|
||||
|
|
|
@ -6,9 +6,9 @@
|
|||
:welcome (js/require "../resources/images/ui/welcome.jpg")
|
||||
:welcome-dark (js/require "../resources/images/ui/welcome-dark.jpg")
|
||||
:chat (js/require "../resources/images/ui/chat.jpg")
|
||||
:chat-dark (js/require "../resources/images/ui/chat-dark.jpg")
|
||||
:wallet (js/require "../resources/images/ui/wallet.jpg")
|
||||
:browser (js/require "../resources/images/ui/browser.jpg")
|
||||
:chat-dark (js/require "../resources/images/ui/chat-dark.jpg")
|
||||
:wallet-dark (js/require "../resources/images/ui/wallet-dark.jpg")
|
||||
:browser-dark (js/require "../resources/images/ui/browser-dark.jpg")
|
||||
:keys (js/require "../resources/images/ui/keys.jpg")
|
||||
|
@ -43,6 +43,8 @@
|
|||
:theme-light (js/require "../resources/images/ui/theme-light.png")
|
||||
:theme-system (js/require "../resources/images/ui/theme-system.png")
|
||||
:notifications (js/require "../resources/images/ui/notifications.png")
|
||||
:collectible (js/require "../resources/images/ui/collectible.png")
|
||||
:collectible-dark (js/require "../resources/images/ui/collectible-dark.png")
|
||||
:graph (js/require "../resources/images/ui/graph.png")})
|
||||
|
||||
(defn get-theme-image [k]
|
||||
|
|
|
@ -176,8 +176,10 @@
|
|||
(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/fetching-collection-assets :wallet/fetching-collection-assets)
|
||||
(reg-root-key-sub :wallet/collectible-assets :wallet/collectible-assets)
|
||||
(reg-root-key-sub :wallet/current-collectible-asset :wallet/current-collectible-asset)
|
||||
(reg-root-key-sub :wallet/selected-collectible :wallet/selected-collectible)
|
||||
|
||||
;;commands
|
||||
(reg-root-key-sub :commands/select-account :commands/select-account)
|
||||
|
||||
|
@ -1831,7 +1833,8 @@
|
|||
:<- [:wallet/collectible-collections]
|
||||
(fn [all-collections [_ address]]
|
||||
(when address
|
||||
(get all-collections (string/lower-case address) []))))
|
||||
(let [all-collections (get all-collections (string/lower-case address) [])]
|
||||
(sort-by :name all-collections)))))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:wallet/collectible-assets-by-collection-and-address
|
||||
|
@ -1839,6 +1842,12 @@
|
|||
(fn [all-assets [_ address collectible-slug]]
|
||||
(get-in all-assets [address collectible-slug] [])))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:wallet/fetching-assets-by-collectible-slug
|
||||
:<- [:wallet/fetching-collection-assets]
|
||||
(fn [fetching-collection-assets [_ collectible-slug]]
|
||||
(get fetching-collection-assets collectible-slug false)))
|
||||
|
||||
;;ACTIVITY CENTER NOTIFICATIONS ========================================================================================
|
||||
|
||||
(defn- group-notifications-by-date
|
||||
|
|
|
@ -20,15 +20,18 @@
|
|||
(let [opened? (reagent/atom false)]
|
||||
(fn [{:keys [title content icon opened disabled
|
||||
padding-vertical dropdown-margin-left
|
||||
open-container-style
|
||||
on-open on-close]
|
||||
:or {padding-vertical 8
|
||||
dropdown-margin-left 8
|
||||
open-container-style {}
|
||||
on-open #()
|
||||
on-close #()}}]
|
||||
(let [on-press #(do
|
||||
(apply (if @opened? on-close on-open) [])
|
||||
(swap! opened? not))]
|
||||
[react/view {:padding-vertical padding-vertical}
|
||||
[react/view (merge {:padding-vertical padding-vertical}
|
||||
(when @opened? open-container-style))
|
||||
(if (string? title)
|
||||
[quo/list-item
|
||||
{:title title
|
||||
|
@ -40,7 +43,7 @@
|
|||
:margin-right 14
|
||||
:justify-content :space-between}
|
||||
title
|
||||
[drop-down-icon {:opened? (or @opened? opened)
|
||||
[drop-down-icon {:opened? (or @opened? opened)
|
||||
:dropdown-margin-left dropdown-margin-left}]]])
|
||||
(when (or @opened? opened)
|
||||
content)]))))
|
||||
|
|
|
@ -0,0 +1,117 @@
|
|||
(ns status-im.ui.components.toastable-highlight
|
||||
"A wrapped touchable highlight that presents a toast when clicked"
|
||||
(:require [reagent.core :as reagent]
|
||||
[status-im.ui.components.animation :as animation]
|
||||
[quo.design-system.colors :as colors]
|
||||
[status-im.ui.components.react :as react]))
|
||||
|
||||
(defn hide-cue-atom [anim-opacity anim-y cue-atom]
|
||||
(animation/start
|
||||
(animation/parallel
|
||||
[(animation/timing
|
||||
anim-opacity
|
||||
{:toValue 0
|
||||
:duration 140
|
||||
:delay 1000
|
||||
:easing (.-ease ^js animation/easing)
|
||||
:useNativeDriver true})
|
||||
(animation/timing
|
||||
anim-y
|
||||
{:toValue 0
|
||||
:duration 140
|
||||
:delay 1000
|
||||
:easing (.-ease ^js animation/easing)
|
||||
:useNativeDriver true})])
|
||||
#(reset! cue-atom false)))
|
||||
|
||||
(defn show-cue-atom [anim-opacity anim-y cue-atom y]
|
||||
(when @cue-atom
|
||||
(animation/start
|
||||
(animation/parallel
|
||||
[(animation/timing
|
||||
anim-opacity
|
||||
{:toValue 1
|
||||
:duration 140
|
||||
:easing (.-ease ^js animation/easing)
|
||||
:useNativeDriver true})
|
||||
(animation/timing
|
||||
anim-y
|
||||
{:toValue y
|
||||
:duration 140
|
||||
:easing (.-ease ^js animation/easing)
|
||||
:useNativeDriver true})])
|
||||
#(hide-cue-atom anim-opacity anim-y cue-atom))))
|
||||
|
||||
(defn toast [anim-opacity anim-y width cue-atom label]
|
||||
[react/animated-view
|
||||
{:style
|
||||
{:opacity anim-opacity
|
||||
:transform [{:translateY anim-y}]
|
||||
:max-width @width
|
||||
:z-index (if @cue-atom 1 -1)
|
||||
:height 34
|
||||
:position :absolute
|
||||
:border-radius 8
|
||||
:align-self :center
|
||||
:align-items :center
|
||||
:justify-content :center
|
||||
:shadow-offset {:width 0 :height 4}
|
||||
:shadow-radius 12
|
||||
:elevation 8
|
||||
:shadow-opacity 1
|
||||
:shadow-color "rgba(0, 34, 51, 0.08)"
|
||||
:background-color colors/white}}
|
||||
[react/view
|
||||
{:padding-horizontal 16
|
||||
:padding-vertical 7
|
||||
:border-radius 8
|
||||
:background-color colors/white
|
||||
:shadow-offset {:width 0 :height 2}
|
||||
:shadow-radius 4
|
||||
:shadow-opacity 1
|
||||
:shadow-color "rgba(0, 34, 51, 0.16)"}
|
||||
[react/text
|
||||
{:style
|
||||
{:typography :main-medium
|
||||
;; line height specified here because of figma spec
|
||||
:line-height 20
|
||||
:font-size 14}}
|
||||
label]]])
|
||||
|
||||
(defn toastable-highlight-view
|
||||
[{:keys [toast-label on-press
|
||||
container-style]}
|
||||
content]
|
||||
(let [cue-atom (reagent/atom false)
|
||||
width (reagent/atom 0)
|
||||
height (reagent/atom 0)
|
||||
anim-y (animation/create-value 0)
|
||||
anim-opacity (animation/create-value 0)]
|
||||
(reagent/create-class
|
||||
{:reagent-render
|
||||
(fn [{:keys []} _]
|
||||
(let [press-fn #(when (not @cue-atom)
|
||||
(reset! cue-atom true)
|
||||
(show-cue-atom
|
||||
anim-opacity
|
||||
anim-y
|
||||
cue-atom
|
||||
(if (> @height 34)
|
||||
(- (/ @height 2))
|
||||
(- (+ 17 @height))))
|
||||
(when on-press
|
||||
(on-press)))]
|
||||
[react/view
|
||||
{:style (if container-style container-style {})
|
||||
:on-layout
|
||||
#(do
|
||||
(reset! width (-> ^js % .-nativeEvent .-layout .-width))
|
||||
(reset! height (-> ^js % .-nativeEvent .-layout .-height)))}
|
||||
[toast anim-opacity anim-y width cue-atom toast-label]
|
||||
[react/touchable-highlight
|
||||
{:active-opacity (if @cue-atom 1 0.85)
|
||||
:underlay-color colors/black
|
||||
:on-press press-fn
|
||||
:on-long-press press-fn}
|
||||
[react/view {:background-color colors/white}
|
||||
content]]]))})))
|
|
@ -71,7 +71,7 @@
|
|||
((complement boolean) preview-privacy?)])}]
|
||||
(when config/collectibles-enabled?
|
||||
[quo/list-item {:size :small
|
||||
:title (i18n/label :t/enable-opensea-nfts)
|
||||
:title (i18n/label :t/display-collectibles)
|
||||
:container-margin-bottom 8
|
||||
:active opensea-enabled?
|
||||
:accessory :switch
|
||||
|
|
|
@ -55,6 +55,7 @@
|
|||
[status-im.ui.screens.wallet.transactions.views :as wallet-transactions]
|
||||
[status-im.ui.screens.wallet.custom-tokens.views :as custom-tokens]
|
||||
[status-im.ui.screens.wallet.accounts.views :as wallet.accounts]
|
||||
[status-im.ui.screens.wallet.collectibles.views :as wallet.collectibles]
|
||||
[status-im.ui.screens.wallet.account.views :as wallet.account]
|
||||
[status-im.ui.screens.wallet.add-new.views :as add-account]
|
||||
[status-im.ui.screens.wallet.account-settings.views :as account-settings]
|
||||
|
@ -700,7 +701,7 @@
|
|||
:insets {:bottom true}
|
||||
;;TODO dynamic title
|
||||
:options {:topBar {:visible false}}
|
||||
:component wallet.account/nft-details}
|
||||
:component wallet.collectibles/nft-details-modal}
|
||||
|
||||
;My Status
|
||||
{:name :my-status
|
||||
|
|
|
@ -6,8 +6,6 @@
|
|||
[status-im.ui.components.animation :as animation]
|
||||
[quo.design-system.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]
|
||||
|
@ -17,11 +15,8 @@
|
|||
[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.wallet.core :as wallet]
|
||||
[status-im.ui.components.tabs :as tabs]
|
||||
[status-im.multiaccounts.update.core :as multiaccounts.update]
|
||||
[status-im.ui.screens.wallet.components.views :as wallet.components]
|
||||
[status-im.utils.handlers :refer [<sub]])
|
||||
[status-im.ui.screens.wallet.collectibles.views :as collectibles.views])
|
||||
(:require-macros [status-im.utils.views :as views]))
|
||||
|
||||
(def state (reagent/atom {:tab :assets}))
|
||||
|
@ -102,136 +97,6 @@
|
|||
{:style {:color colors/blue}}
|
||||
(i18n/label :t/check-on-opensea)]]])
|
||||
|
||||
(defn nft-assets-skeleton [num-assets]
|
||||
[:<>
|
||||
(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 (<sub [:wallet/collectible-assets-by-collection-and-address address collectible-slug])]
|
||||
[react/view {:flex 1
|
||||
:flex-wrap :wrap
|
||||
:justify-content :space-between
|
||||
:flex-direction :row
|
||||
:style {:padding-horizontal 16}}
|
||||
(if (seq assets)
|
||||
(for [asset assets]
|
||||
^{:key (:id asset)}
|
||||
[react/touchable-opacity
|
||||
{:style {:width "48%"
|
||||
:margin-bottom 16}
|
||||
:on-press #(re-frame/dispatch [::wallet/show-nft-details asset])}
|
||||
[react/image {:style {:flex 1
|
||||
:aspect-ratio 1
|
||||
:border-width 1
|
||||
:border-color colors/gray-lighter
|
||||
:border-radius 16}
|
||||
:source {:uri (:image_url asset)}}]])
|
||||
[nft-assets-skeleton num-assets])]))
|
||||
|
||||
(defn nft-collections [address]
|
||||
(let [collection (<sub [:wallet/collectible-collection address])]
|
||||
[:<>
|
||||
(for [collectible collection]
|
||||
^{:key (:slug collectible)}
|
||||
[accordion/section
|
||||
{:title
|
||||
[react/view {:flex 1}
|
||||
[quo/list-item
|
||||
{:title (:name collectible)
|
||||
:text-size :large
|
||||
:icon
|
||||
[wallet.components/token-icon {: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 (<sub [:wallet/current-collectible-asset])]
|
||||
[:<>
|
||||
[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]} [:wallet/visible-assets-with-values address]
|
||||
currency [:wallet/currency]
|
||||
|
@ -257,10 +122,10 @@
|
|||
(when config/collectibles-enabled?
|
||||
(cond
|
||||
(not opensea-enabled?)
|
||||
[enable-opensea-view]
|
||||
[collectibles.views/enable-opensea-view]
|
||||
|
||||
(and opensea-enabled? (seq collectible-collection))
|
||||
[nft-collections address]
|
||||
[collectibles.views/nft-collections address]
|
||||
|
||||
:else
|
||||
[react/view {:align-items :center :margin-top 32}
|
||||
|
|
|
@ -0,0 +1,256 @@
|
|||
(ns status-im.ui.screens.wallet.collectibles.views
|
||||
(:require [re-frame.core :as re-frame]
|
||||
[clojure.string :as str]
|
||||
[status-im.ui.components.react :as react]
|
||||
[quo.core :as quo]
|
||||
[status-im.utils.handlers :refer [<sub]]
|
||||
[status-im.ui.components.topbar :as topbar]
|
||||
[status-im.ui.components.toastable-highlight :refer [toastable-highlight-view]]
|
||||
[status-im.ui.screens.wallet.components.views :as wallet.components]
|
||||
[status-im.react-native.resources :as resources]
|
||||
[status-im.multiaccounts.core :as multiaccounts]
|
||||
[status-im.wallet.core :as wallet]
|
||||
[status-im.multiaccounts.update.core :as multiaccounts.update]
|
||||
[quo.design-system.colors :as colors]
|
||||
[status-im.ui.components.icons.icons :as icons]
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
[status-im.ui.components.accordion :as accordion]))
|
||||
|
||||
(defn is-image? [nft]
|
||||
(and (seq (:image_url nft))
|
||||
(not (str/ends-with? (:image_url nft) ".svg"))
|
||||
(not (str/ends-with? (:image_url nft) ".mp4"))))
|
||||
|
||||
(defn missing-image-placeholder []
|
||||
[react/view {:style {:width "100%"
|
||||
:flex 1
|
||||
:align-items :center
|
||||
:border-radius 16
|
||||
:background-color colors/gray-lighter
|
||||
:justify-content :center
|
||||
:aspect-ratio 1}}
|
||||
[icons/icon :photo {:color colors/gray}]])
|
||||
|
||||
(defn nft-assets-skeleton [num-assets]
|
||||
[:<>
|
||||
(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-trait-card [trait]
|
||||
[react/view {:style {:border-width 1
|
||||
:border-radius 12
|
||||
:margin-right 8
|
||||
:padding-vertical 4
|
||||
:padding-horizontal 8
|
||||
:border-color colors/gray-lighter}}
|
||||
[quo/text {:size :small
|
||||
:color :secondary}
|
||||
(:trait_type trait)]
|
||||
[quo/text {}
|
||||
(:value trait)]])
|
||||
|
||||
(defn nft-traits-scroller [traits]
|
||||
[react/scroll-view {:horizontal true
|
||||
:deceleration-rate "fast"
|
||||
:snap-to-alignment "left"
|
||||
:shows-horizontal-scroll-indicator
|
||||
false
|
||||
:scroll-event-throttle 64
|
||||
:style {:padding-left 16
|
||||
:margin-vertical 16
|
||||
:padding-bottom 8}}
|
||||
(for [trait traits]
|
||||
^{:key (:trait_type trait)}
|
||||
[nft-trait-card trait])
|
||||
|
||||
;; spacer
|
||||
[react/view {:style {:height 40
|
||||
:width 40}}]])
|
||||
|
||||
(defn nft-details-modal []
|
||||
(let [nft (<sub [:wallet/selected-collectible])]
|
||||
[react/scroll-view
|
||||
[topbar/topbar
|
||||
{:navigation {:icon :main-icons/close}
|
||||
:border-bottom false}]
|
||||
[react/view {:padding-horizontal 16}
|
||||
[quo/text {:size :large
|
||||
:weight :bold}
|
||||
(:name nft)]
|
||||
[quo/text {:size :small
|
||||
:color :secondary
|
||||
:style {:margin-top 4}}
|
||||
(-> nft :collection :name)]
|
||||
|
||||
(if (is-image? nft)
|
||||
[react/image {:source {:uri (:image_url nft)}
|
||||
:style {:width "100%"
|
||||
:margin-bottom 16
|
||||
:aspect-ratio 1
|
||||
:border-radius 4
|
||||
:border-width 1
|
||||
:border-color colors/gray-lighter}}]
|
||||
[missing-image-placeholder])
|
||||
|
||||
[quo/text {:style {:margin-top 12}}
|
||||
(:description nft)]]
|
||||
|
||||
(when (seq (:traits nft))
|
||||
[nft-traits-scroller (:traits nft)])
|
||||
|
||||
;; seperator
|
||||
[react/view {:style {:border-bottom-width 1
|
||||
:padding-top 8
|
||||
:border-color colors/gray-lighter}}]
|
||||
|
||||
;; TODO <shivekkhurana>: Enable txns
|
||||
;; [quo/list-item {:title (i18n/label :t/wallet-send)
|
||||
;; :icon :main-icons/send
|
||||
;; :accessibility-label
|
||||
;; :nft-send
|
||||
;; :theme :accent
|
||||
;; :on-press #()}]
|
||||
|
||||
;; TODO <shivekkhurana>: What to do with share?
|
||||
;; Share links or share image?
|
||||
;; [quo/list-item {:title (i18n/label :t/share)
|
||||
;; :theme :accent
|
||||
;; :accessibility-label
|
||||
;; :nft-share
|
||||
;; :on-press #()
|
||||
;; :icon :main-icons/share}]
|
||||
[quo/list-item {:title (i18n/label :t/view-on-opensea)
|
||||
:theme :accent
|
||||
:icon :main-icons/browser
|
||||
:on-press #(re-frame/dispatch [:browser.ui/open-url (:permalink nft)])}]
|
||||
(when (is-image? nft)
|
||||
[toastable-highlight-view
|
||||
;; the last string is an emoji. It might not show up in all editors but its there
|
||||
{:toast-label (str (i18n/label :profile-picture-updated)) " " "😎"}
|
||||
[quo/list-item {:title (i18n/label :t/use-as-profile-picture)
|
||||
:theme :accent
|
||||
:on-press #(re-frame/dispatch
|
||||
[::multiaccounts/save-profile-picture-from-url (:image_url nft)])
|
||||
:icon :main-icons/profile
|
||||
:accessibility-label
|
||||
:set-nft-as-pfp}]])]))
|
||||
|
||||
(defn no-assets-error []
|
||||
[react/view {:style {:flex 1
|
||||
:justify-content :center
|
||||
:padding 16
|
||||
:margin-vertical 16
|
||||
:align-items :center
|
||||
:border-radius 16}}
|
||||
[icons/icon :photo {:color colors/red}]
|
||||
[quo/text {:color :secondary
|
||||
:style {:magin-top 8}}
|
||||
(i18n/label :t/no-collectibles)]])
|
||||
|
||||
(defn nft-assets [{:keys [num-assets address collectible-slug]}]
|
||||
(let [assets (<sub [:wallet/collectible-assets-by-collection-and-address address collectible-slug])
|
||||
fetching? (<sub [:wallet/fetching-assets-by-collectible-slug collectible-slug])]
|
||||
[react/view {:flex 1
|
||||
:flex-wrap :wrap
|
||||
:justify-content :space-between
|
||||
:flex-direction :row
|
||||
:style {:padding-horizontal 16}}
|
||||
(cond
|
||||
fetching? [nft-assets-skeleton num-assets]
|
||||
|
||||
;; <shivekkhurana> OpenSea sometimes doesn't return an asset
|
||||
;; This condition handles it
|
||||
(and (not fetching?)
|
||||
(not (seq assets)))
|
||||
[no-assets-error]
|
||||
|
||||
(seq assets)
|
||||
(for [asset assets]
|
||||
^{:key (:id asset)}
|
||||
[react/touchable-opacity
|
||||
{:style
|
||||
{:width "48%"
|
||||
:border-radius 16
|
||||
:margin-bottom 16}
|
||||
:on-press #(re-frame/dispatch [::wallet/show-nft-details asset])
|
||||
:accessibility-label
|
||||
:nft-asset}
|
||||
(if (is-image? asset)
|
||||
[react/image {:style {:flex 1
|
||||
:aspect-ratio 1
|
||||
:border-width 1
|
||||
:border-color colors/gray-lighter
|
||||
:border-radius 16}
|
||||
:source {:uri (:image_url asset)}}]
|
||||
[missing-image-placeholder])]))]))
|
||||
|
||||
(defn nft-collections [address]
|
||||
(let [collection (<sub [:wallet/collectible-collection address])]
|
||||
[:<>
|
||||
(for [[index collectible] (map-indexed vector collection)]
|
||||
^{:key (:slug collectible)}
|
||||
[accordion/section
|
||||
{:title
|
||||
[react/view {:flex 1}
|
||||
[quo/list-item
|
||||
{:title (:name collectible)
|
||||
:text-size :large
|
||||
:accessibility-label
|
||||
(keyword (str "collection-" index))
|
||||
:icon (if (seq (:image_url collectible))
|
||||
[wallet.components/token-icon
|
||||
{:style {:border-radius 40
|
||||
:overflow :hidden
|
||||
:border-width 1
|
||||
:border-color colors/gray-lighter}
|
||||
:source {:uri (:image_url collectible)}}]
|
||||
:main-icons/photo)
|
||||
:accessory :text
|
||||
:accessory-text (:owned_asset_count collectible)}]]
|
||||
:padding-vertical 0
|
||||
:dropdown-margin-left -12
|
||||
:open-container-style {:border-top-width 8
|
||||
:border-bottom-width 8
|
||||
:border-color colors/gray-lighter}
|
||||
: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 enable-opensea-view []
|
||||
[react/view {:style {:padding 16}}
|
||||
[react/view {:style {:border-color colors/gray-lighter
|
||||
:border-width 1
|
||||
:align-self :center
|
||||
:padding 4
|
||||
:border-radius 12}}
|
||||
[react/image {:source (resources/get-theme-image :collectible)
|
||||
:style {:align-self :center
|
||||
:resize-mode :contain}}]]
|
||||
[quo/text {:align :center
|
||||
:style {:margin-vertical 16}}
|
||||
(i18n/label :t/collectibles-leak-metadata)]
|
||||
[react/view {:align-items :center}
|
||||
[quo/button {:accessibility-label :enable-opensea-nft-visibility
|
||||
:on-press
|
||||
#(re-frame/dispatch
|
||||
[::multiaccounts.update/toggle-opensea-nfts-visiblity true])
|
||||
:theme :main
|
||||
:type :primary}
|
||||
(i18n/label :display-collectibles)]]
|
||||
[quo/text {:size :small
|
||||
:color :secondary
|
||||
:align :center
|
||||
:style {:margin-top 10}}
|
||||
(i18n/label :t/disable-later-in-settings)]])
|
|
@ -343,3 +343,4 @@
|
|||
{:events [:wallet.accounts/share]}
|
||||
[_ address]
|
||||
{:list.selection/open-share {:message (eip55/address->checksum address)}})
|
||||
|
||||
|
|
|
@ -220,36 +220,49 @@
|
|||
{:events [::fetch-collectibles-collection]}
|
||||
[{:keys [db]}]
|
||||
(let [addresses (map (comp string/lower-case :address)
|
||||
(get db :multiaccount/accounts))]
|
||||
(get db :multiaccount/accounts))
|
||||
chain-id (-> db
|
||||
ethereum/current-network
|
||||
ethereum/network->chain-id)]
|
||||
(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))
|
||||
{:method "wallet_getOpenseaCollectionsByOwner"
|
||||
:params [chain-id 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)})
|
||||
{:db (-> db
|
||||
(assoc-in [:wallet/fetching-collection-assets collectible-slug] false)
|
||||
(assoc-in [:wallet/collectible-assets address collectible-slug] assets))})
|
||||
|
||||
(fx/defn collectibles-assets-fetch-error
|
||||
{:events [::collectibles-assets-fetch-error]}
|
||||
[{:keys [db]} collectible-slug]
|
||||
{:db (assoc-in db [:wallet/fetching-collection-assets collectible-slug] false)})
|
||||
|
||||
(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 %])}]})
|
||||
[{:keys [db]} address collectible-slug limit]
|
||||
(let [chain-id (ethereum/network->chain-id (ethereum/current-network db))]
|
||||
{:db (assoc-in db [:wallet/fetching-collection-assets collectible-slug] true)
|
||||
::json-rpc/call [{:method "wallet_getOpenseaAssetsByOwnerAndCollection"
|
||||
:params [chain-id address collectible-slug limit]
|
||||
:on-error (fn [error]
|
||||
(log/error "Unable to get collectible assets" address error)
|
||||
(re-frame/dispatch [::collectibles-assets-fetch-error collectible-slug]))
|
||||
: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 {})))
|
||||
{:db (assoc (:db cofx) :wallet/selected-collectible asset)}
|
||||
(navigation/open-modal :nft-details {})))
|
||||
|
||||
(defn rpc->token [tokens]
|
||||
(reduce (fn [acc {:keys [address] :as token}]
|
||||
|
|
|
@ -1669,6 +1669,10 @@
|
|||
"fee-options": "Suggested fee options",
|
||||
"fee-cap": "Fee 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"
|
||||
"collectibles-leak-metadata": "You can display your NFTs here. If you do, you will share your wallet and IP address",
|
||||
"display-collectibles": "Display collectibles",
|
||||
"disable-later-in-settings": "You can disable this later in Settings",
|
||||
"use-as-profile-picture": "Use as profile picture",
|
||||
"view-on-opensea": "View on OpenSea",
|
||||
"profile-picture-updated": "Profile picture updated"
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue