connectivity-status animation with native driver

This commit is contained in:
Roman Volosovskyi 2019-06-26 13:24:21 +03:00
parent ab2248f9f8
commit 2b6bfb9851
No known key found for this signature in database
GPG Key ID: 0238A4B5ECEE70DE
9 changed files with 147 additions and 84 deletions

View File

@ -11,14 +11,27 @@
(defn interpolate [anim-value config] (defn interpolate [anim-value config]
(.interpolate anim-value (clj->js config))) (.interpolate anim-value (clj->js config)))
(defn add-native-driver [{:keys [useNativeDriver] :as config}]
(assoc config
:useNativeDriver
(if (nil? useNativeDriver)
true
useNativeDriver)))
(defn timing [anim-value config] (defn timing [anim-value config]
(.timing (react/animated) anim-value (clj->js config))) (.timing (react/animated)
anim-value
(clj->js (add-native-driver config))))
(defn spring [anim-value config] (defn spring [anim-value config]
(.spring (react/animated) anim-value (clj->js config))) (.spring (react/animated)
anim-value
(clj->js (add-native-driver config))))
(defn decay [anim-value config] (defn decay [anim-value config]
(.decay (react/animated) anim-value (clj->js config))) (.decay (react/animated)
anim-value
(clj->js (add-native-driver config))))
(defn anim-sequence [animations] (defn anim-sequence [animations]
(.sequence (react/animated) (clj->js animations))) (.sequence (react/animated) (clj->js animations)))

View File

