Wallet (collectibles) - integrate new Collectible list item component into designs #19140 (#19528)

This commit is contained in:
mmilad75 2024-04-18 11:24:28 +01:00 committed by GitHub
parent a6b19c021f
commit 3af4384e00
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 212 additions and 298 deletions

View File

@ -9,10 +9,12 @@
:image - collection image
:theme - keyword -> :light/:dark"
[{:keys [image theme size] :or {size :size-24}}]
[{:keys [image theme size on-load-end on-error] :or {size :size-24}}]
[fast-image/fast-image
{:accessibility-label :collection-avatar
:source image
:on-load-end on-load-end
:on-error on-error
:style (style/collection-avatar theme size)}])
(def view (quo.theme/with-theme view-internal))

View File

@ -1,116 +0,0 @@
(ns quo.components.profile.collectible-list-item.component-spec
(:require
[quo.components.profile.collectible-list-item.view :as collectible-list-item]
[test-helpers.component :as h]))
(h/describe "Profile/ collectible list item tests"
(h/describe "type card"
(h/test "Renders default and on press fires"
(let [on-press (h/mock-fn)]
(h/render-with-theme-provider
[collectible-list-item/view
{:type :card
:on-press on-press}])
(h/fire-event :press (h/get-by-label-text :collectible-list-item))
(h/was-called on-press)))
(h/test "Renders loading and on press fires"
(let [on-press (h/mock-fn)]
(h/render-with-theme-provider
[collectible-list-item/view
{:type :card
:status :loading
:on-press on-press}])
(h/fire-event :press (h/get-by-label-text :collectible-list-item))
(h/was-not-called on-press)
(h/is-truthy (h/get-by-label-text :gradient-overlay))))
(h/test "Renders counter"
(h/render-with-theme-provider
[collectible-list-item/view
{:type :card
:counter "x500"}])
(h/is-truthy (h/get-by-text "x500")))
(h/test "Renders counter and collectible name"
(h/render-with-theme-provider
[collectible-list-item/view
{:type :card
:collectible-name "Doodle #6822"
:counter "x500"}])
(h/is-truthy (h/get-by-text "x500"))
(h/is-truthy (h/get-by-text "Doodle #6822")))
(h/test "Renders status cant-fetch"
(h/render-with-theme-provider
[collectible-list-item/view
{:type :card
:status :cant-fetch}])
(h/is-truthy (h/get-by-translation-text :t/cant-fetch-info)))
(h/test "Renders status unsupported"
(h/render-with-theme-provider
[collectible-list-item/view
{:type :card
:status :unsupported}])
(h/is-truthy (h/get-by-translation-text :t/unsupported-file)))
(h/test "Renders status unsupported and counter"
(h/render-with-theme-provider
[collectible-list-item/view
{:type :card
:status :unsupported
:counter "x500"}])
(h/is-truthy (h/get-by-text "x500"))
(h/is-truthy (h/get-by-translation-text :t/unsupported-file))))
(h/describe "type image"
(h/test "Renders default and on press fires"
(let [on-press (h/mock-fn)]
(h/render-with-theme-provider
[collectible-list-item/view
{:type :image
:on-press on-press}])
(h/fire-event :press (h/get-by-label-text :collectible-list-item))
(h/was-called on-press)))
(h/test "Renders loading and on press fires"
(let [on-press (h/mock-fn)]
(h/render-with-theme-provider
[collectible-list-item/view
{:type :image
:status :loading
:on-press on-press}])
(h/fire-event :press (h/get-by-label-text :collectible-list-item))
(h/was-not-called on-press)
(h/is-truthy (h/get-by-label-text :gradient-overlay))))
(h/test "Renders counter"
(h/render-with-theme-provider
[collectible-list-item/view
{:type :image
:counter "x500"}])
(h/is-truthy (h/get-by-text "x500")))
(h/test "Renders status cant-fetch"
(h/render-with-theme-provider
[collectible-list-item/view
{:type :image
:status :cant-fetch}])
(h/is-truthy (h/get-by-translation-text :t/cant-fetch-info)))
(h/test "Renders status unsupported"
(h/render-with-theme-provider
[collectible-list-item/view
{:type :image
:status :unsupported}])
(h/is-truthy (h/get-by-translation-text :t/unsupported-file)))
(h/test "Renders status unsupported and counter"
(h/render-with-theme-provider
[collectible-list-item/view
{:type :image
:status :unsupported
:counter "x500"}])
(h/get-by-text "x500")
(h/is-truthy (h/get-by-translation-text :t/unsupported-file)))))

