diff --git a/src/react_native/reanimated.cljs b/src/react_native/reanimated.cljs index 93c3c79aeb..7e53182e0d 100644 --- a/src/react_native/reanimated.cljs +++ b/src/react_native/reanimated.cljs @@ -2,8 +2,9 @@ (:require ["react-native" :as rn] [reagent.core :as reagent] [clojure.string :as string] + ["react-native-linear-gradient" :default LinearGradient] ["react-native-reanimated" :default reanimated - :refer (useSharedValue useAnimatedStyle withTiming withDelay withSpring Easing Keyframe)])) + :refer (useSharedValue useAnimatedStyle withTiming withDelay withSpring withRepeat Easing Keyframe)])) ;; Animated Components (def create-animated-component (comp reagent/adapt-react-class (.-createAnimatedComponent reanimated))) @@ -12,6 +13,8 @@ (def image (reagent/adapt-react-class (.-Image reanimated))) (def touchable-opacity (create-animated-component (.-TouchableOpacity ^js rn))) +(def linear-gradient (create-animated-component LinearGradient)) + ;; Hooks (def use-shared-value useSharedValue) (def use-animated-style useAnimatedStyle) @@ -21,6 +24,7 @@ (def with-delay withDelay) (def with-spring withSpring) (def key-frame Keyframe) +(def with-repeat withRepeat) ;; Easings (def bezier (.-bezier ^js Easing)) @@ -68,3 +72,7 @@ (defn animate-shared-value-with-delay [anim val duration easing delay] (set-shared-value anim (with-delay delay (with-timing val (js-obj "duration" duration "easing" (get easings easing)))))) + +(defn animate-shared-value-with-repeat [anim val duration easing number-of-repetitions reverse?] + (set-shared-value anim (with-repeat (with-timing val (js-obj "duration" duration + "easing" (get easings easing))) number-of-repetitions reverse?))) \ No newline at end of file diff --git a/src/status_im/ui/screens/chat/components/messages_skeleton.cljs b/src/status_im/ui/screens/chat/components/messages_skeleton.cljs new file mode 100644 index 0000000000..ba7fdc450e --- /dev/null +++ b/src/status_im/ui/screens/chat/components/messages_skeleton.cljs @@ -0,0 +1,77 @@ +(ns status-im.ui.screens.chat.components.messages-skeleton + (:require [status-im.ui.components.react :as react] + [react-native.core :as rn] + [reagent.core :as reagent] + [react-native.reanimated :as reanimated] + [quo2.foundations.colors :as colors])) + +(def message-skeleton-height 54) + +(def avatar-skeleton-size 32) + +(def message-content-width [{:author 80 + :message 249} + {:author 124 + :message 156} + {:author 96 + :message 212} + {:author 112 + :message 144}]) + +;; Standlone message skeleton +(defn message-skeleton [] + [:f> + (fn [] + (let [color (colors/theme-colors colors/neutral-5 colors/neutral-70) + loading-color (colors/theme-colors colors/neutral-10 colors/neutral-60) + content-width (rand-nth message-content-width) + author-width (content-width :author) + message-width (content-width :message) + {window-width :width} (rn/use-window-dimensions) + 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%"})] + (reanimated/animate-shared-value-with-repeat translate-x window-width 1000 :linear (- 1) false) + [react/masked-view + {:style {:height message-skeleton-height} + :maskElement (reagent/as-element + [rn/view {:style {:height message-skeleton-height + :flex-direction :row + :padding-vertical 11 + :background-color :transparent + :padding-left 21}} + [rn/view {:style {:height avatar-skeleton-size + :width avatar-skeleton-size + :border-radius (/ avatar-skeleton-size 2) + :background-color color + :overflow :hidden}}] + [rn/view {:style {:padding-left 8 + :background-color :transparent}} + [rn/view {:style {:height 8 + :width author-width + :border-radius 6 + :background-color color + :margin-bottom 8 + :overflow :hidden}}] + [rn/view {:style {:height 16 + :width message-width + :border-radius 6 + :overflow :hidden + :background-color color}}]]])} + [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 animated-gradient-style}]]]))]) + +(defn messages-skeleton [parent-height] + (let [number-of-skeletons (int (Math/floor (/ parent-height message-skeleton-height)))] + [rn/view {:style {:background-color (colors/theme-colors + colors/white + colors/neutral-90) + :flex 1}} + (for [n (range number-of-skeletons)] + [message-skeleton {:key n}])])) \ No newline at end of file diff --git a/src/status_im/ui/screens/chat/message/gap.cljs b/src/status_im/ui/screens/chat/message/gap.cljs index 8efaa16a70..50c4b811b6 100644 --- a/src/status_im/ui/screens/chat/message/gap.cljs +++ b/src/status_im/ui/screens/chat/message/gap.cljs @@ -4,6 +4,7 @@ [re-frame.core :as re-frame] [status-im.i18n.i18n :as i18n] [status-im.utils.datetime :as datetime] + [status-im.ui.screens.chat.components.messages-skeleton :as messages-skeleton] [status-im.ui.screens.chat.styles.input.gap :as style])) (defn on-press @@ -18,16 +19,17 @@ chat-id] connected? [:mailserver/connected?] use-status-nodes? [:mailserver/use-status-nodes?] - first-gap? (= gap-ids #{:first-gap})] + first-gap? (= gap-ids #{:first-gap}) + window-height [:dimensions/window-height]] (when (or (not first-gap?) public? community?) - [react/view {:style (style/gap-container)} + [react/view {:style (when-not in-progress? style/gap-container)} [react/touchable-highlight {:on-press (when (and (not in-progress?) use-status-nodes? connected?) (on-press chat-id gap-ids)) - :style style/touchable} + :style {:height (if in-progress? window-height 48)}} [react/view {:style style/label-container} (if in-progress? - [react/activity-indicator] + [messages-skeleton/messages-skeleton window-height] [react/nested-text {:style (style/gap-text (and connected? use-status-nodes?))} (i18n/label (if first-gap? :t/load-more-messages :t/fetch-messages)) diff --git a/src/status_im/ui2/screens/chat/messages/view.cljs b/src/status_im/ui2/screens/chat/messages/view.cljs index c019669c88..4eef9758be 100644 --- a/src/status_im/ui2/screens/chat/messages/view.cljs +++ b/src/status_im/ui2/screens/chat/messages/view.cljs @@ -9,6 +9,7 @@ [status-im.ui.screens.chat.group :as chat.group] [status-im.ui.screens.chat.message.datemark :as message-datemark] [status-im.ui.screens.chat.message.gap :as gap] + [status-im.ui.screens.chat.components.messages-skeleton :as messages-skeleton] [status-im.utils.utils :as utils] [status-im.utils.platform :as platform] [status-im.ui.screens.chat.state :as state] @@ -19,6 +20,11 @@ (defonce show-floating-scroll-down-button (reagent/atom false)) (defonce messages-list-ref (atom nil)) +(def messages-view-height (reagent/atom 0)) + +(defn on-messages-view-layout [^js ev] + (reset! messages-view-height (-> ev .-nativeEvent .-layout .-height))) + (def list-key-fn #(or (:message-id %) (:value %))) (def list-ref #(reset! messages-list-ref %)) @@ -63,8 +69,7 @@ all-loaded? (