@ -4,9 +4,9 @@
[status-im.utils.platform :as platform])) [status-im.utils.platform :as platform]))
(defnstyle text-wrapper (defnstyle text-wrapper
[{:keys [window-width modal? height background-color opacity]}] [{:keys [window-width height background-color opacity]}]
(cond-> {:flex-direction :row (cond-> {:flex-direction :row
:justify-content :center :justify-content :center
:opacity opacity :opacity opacity
:background-color (or background-color colors/gray) :background-color (or background-color colors/gray)
:height height} :height height}

View File

@ -6,7 +6,8 @@
[status-im.i18n :as i18n] [status-im.i18n :as i18n]
[status-im.ui.components.colors :as colors] [status-im.ui.components.colors :as colors]
[status-im.ui.components.animation :as animation] [status-im.ui.components.animation :as animation]
[status-im.utils.utils :as utils])) [status-im.utils.utils :as utils]
[status-im.utils.platform :as platform]))
(defn easing [direction n] (defn easing [direction n]
{:toValue n {:toValue n
@ -71,12 +72,14 @@
{:toValue 0 {:toValue 0
:delay 800 :delay 800
:duration 150 :duration 150
:easing (.-ease (animation/easing))}) :easing (.-ease (animation/easing))
:useNativeDriver true})
(animation/timing anim-height (animation/timing anim-height
{:toValue 0 {:toValue (if platform/desktop? 0 -35)
:delay 800 :delay 800
:duration 150 :duration 150
:easing (.-ease (animation/easing))})])) :easing (.-ease (animation/easing))
:useNativeDriver true})]))
(utils/set-timeout (utils/set-timeout
#(reset! show-connected? false) #(reset! show-connected? false)
2000)) 2000))
@ -86,52 +89,65 @@
[(animation/timing anim-opacity [(animation/timing anim-opacity
{:toValue 1 {:toValue 1
:duration 150 :duration 150
:easing (.-ease (animation/easing))}) :easing (.-ease (animation/easing))
:useNativeDriver true})
(animation/timing anim-height (animation/timing anim-height
{:toValue 35 {:toValue (if platform/desktop? 35 0)
:duration 150 :duration 150
:easing (.-ease (animation/easing))})]))))) :easing (.-ease (animation/easing))
:useNativeDriver true})])))))
(defn connectivity-status (defn connectivity-status
[{:keys [connected?]}] [{:keys [connected?]} anim-translate-y]
(let [anim-opacity (animation/create-value 0) (let [anim-translate-y (or anim-translate-y (animation/create-value 0))
anim-height (animation/create-value 0)] anim-opacity (animation/create-value 0)]
(manage-visibility connected? (manage-visibility connected?
anim-opacity anim-height) anim-opacity anim-translate-y)
(reagent/create-class (reagent/create-class
{:component-did-update {:component-did-update
(fn [comp] (fn [comp]
(manage-visibility (:connected? (reagent/props comp)) (manage-visibility (:connected? (reagent/props comp))
anim-opacity anim-height)) anim-opacity anim-translate-y))
:reagent-render :reagent-render
(fn [{:keys [view-id message on-press-fn (fn [{:keys [view-id message on-press-fn
connected? connecting? loading-indicator?] :as opts}] connected? connecting?] :as opts}]
(when (or (not connected?) [react/animated-view {:style (styles/text-wrapper
@show-connected?) (assoc opts
[react/animated-view {:style (styles/text-wrapper :height (if platform/desktop?
(assoc opts anim-translate-y
:height anim-height 35)
:background-color (if connected? :background-color (if connected?
colors/green colors/green
colors/gray) colors/gray)
:opacity anim-opacity :opacity anim-opacity
:modal? (= view-id :chat-modal))) :modal? (= view-id :chat-modal)))
:accessibility-label :connection-status-text} :accessibility-label :connection-status-text}
(when connecting? (when connecting?
[react/activity-indicator {:animated true [react/activity-indicator {:animated true
:color colors/white :color colors/white
:margin-right 6}]) :margin-right 6}])
(if (= message :mobile-network) (if (= message :mobile-network)
[react/nested-text {:style styles/text [react/nested-text {:style styles/text
:on-press on-press-fn} :on-press on-press-fn}
(i18n/label :t/waiting-for-wifi) " " (i18n/label :t/waiting-for-wifi) " "
[{:style {:text-decoration-line :underline}} [{:style {:text-decoration-line :underline}}
(i18n/label :t/waiting-for-wifi-change)]] (i18n/label :t/waiting-for-wifi-change)]]
[react/text {:style styles/text [react/text {:style styles/text
:on-press on-press-fn} :on-press on-press-fn}
(i18n/label message)])]))}))) (i18n/label message)])])})))
(defview connectivity-view [] (defn connectivity-animation-wrapper [style anim-value & content]
(vec (concat
(if platform/desktop?
[react/view {:style {:flex 1}}]
[react/animated-view
{:style
(merge {:flex 1
:transform [{:translateY anim-value}]}
style)}])
content)))
(defview connectivity-view [anim-translate-y]
(letsubs [status-properties [:connectivity/status-properties] (letsubs [status-properties [:connectivity/status-properties]
view-id [:view-id] view-id [:view-id]
window-width [:dimensions/window-width]] window-width [:dimensions/window-width]]
@ -142,4 +158,5 @@
[connectivity-status [connectivity-status
(merge status-properties (merge status-properties
{:view-id view-id {:view-id view-id
:window-width window-width})]]))) :window-width window-width})
anim-translate-y]])))

View File