View File

@ -28,9 +28,6 @@
:bottom 12
:left 12})
(def container
{:flex 1})
(defn card-view-container
[theme]
(merge
@ -85,6 +82,17 @@
(defn loading-image
[theme]
{:flex 1
{:position :absolute
:top 0
:bottom 0
:left 0
:right 0
:border-radius container-border-radius
:background-color (colors/theme-colors colors/white-70-blur colors/neutral-95-opa-70-blur theme)})
:background-color (colors/theme-colors colors/white-70-blur colors/neutral-95-opa-70-blur theme)
:z-index 2})
(defn avatar-container
[loaded?]
{:flex 1
:flex-direction :row
:opacity (when-not loaded? 0)})

View File

@ -41,17 +41,9 @@
:color-index gradient-color-index}])
(defn- card-details
[{:keys [status community? avatar-image-src collectible-name theme]}]
[{:keys [community? avatar-image-src collectible-name theme state set-state]}]
[rn/view {:style style/card-details-container}
(cond (= :cant-fetch status)
[text/text
{:size :paragraph-1
:weight :medium
:style {:color (colors/theme-colors colors/neutral-50 colors/neutral-40 theme)}}
(i18n/label :t/unknown)]
(= :loading status)
(cond (not (:avatar-loaded? state))
[rn/view {:style {:flex-direction :row}}
[loading-square theme]
[loading-message theme]]
@ -67,92 +59,98 @@
{:size :paragraph-1
:weight :semi-bold
:style style/card-detail-text}
collectible-name]]
collectible-name]])
:else
[:<>
[collection-avatar/view
{:size :size-20
:image avatar-image-src}]
[rn/view {:style {:width 8}}]
[text/text
{:size :paragraph-1
:weight :semi-bold
:ellipsize-mode :tail
:number-of-lines 1
:style style/card-detail-text}
collectible-name]])])
[rn/view
{:style (style/avatar-container (:avatar-loaded? state))}
[:<>
[collection-avatar/view
{:size :size-20
:on-load-end #(set-state (fn [prev-state] (assoc prev-state :avatar-loaded? true)))
:image avatar-image-src}]
[rn/view {:style {:width 8}}]]
[text/text
{:size :paragraph-1
:weight :semi-bold
:ellipsize-mode :tail
:number-of-lines 1
:style style/card-detail-text}
collectible-name]]])
(defn- card-view
[{:keys [avatar-image-src collectible-name community? counter
gradient-color-index image-src status]}]
[{:keys [avatar-image-src collectible-name community? counter state set-state
gradient-color-index image-src supported-file?]}]
(let [theme (quo.theme/use-theme-value)]
[rn/view {:style (style/card-view-container theme)}
[rn/view {:style {:aspect-ratio 1}}
(cond
(= :loading status)
[loading-image
{:theme theme
:gradient-color-index gradient-color-index}]
(= status :unsupported)
[fallback-view
{:theme theme
:label (i18n/label :t/unsupported-file)}]
(= status :cant-fetch)
(:image-error? state)
[fallback-view
{:theme theme
:label (i18n/label :t/cant-fetch-info)}]
:else
(not supported-file?)
[fallback-view
{:theme theme
:label (i18n/label :t/unsupported-file)}]
(not (:image-loaded? state))
[loading-image
{:theme theme
:gradient-color-index gradient-color-index}])
(when supported-file?
[rn/view {:style {:aspect-ratio 1}}
[rn/image
{:style style/image
:source image-src}]])]
(when (and (not= status :loading) (not= status :cant-fetch) counter)
{:style style/image
:on-load-end #(set-state (fn [prev-state] (assoc prev-state :image-loaded? true)))
:on-error #(set-state (fn [prev-state] (assoc prev-state :image-error? true)))
:source image-src}]])]
(when (and (:image-loaded? state) (not (:image-error? state)) counter)
[collectible-counter/view
{:container-style style/collectible-counter
:size :size-24
:value counter}])
[card-details
{:status status
{:state state
:set-state set-state
:community? community?
:avatar-image-src avatar-image-src
:collectible-name collectible-name
:theme theme}]]))
(defn- image-view
[{:keys [avatar-image-src community? counter
gradient-color-index image-src status]}]
[{:keys [avatar-image-src community? counter state set-state
gradient-color-index image-src supported-file?]}]
(let [theme (quo.theme/use-theme-value)]
[rn/view {:style style/image-view-container}
(cond
(= :loading status)
[loading-image
{:theme theme
:gradient-color-index gradient-color-index}]
(= status :unsupported)
[fallback-view
{:theme theme
:label (i18n/label :t/unsupported-file)}]
(= status :cant-fetch)
(:image-error? state)
[fallback-view
{:theme theme
:label (i18n/label :t/cant-fetch-info)}]
:else [rn/view {:style {:aspect-ratio 1}}
[rn/image
{:style style/image
:source image-src}]])
(when (and (not= status :loading) (not= status :cant-fetch) counter)
(not supported-file?)
[fallback-view
{:theme theme
:label (i18n/label :t/unsupported-file)}]
(not (:image-loaded? state))
[loading-image
{:theme theme
:gradient-color-index gradient-color-index}])
(when supported-file?
[rn/view {:style {:aspect-ratio 1}}
[rn/image
{:style style/image
:on-load-end #(set-state (fn [prev-state] (assoc prev-state :image-loaded? true)))
:on-error #(set-state (fn [prev-state] (assoc prev-state :image-error? true)))
:source image-src}]])
(when (and (:image-loaded? state) (not (:image-error? state)) counter)
[collectible-counter/view
{:container-style style/collectible-counter
:size :size-24
:value counter}])
(when (and (not= status :loading) (not= status :cant-fetch) community?)
(when (and (:image-loaded? state) (not (:image-error? state)) community?)
[preview-list/view
{:container-style style/avatar
:type :communities
@ -160,15 +158,28 @@
[avatar-image-src]])]))
(defn- view-internal
[{:keys [container-style type on-press status]
[{:keys [container-style type on-press on-long-press supported-file?]
:as props}]
[rn/pressable
{:on-press (when-not (= status :loading) on-press)
:accessibility-label :collectible-list-item
:style (merge container-style style/container)}
(if (= type :card)
[card-view props]
[image-view props])])
(let [[state set-state] (rn/use-state {:image-loaded? false
:image-error? false
:avatar-loaded? false})
collectible-ready? (or (:image-loaded? state) (not supported-file?))]
[rn/pressable
{:on-press (when collectible-ready? on-press)
:on-long-press (when collectible-ready? on-long-press)
:accessibility-label :collectible-list-item
:style container-style}
(if (= type :card)
[card-view
(assoc props
:state state
:set-state set-state
:supported-file? supported-file?)]
[image-view
(assoc props
:state state
:set-state set-state
:supported-file? supported-file?)])]))
(def ?schema
[:=>
@ -177,14 +188,17 @@
[:map {:closed true}
[:avatar-image-src {:optional true} [:maybe :schema.common/image-source]]
[:collectible-name {:optional true} [:maybe string?]]
[:supported-file? {:optional true} [:maybe boolean?]]
[:native-ID {:optional true} [:maybe [:or string? keyword?]]]
[:community? {:optional true} [:maybe boolean?]]
[:counter {:optional true} [:maybe string?]]
[:counter {:optional true} [:maybe [:or :string :int]]]
[:gradient-color-index {:optional true}
[:maybe [:enum :gradient-1 :gradient-2 :gradient-3 :gradient-4 :gradient-5]]]
[:image-src {:optional true} [:maybe :schema.common/image-source]]
[:on-press {:optional true} [:maybe fn?]]
[:status {:optional true} [:maybe [:enum :default :loading :cant-fetch :unsupported]]]
[:type [:enum :card :image]]]]]
[:on-long-press {:optional true} [:maybe fn?]]
[:type [:enum :card :image]]
[:container-style {:optional true} [:maybe :map]]]]]
:any])
(def view (schema/instrument #'view-internal ?schema))

View File

@ -1,40 +0,0 @@
(ns quo.components.profile.expanded-collectible.component-spec
(:require
[quo.components.profile.expanded-collectible.view :as expanded-collectible]
[test-helpers.component :as h]))
(h/describe "Profile/ expanded collectible "
(h/test "renders with counter and has on-press event"
(let [on-press (h/mock-fn)]
(h/render-with-theme-provider
[expanded-collectible/view
{:image-src
"https://media.istockphoto.com/id/603164912/photo/suburb-asphalt-road-and-sun-flowers.jpg?s=612x612&w=0&k=20&c=qLoQ5QONJduHrQ0kJF3fvoofmGAFcrq6cL84HbzdLQM="
:counter "1200"
:on-press on-press}])
(h/fire-event :press (h/get-by-label-text :expanded-collectible))
(h/was-called on-press)
(h/is-truthy (h/get-by-text "1200"))))
(h/test "renders with status :cant-fetch and has on-press event"
(let [on-press (h/mock-fn)]
(h/render-with-theme-provider
[expanded-collectible/view
{:counter "1200"
:status :cant-fetch
:on-press on-press}])
(h/fire-event :press (h/get-by-label-text :expanded-collectible))
(h/was-called on-press)
(h/is-truthy (h/get-by-translation-text :t/cant-fetch-info))))
(h/test "renders with status :unsupported and has on-press event"
(let [on-press (h/mock-fn)]
(h/render-with-theme-provider
[expanded-collectible/view
{:counter "1200"
:status :unsupported
:on-press on-press}])
(h/fire-event :press (h/get-by-label-text :expanded-collectible))
(h/was-called on-press)
(h/is-truthy (h/get-by-translation-text :t/unsupported-file)))))

View File

@ -32,9 +32,10 @@
label]])
(defn view-internal
[{:keys [container-style square? status on-press counter image-src] :or {status :default}}]
(let [theme (quo.theme/use-theme-value)
[image-size set-image-size] (rn/use-state {})]
[{:keys [container-style square? on-press counter image-src native-ID supported-file?]}]
(let [theme (quo.theme/use-theme-value)
[image-size set-image-size] (rn/use-state {})
[image-error? set-image-error] (rn/use-state false)]
(rn/use-effect
(fn []
(promesa/let [[image-width image-height] (rn/image-get-size image-src)]
@ -43,22 +44,29 @@
:aspect-ratio (/ image-width image-height)})))
[image-src])
[rn/pressable
{:on-press on-press
{:on-press (when (and (not image-error?) supported-file?) on-press)
:accessibility-label :expanded-collectible
:style (merge container-style style/container)}
(case status
:unsupported [fallback-view
{:label (i18n/label :t/unsupported-file)
:counter counter
:theme theme}]
:cant-fetch [fallback-view
{:label (i18n/label :t/cant-fetch-info)
:counter counter
:theme theme}]
(cond
(not supported-file?)
[fallback-view
{:label (i18n/label :t/unsupported-file)
:counter counter
:theme theme}]
image-error?
[fallback-view
{:label (i18n/label :t/cant-fetch-info)
:counter counter
:theme theme}]
(and (not image-error?) supported-file?)
[rn/view
[rn/image
{:style (style/image square? (:aspect-ratio image-size))
:source image-src}]
{:style (style/image square? (:aspect-ratio image-size))
:source image-src
:native-ID native-ID
:on-error #(set-image-error true)}]
[counter-view counter]])]))
(def ?schema
@ -67,10 +75,11 @@
[:props
[:map {:closed true}
[:image-src {:optional true} [:maybe string?]]
[:supported-file? {:optional true} [:maybe boolean?]]
[:container-style {:optional true} [:maybe :map]]
[:native-ID {:optional true} [:maybe [:or string? keyword?]]]
[:square? {:optional true} [:maybe boolean?]]
[:counter {:optional true} [:maybe string?]]
[:status {:optional true} [:maybe [:enum :default :loading :cant-fetch :unsupported]]]
[:on-press {:optional true} [:maybe fn?]]]]]
:any])

