FF/animate-loading-skeleton (#16865)

New Animated skeleton loader
This commit is contained in:
Flavio Fraschetti 2023-08-29 15:15:32 +01:00 committed by GitHub
parent 231272603d
commit 141bf5a333
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 161 additions and 102 deletions

View File

@ -1,46 +0,0 @@
(ns quo2.components.loaders.skeleton.view
(:require [quo2.theme :as theme]
[react-native.core :as rn]
[quo2.foundations.colors :as colors]
[quo2.components.loaders.skeleton.style :as style]
[quo2.components.loaders.skeleton.constants :as constants]))
(defn- skeleton-item
[index content color]
[rn/view {:style (style/container content)}
[rn/view {:style (style/avatar color)}]
[rn/view {:style style/content-container}
[rn/view
{:style (style/content-view
{:type (if (= content :list-items) :message :author)
:index index
:content content
:color color})}]
[rn/view
{:style (style/content-view
{:type (if (= content :list-items) :author :message)
:index index
:content content
:color color})}]
(when (= content :notifications)
[rn/view
{:style (style/content-view {:type :message2
:index index
:content content
:color color})}])]])
(defn- internal-view
[{:keys [content theme blur? parent-height]}]
(let [skeleton-height (get-in constants/layout-dimensions [content :height])
number-of-skeletons (int (Math/ceil (/ parent-height skeleton-height)))
color (cond
blur? colors/white-opa-5
(= theme :dark) colors/neutral-90
:else colors/neutral-5)]
[rn/view {:style {:padding 8}}
(doall
(for [index (range number-of-skeletons)]
^{:key index}
[skeleton-item (mod index 4) content color]))]))
(def view (theme/with-theme internal-view))

View File

@ -0,0 +1,26 @@
(ns quo2.components.loaders.skeleton-list.component-spec
(:require [quo2.components.loaders.skeleton-list.view :as skeleton-list]
[quo2.foundations.colors :as colors]
[test-helpers.component :as h]))
(defn test-skeleton
[content animated?]
(let [rendered (h/render [skeleton-list/view
{:index 0
:content content
:color colors/neutral-10
:parent-height 600
:animated? animated?}])
accessibility-text :skeleton-list]
(h/is-truthy (h/get-by-label-text rendered accessibility-text))))
(h/describe "Skeleton tests"
(doseq [content [:messages :notifications :list-items]
animated? [true false]]
(let [content-str (name content)]
(h/test (str "Skeleton :"
content-str
" is "
(if animated? "animated" "static")
" based on animated? " animated?)
(test-skeleton content animated?)))))

View File

@ -1,4 +1,4 @@
(ns quo2.components.loaders.skeleton.constants)
(ns quo2.components.loaders.skeleton-list.constants)
(def ^:const layout-dimensions
{:messages {:height 56

View File

@ -1,5 +1,5 @@
(ns quo2.components.loaders.skeleton.style
(:require [quo2.components.loaders.skeleton.constants :as constants]))
(ns quo2.components.loaders.skeleton-list.style
(:require [quo2.components.loaders.skeleton-list.constants :as constants]))
(defn container
[content]

View File

@ -0,0 +1,99 @@
(ns quo2.components.loaders.skeleton-list.view
(:require [quo2.theme :as theme]
[react-native.core :as rn]
[quo2.foundations.colors :as colors]
[react-native.masked-view :as masked-view]
[react-native.reanimated :as reanimated]
[quo2.components.loaders.skeleton-list.style :as style]
[reagent.core :as reagent]
[quo2.components.loaders.skeleton-list.constants :as constants]))
(defn static-skeleton-view
[{:keys [index content color]}]
[rn/view
{:style (style/container content)}
[rn/view {:style (style/avatar color)}]
[rn/view {:style style/content-container}
[rn/view
{:style (style/content-view
{:type (if (= content :list-items) :message :author)
:index index
:content content
:color color})}]
[rn/view
{:style (style/content-view
{:type (if (= content :list-items) :author :message)
:index index
:content content
:color color})}]
(when (= content :notifications)
[rn/view
{:style (style/content-view {:type :message2
:index index
:content content
:color color})}])]])
(defn- f-animated-skeleton-view
[{:keys [style color skeleton-height animated? translate-x window-width] :as data}]
(let [loading-color (colors/theme-colors colors/neutral-10 colors/neutral-60)]
(rn/use-effect
(fn []
(when-not animated?
(reanimated/cancel-animation translate-x))
(reanimated/animate-shared-value-with-repeat translate-x window-width 1000 :linear -1 false)
#(when-not animated?
(reanimated/cancel-animation translate-x)))
[animated?])
[masked-view/masked-view
{:style {:height skeleton-height}
:mask-element (reagent/as-element [static-skeleton-view data])}
[rn/view
{:style {:flex 1
:background-color color}}
[reanimated/linear-gradient
{:colors [color color loading-color color color]
:start {:x 0 :y 0}
:end {:x 1 :y 0}
:style style}]]]))
(defn- animated-skeleton-view
[props]
[:f> f-animated-skeleton-view props])
(defn- f-internal-view
[{:keys [content theme blur? parent-height animated?] :as props}]
(let [{window-width :width} (rn/get-window)
translate-x (reanimated/use-shared-value (- window-width))
animated-gradient-style (reanimated/apply-animations-to-style
{:transform [{:translateX translate-x}]}
{:width window-width
:height "100%"})
skeleton-height (get-in constants/layout-dimensions [content :height])
number-of-skeletons (int (Math/ceil (/ parent-height skeleton-height)))
color (cond
(and blur? (= theme :dark)) colors/white-opa-5
(= theme :dark) colors/neutral-90
:else colors/neutral-5)
component (if animated? animated-skeleton-view static-skeleton-view)]
[rn/view
{:style {:padding 8}
:accessibility-label :skeleton-list}
(for [parent-index (range number-of-skeletons)]
^{:key parent-index}
[component
(merge props
{:index (mod parent-index 4)
:parent-index parent-index
:color color
:translate-x translate-x
:skeleton-height skeleton-height
:style animated-gradient-style})])]))
(defn- internal-view
[props]
[:f> f-internal-view props])
(def view (theme/with-theme internal-view))

View File

@ -68,7 +68,7 @@
quo2.components.list-items.token-value.view
quo2.components.list-items.user-list
quo2.components.loaders.skeleton
quo2.components.loaders.skeleton.view
quo2.components.loaders.skeleton-list.view
quo2.components.markdown.list.view
quo2.components.markdown.text
quo2.components.messages.author.view
@ -247,7 +247,7 @@
;;;; Loaders
(def skeleton quo2.components.loaders.skeleton/skeleton)
(def static-skeleton quo2.components.loaders.skeleton.view/view)
(def skeleton-list quo2.components.loaders.skeleton-list.view/view)
;;;; Navigation
(def floating-shell-button quo2.components.navigation.floating-shell-button.view/view)

View File

@ -38,6 +38,7 @@
[quo2.components.list-items.community.component-spec]
[quo2.components.list-items.dapp.component-spec]
[quo2.components.list-items.token-value.component-spec]
[quo2.components.loaders.skeleton-list.component-spec]
[quo2.components.markdown.text-component-spec]
[quo2.components.markdown.list.component-spec]
[quo2.components.notifications.notification.component-spec]

View File

@ -355,7 +355,7 @@
(if chat-screen-loaded?
[:f> f-messages-list-content props]
[rn/view {:style {:flex 1}}
[quo/static-skeleton
[quo/skeleton-list
{:content :messages
:parent-height content-height}]])))

View File

@ -1,43 +0,0 @@
(ns status-im2.contexts.quo-preview.loaders.skeleton
(:require [quo2.core :as quo]
[quo2.foundations.colors :as colors]
[react-native.core :as rn]
[reagent.core :as reagent]
[status-im2.contexts.quo-preview.preview :as preview]))
(def descriptor
[{:label "Content:"
:key :content
:type :select
:options [{:key :list-items
:value "List items"}
{:key :notifications
:value "Notifications"}
{:key :messages
:value "Messages"}]}
{:label "Blur?"
:key :blur?
:type :boolean}])
(defn cool-preview
[]
(let [state (reagent/atom {:content :messages
:blur? false
:parent-height 600})]
(fn []
[rn/touchable-without-feedback {:on-press rn/dismiss-keyboard!}
[rn/view {:padding-bottom 150}
[preview/customizer state descriptor]
[rn/view
[quo/static-skeleton @state]]]])))
(defn preview-skeleton
[]
[rn/view
{:background-color (colors/theme-colors colors/white colors/neutral-95)
:flex 1}
[rn/flat-list
{:flex 1
:keyboard-should-persist-taps :always
:header [cool-preview]
:key-fn str}]])

View File

@ -0,0 +1,23 @@
(ns status-im2.contexts.quo-preview.loaders.skeleton-list
(:require [quo2.core :as quo]
[reagent.core :as reagent]
[status-im2.contexts.quo-preview.preview :as preview]))
(def descriptor
[{:key :content
:type :select
:options [{:key :list-items}
{:key :notifications}
{:key :messages}]}
{:key :blur? :type :boolean}
{:key :animated? :type :boolean}])
(defn view
[]
(let [state (reagent/atom {:content :messages
:blur? false
:animated? true
:parent-height 600})]
(fn []
[preview/preview-container {:state state :descriptor descriptor}
[quo/skeleton-list @state]])))

View File

@ -66,7 +66,6 @@
[status-im2.contexts.quo-preview.list-items.preview-lists :as preview-lists]
[status-im2.contexts.quo-preview.list-items.user-list :as user-list]
[status-im2.contexts.quo-preview.list-items.community-list :as community-list]
[status-im2.contexts.quo-preview.list-items.token-value :as token-value]
[status-im2.contexts.quo-preview.markdown.text :as text]
[status-im2.contexts.quo-preview.markdown.list :as markdown-list]
[status-im2.contexts.quo-preview.messages.author :as messages-author]
@ -111,7 +110,7 @@
[status-im2.contexts.quo-preview.tags.token-tag :as token-tag]
[status-im2.contexts.quo-preview.title.title :as title]
[status-im2.contexts.quo-preview.keycard.keycard :as keycard]
[status-im2.contexts.quo-preview.loaders.skeleton :as skeleton]
[status-im2.contexts.quo-preview.loaders.skeleton-list :as skeleton-list]
[status-im2.contexts.quo-preview.community.channel-actions :as channel-actions]
[status-im2.contexts.quo-preview.gradient.gradient-cover :as gradient-cover]
[status-im2.contexts.quo-preview.wallet.account-card :as account-card]
@ -256,11 +255,11 @@
{:name :preview-lists
:component preview-lists/view}
{:name :user-list
:component user-list/preview-user-list}
{:name :token-value
:component token-value/preview}]
:loaders [{:name :skeleton
:component skeleton/preview-skeleton}]
:options {:topBar {:visible true}}
:component user-list/preview-user-list}]
:loaders [{:name :skeleton-list
:options {:topBar {:visible true}}
:component skeleton-list/view}]
:markdown [{:name :texts
:component text/preview-text}
{:name :markdown-list