@ -248,15 +248,17 @@
To render something on empty sections, use renderSectionFooter and conditionaly To render something on empty sections, use renderSectionFooter and conditionaly
render on empty data render on empty data
See https://facebook.github.io/react-native/docs/sectionlist.html" See https://facebook.github.io/react-native/docs/sectionlist.html"
[{:keys [sections render-section-header-fn render-section-footer-fn] :as props [{:keys [sections render-section-header-fn render-section-footer-fn style] :as props
:or {render-section-header-fn default-render-section-header}}] :or {render-section-header-fn default-render-section-header
style {}}}]
[(section-list-class) [(section-list-class)
(merge (base-list-props props) (merge (base-list-props props)
props props
(when render-section-footer-fn (when render-section-footer-fn
{:renderSectionFooter (wrap-render-section-header-fn render-section-footer-fn)}) {:renderSectionFooter (wrap-render-section-header-fn render-section-footer-fn)})
{:sections (clj->js (map wrap-per-section-render-fn sections)) {:sections (clj->js (map wrap-per-section-render-fn sections))
:renderSectionHeader (wrap-render-section-header-fn render-section-header-fn)})]) :renderSectionHeader (wrap-render-section-header-fn render-section-header-fn)
:style style})])
(defn render-action [{:keys [label subtext accessibility-label icon action disabled?]} (defn render-action [{:keys [label subtext accessibility-label icon action disabled?]}
{:keys [action-style action-label-style action-subtext-style icon-opts]}] {:keys [action-style action-label-style action-subtext-style icon-opts]}]

View File

@ -181,13 +181,15 @@
{:margin-right 12}) {:margin-right 12})
(def message-view-preview (def message-view-preview
{:flex 1 {:flex 1
:align-items :center :align-items :center
:justify-content :center}) :justify-content :center
:background-color :white})
(defn message-view-animated [opacity] (defn message-view-animated [opacity]
{:opacity opacity {:opacity opacity
:flex 1}) :flex 1
:background-color :white})
(def empty-chat-container (def empty-chat-container
{:flex 1 {:flex 1

View File

@ -50,7 +50,7 @@
(defn chat-toolbar (defn chat-toolbar
[{:keys [chat-name group-chat chat-id contact]} public? modal?] [{:keys [chat-name group-chat chat-id contact]} public? modal?]
[react/view [react/view {:style {:z-index 100}}
[status-bar/status-bar (when modal? {:type :modal-white})] [status-bar/status-bar (when modal? {:type :modal-white})]
[toolbar/toolbar [toolbar/toolbar
{:chat? true} {:chat? true}
@ -65,7 +65,6 @@
:icon-opts {:color :black :icon-opts {:color :black
:accessibility-label :chat-menu-button} :accessibility-label :chat-menu-button}
:handler #(on-options chat-id chat-name group-chat public?)}]])] :handler #(on-options chat-id chat-name group-chat public?)}]])]
[connectivity/connectivity-view]
(when (and (not group-chat) (when (and (not group-chat)
(not (contact.db/added? contact))) (not (contact.db/added? contact)))
[add-contact-bar chat-id])]) [add-contact-bar chat-id])])
@ -354,7 +353,8 @@
:input-focused? false}]))} :input-focused? false}]))}
(let [no-messages (empty? messages) (let [no-messages (empty? messages)
flat-list-conf flat-list-conf
{:data messages {:style {:margin-bottom -35}
:data messages
:ref #(reset! messages-list-ref %) :ref #(reset! messages-list-ref %)
:footer [chat-intro-header-container chat no-messages] :footer [chat-intro-header-container chat no-messages]
:key-fn #(or (:message-id %) (:value %)) :key-fn #(or (:message-id %) (:value %))
@ -428,9 +428,10 @@
(defview chat-root [modal?] (defview chat-root [modal?]
(letsubs [{:keys [public? chat-id show-input?] :as current-chat} (letsubs [{:keys [public? chat-id show-input?] :as current-chat}
[:chats/current-chat] [:chats/current-chat]
current-chat-id [:chats/current-chat-id] current-chat-id [:chats/current-chat-id]
show-message-options? [:chats/current-chat-ui-prop :show-message-options?] show-message-options? [:chats/current-chat-ui-prop :show-message-options?]
show-stickers? [:chats/current-chat-ui-prop :show-stickers?]] show-stickers? [:chats/current-chat-ui-prop :show-stickers?]
anim-translate-y (animation/create-value -35)]
;; this check of current-chat-id is necessary only because in a fresh public chat creation sometimes ;; this check of current-chat-id is necessary only because in a fresh public chat creation sometimes
;; this component renders before current-chat-id is set to current chat-id. Hence further down in sub ;; this component renders before current-chat-id is set to current chat-id. Hence further down in sub
;; components (e.g. chat-toolbar) there can be a brief visual inconsistancy like showing 'add contact' ;; components (e.g. chat-toolbar) there can be a brief visual inconsistancy like showing 'add contact'
@ -447,11 +448,15 @@
:on-layout (fn [e] :on-layout (fn [e]
(re-frame/dispatch [:set :layout-height (-> e .-nativeEvent .-layout .-height)]))} (re-frame/dispatch [:set :layout-height (-> e .-nativeEvent .-layout .-height)]))}
[chat-toolbar current-chat public? modal?] [chat-toolbar current-chat public? modal?]
[messages-view-animation [connectivity/connectivity-view anim-translate-y]
;;TODO(kozieiev) : When FlatList in react-native-desktop become viable it should be used instead of optimized ScrollView for chat [connectivity/connectivity-animation-wrapper
(if platform/desktop? {}
[messages-view-desktop current-chat modal?] anim-translate-y
[messages-view current-chat modal?])] [messages-view-animation
;;TODO(kozieiev) : When FlatList in react-native-desktop become viable it should be used instead of optimized ScrollView for chat
(if platform/desktop?
[messages-view-desktop current-chat modal?]
[messages-view current-chat modal?])]]
(when show-input? (when show-input?
[input/container]) [input/container])
(when show-stickers? (when show-stickers?

View File

@ -26,7 +26,8 @@
[status-im.utils.utils :as utils] [status-im.utils.utils :as utils]
[status-im.ui.screens.desktop.main.chat.emoji :as emoji] [status-im.ui.screens.desktop.main.chat.emoji :as emoji]
[status-im.ui.components.icons.vector-icons :as icons] [status-im.ui.components.icons.vector-icons :as icons]
[status-im.ui.screens.chat.message.gap :as gap]) [status-im.ui.screens.chat.message.gap :as gap]
[status-im.ui.components.animation :as animation])
(:require-macros [status-im.utils.views :as views])) (:require-macros [status-im.utils.views :as views]))
(defn toolbar-chat-view (defn toolbar-chat-view

View File

@ -55,6 +55,7 @@
(defn show-search! (defn show-search!
[] []
(swap! search-input-state assoc :show? true)
(animation/start (animation/start
(animation/timing (:height @search-input-state) (animation/timing (:height @search-input-state)
{:toValue 0 {:toValue 0
@ -69,6 +70,9 @@
(defn hide-search! (defn hide-search!
[] []
(utils/set-timeout
#(swap! search-input-state assoc :show? false)
350)
(animation/start (animation/start
(animation/timing (:height @search-input-state) (animation/timing (:height @search-input-state)
{:toValue (- styles/search-input-height) {:toValue (- styles/search-input-height)
@ -108,7 +112,8 @@
(defn home-filtered-items-list (defn home-filtered-items-list
[chats] [chats]
[list/section-list [list/section-list
{:sections [{:title :t/chats {:style {:margin-bottom -35}
:sections [{:title :t/chats
:data chats} :data chats}
{:title :t/messages {:title :t/messages
:data []}] :data []}]

View File

@ -15,7 +15,8 @@
[status-im.ui.components.bottom-bar.styles :as tabs.styles] [status-im.ui.components.bottom-bar.styles :as tabs.styles]
[status-im.ui.screens.home.views.inner-item :as inner-item] [status-im.ui.screens.home.views.inner-item :as inner-item]
[status-im.ui.components.common.common :as components.common] [status-im.ui.components.common.common :as components.common]
[status-im.ui.components.list-selection :as list-selection]) [status-im.ui.components.list-selection :as list-selection]
[status-im.ui.components.animation :as animation])
(:require-macros [status-im.utils.views :as views])) (:require-macros [status-im.utils.views :as views]))
(views/defview les-debug-info [] (views/defview les-debug-info []
@ -64,9 +65,10 @@
(if (not-empty search-filter) (if (not-empty search-filter)
[filter.views/home-filtered-items-list chats] [filter.views/home-filtered-items-list chats]
[react/animated-view [react/animated-view
(merge {:style {:flex 1 (merge {:style {:flex 1
:margin-bottom -35
:background-color :white :background-color :white
:transform [{:translateY (:height @search-input-state)}]}} :transform [{:translateY (:height @search-input-state)}]}}
(when @scrolling-from-top? (when @scrolling-from-top?
{:on-start-should-set-responder-capture {:on-start-should-set-responder-capture
(fn [event] (fn [event]
@ -87,7 +89,8 @@
previous-position))) previous-position)))
(filter.views/show-search!))) (filter.views/show-search!)))
false)})) false)}))
[list/flat-list {:data all-home-items [list/flat-list {:style {:margin-bottom (- styles/search-input-height)}
:data all-home-items
:key-fn first :key-fn first
:footer [react/view :footer [react/view
{:style {:height tabs.styles/tabs-diff {:style {:height tabs.styles/tabs-diff
@ -99,7 +102,10 @@
(zero? (.-y (.-contentOffset (.-nativeEvent e)))))) (zero? (.-y (.-contentOffset (.-nativeEvent e))))))
:render-fn :render-fn
(fn [home-item] (fn [home-item]
[inner-item/home-list-item home-item])}]])))) [inner-item/home-list-item home-item])}]
(when (:show? @search-input-state)
[react/view {:width 1
:height styles/search-input-height}])]))))
(views/defview home-action-button [] (views/defview home-action-button []
(views/letsubs [logging-in? [:accounts/login]] (views/letsubs [logging-in? [:accounts/login]]
@ -113,13 +119,15 @@
[icons/icon :main-icons/add {:color :white}])]]])) [icons/icon :main-icons/add {:color :white}])]]]))
(views/defview home [loading?] (views/defview home [loading?]
(views/letsubs [{:keys [search-filter chats all-home-items]} [:home-items]] (views/letsubs
[anim-translate-y (animation/create-value -35)
{:keys [search-filter chats all-home-items]} [:home-items]]
{:component-did-mount (fn [this] {:component-did-mount (fn [this]
(let [[_ loading?] (.. this -props -argv)] (let [[_ loading?] (.. this -props -argv)]
(when loading? (utils/set-timeout #(re-frame/dispatch [:init-rest-of-chats]) 100))))} (when loading? (utils/set-timeout #(re-frame/dispatch [:init-rest-of-chats]) 100))))}
[react/view {:flex 1} [react/view {:flex 1}
[status-bar/status-bar {:type :main}] [status-bar/status-bar {:type :main}]
[react/keyboard-avoiding-view {:style {:flex 1 [react/keyboard-avoiding-view {:style {:flex 1
:align-items :center} :align-items :center}
:on-layout (fn [e] :on-layout (fn [e]
(re-frame/dispatch (re-frame/dispatch
@ -133,18 +141,28 @@
[react/view {:style {:flex 1 [react/view {:style {:flex 1
:justify-content :center :justify-content :center
:align-items :center}} :align-items :center}}
[connectivity/connectivity-view] [connectivity/connectivity-view anim-translate-y]
[react/activity-indicator {:flex 1 [connectivity/connectivity-animation-wrapper
:animating true}]] {}
anim-translate-y
[react/activity-indicator {:flex 1
:animating true}]]]
:else :else
[react/view {:style {:flex 1}} [react/view {:style {:flex 1}}
[connectivity/connectivity-view] [connectivity/connectivity-view anim-translate-y]
[filter.views/search-input-wrapper search-filter] [connectivity/connectivity-animation-wrapper
(if (and (not search-filter) {}
(empty? all-home-items)) anim-translate-y
[home-empty-view] [filter.views/search-input-wrapper search-filter]
[home-items-view search-filter chats all-home-items filter.views/search-input-state])])] (if (and (not search-filter)
(empty? all-home-items))
[home-empty-view]
[home-items-view
search-filter
chats
all-home-items
filter.views/search-input-state])]])]
[home-action-button]]])) [home-action-button]]]))
(views/defview home-wrapper [] (views/defview home-wrapper []