View File

@ -65,8 +65,6 @@
quo.components.numbered-keyboard.keyboard-key.component-spec
quo.components.onboarding.small-option-card.component-spec
quo.components.password.tips.component-spec
quo.components.profile.collectible-list-item.component-spec
quo.components.profile.expanded-collectible.component-spec
quo.components.profile.link-card.component-spec
quo.components.profile.select-profile.component-spec
quo.components.profile.showcase-nav.component-spec

View File

@ -15,12 +15,6 @@
{:key :image}]}
{:key :community?
:type :boolean}
{:key :status
:type :select
:options [{:key :loading}
{:key :default}
{:key :unsupported}
{:key :cant-fetch}]}
{:key :gradient-color-index
:type :select
:options [{:key :gradient-1}
@ -39,7 +33,6 @@
:collectible-name "Doodle #6822"
:gradient-color-index :gradient-1
:community? false
:status :loading
:counter ""})]
(fn []
[preview/preview-container
@ -52,5 +45,6 @@
:counter (when (seq (:counter @state)) (:counter @state))
:gradient-color-index (:gradient-color-index @state)
:image-src test-image
:supported-file? true
:avatar-image-src test-avatar
:on-press #(js/alert "Pressed"))]])))

View File

@ -14,12 +14,6 @@
:type :boolean}
{:key :counter
:type :text}
{:key :status
:type :select
:options [{:key :loading}
{:key :default}
{:key :unsupported}
{:key :cant-fetch}]}
{:type :select
:key :image-type
:options [{:key :vertical}
@ -29,7 +23,6 @@
[]
(let [state (reagent/atom {:square? false
:counter ""
:status :default
:image-type :horizontal})]
(fn []
[preview/preview-container
@ -39,8 +32,9 @@
:margin-horizontal 35}}
[quo/expanded-collectible
(assoc (dissoc @state :image-type)
:image-src (if (= :vertical (:image-type @state))
vertical-image
horizontal-image)
:counter (when (seq (:counter @state)) (:counter @state))
:on-press #(js/alert "Pressed"))]])))
:image-src (if (= :vertical (:image-type @state))
vertical-image
horizontal-image)
:counter (when (seq (:counter @state)) (:counter @state))
:supported-file? true
:on-press #(js/alert "Pressed"))]])))

View File

@ -13,12 +13,15 @@
(defn view
[{:keys [selected-tab]}]
(let [collectible-list (rf/sub [:wallet/current-viewing-account-collectibles-in-selected-networks])]
(let [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 #(rf/dispatch
[:wallet/request-collectibles-for-current-viewing-account])
:on-collectible-press (fn [{:keys [id]}]

View File

@ -7,3 +7,33 @@
first
:balance
js/parseInt))
(def ^:const supported-collectible-types
#{"image/jpeg"
"image/gif"
"image/bmp"
"image/png"
"image/webp"})
(defn supported-file?
[collectible-type]
(if (supported-collectible-types collectible-type)
true
(do
(println "unsupoorted collectible file type" collectible-type)
false)))
(defn total-owned-collectible
([ownership]
(total-owned-collectible ownership false))
([ownership address]
(reduce (fn [acc item]
(if (or (not address) (= (:address item) address))
(+ acc (js/parseInt (:balance item)))
acc))
0
ownership)))
(defn collectible-owned-counter
[total]
(when (> total 1) (str "x" total)))

View File

@ -4,12 +4,12 @@
[quo.foundations.colors :as colors]
[quo.theme :as quo.theme]
[react-native.core :as rn]
[react-native.svg :as svg]
[reagent.core :as reagent]
[status-im.common.scroll-page.view :as scroll-page]
[status-im.contexts.wallet.collectible.options.view :as options-drawer]
[status-im.contexts.wallet.collectible.style :as style]
[status-im.contexts.wallet.collectible.tabs.view :as tabs]
[status-im.contexts.wallet.collectible.utils :as utils]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
@ -61,6 +61,7 @@
(fn []
(let [collectible (rf/sub [:wallet/last-collectible-details])
animation-shared-element-id (rf/sub [:animation-shared-element-id])
wallet-address (rf/sub [:wallet/current-viewing-account-address])
{:keys [id
preview-url
collection-data
@ -80,7 +81,9 @@
:image-height 300
:id token-id
:header collectible-name
:description collection-name}]
:description collection-name}
total-owned (utils/total-owned-collectible (:ownership collectible)
wallet-address)]
(rn/use-unmount #(rf/dispatch [:wallet/clear-last-collectible-details]))
[scroll-page/scroll-page
{:navigate-back? true
@ -97,10 +100,13 @@
:theme theme}])}]
:picture preview-uri}}
[rn/view {:style style/container}
[rn/view {:style style/preview-container}
[rn/touchable-opacity
{:active-opacity 1
:on-press (fn []
[quo/expanded-collectible
{:image-src preview-uri
:container-style style/preview-container
:counter (utils/collectible-owned-counter total-owned)
:native-ID (when (= animation-shared-element-id token-id) :shared-element)
:supported-file? (utils/supported-file? (:animation-media-type collectible-data))
:on-press (fn []
(if svg?
(js/alert "Can't visualize SVG images in lightbox")
(rf/dispatch
@ -113,17 +119,7 @@
(fn []
[options-drawer/view
{:name collectible-name
:image preview-uri}])}])}])))}
(if svg?
[rn/view
{:style (assoc style/preview :overflow :hidden)
:native-ID (when (= animation-shared-element-id token-id)
:shared-element)}
[svg/svg-uri (assoc style/preview :uri preview-uri)]]
[rn/image
{:source preview-uri
:style style/preview
:native-ID (when (= animation-shared-element-id token-id) :shared-element)}])]]
:image preview-uri}])}])}])))}]
[header collectible-name collection-name collection-image]
[cta-buttons]
[quo/tabs

View File

@ -4,11 +4,32 @@
[quo.theme]
[react-native.core :as rn]
[status-im.common.resources :as resources]
[status-im.contexts.wallet.collectible.utils :as utils]
[status-im.contexts.wallet.common.empty-tab.view :as empty-tab]
[utils.i18n :as i18n]))
(defn- render-fn
[{:keys [preview-url collection-data ownership collectible-data] :as collectible} index address
on-press on-long-press]
(let [total-owned (utils/total-owned-collectible ownership address)]
[quo/collectible-list-item
{:type :card
:image-src (:uri preview-url)
:avatar-image-src (:image-url collection-data)
:collectible-name (:name collection-data)
:supported-file? (utils/supported-file? (:animation-media-type collectible-data))
:gradient-color-index (keyword (str "gradient-" (inc (mod index 5))))
:counter (utils/collectible-owned-counter total-owned)
:container-style {:padding 8
:width "50%"}
:on-press #(when on-press
(on-press collectible))
:on-long-press #(when on-long-press
(on-long-press collectible))}]))
(defn- view-internal
[{:keys [theme collectibles filtered? on-collectible-press on-collectible-long-press on-end-reached]}]
[{:keys [theme collectibles filtered? on-collectible-press on-end-reached current-account-address
on-collectible-long-press]}]
(let [no-results-match-query? (and filtered? (empty? collectibles))]
(cond
no-results-match-query?
@ -28,16 +49,15 @@
[rn/flat-list
{:data collectibles
:style {:flex 1}
:content-container-style {:align-items :center}
:content-container-style {:margin-horizontal 12}
:window-size 11
:num-columns 2
:render-fn (fn [{:keys [preview-url] :as collectible}]
[quo/collectible
{:images [preview-url]
:on-press #(when on-collectible-press
(on-collectible-press collectible))
:on-long-press #(when on-collectible-long-press
(on-collectible-long-press collectible))}])
:render-fn (fn [item index]
(render-fn item
index
current-account-address
on-collectible-press
on-collectible-long-press))
:on-end-reached on-end-reached
:on-end-reached-threshold 4}])))

View File

@ -37,6 +37,8 @@
[quo/expanded-collectible
{:image-src preview-uri
:square? true
:supported-file? (utils/supported-file? (get-in collectible
[:collectible-data :animation-media-type]))
:container-style style/collectible-container}]
[quo/network-tags
{:title (i18n/label :t/max {:number balance})