From 81070f07b0f71e3f0b106245ba468f90a0347f4b Mon Sep 17 00:00:00 2001 From: Gheorghe Pinzaru Date: Fri, 20 Mar 2020 12:38:44 +0300 Subject: [PATCH] Fix android tabbar animation Signed-off-by: Gheorghe Pinzaru --- clj-rn.conf.edn | 1 + .../src/status_im/ui/components/react.cljs | 3 + .../react_native/js_dependencies.cljs | 2 + src/status_im/ui/components/tabbar/core.cljs | 121 +++++++++--------- .../ui/components/tabbar/styles.cljs | 62 ++++----- src/status_im/ui/screens/group/views.cljs | 8 +- src/status_im/ui/screens/pairing/views.cljs | 9 +- .../ui/screens/routing/browser_stack.cljs | 4 +- .../ui/screens/routing/chat_stack.cljs | 2 + src/status_im/ui/screens/routing/core.cljs | 11 +- .../ui/screens/routing/profile_stack.cljs | 2 + .../ui/screens/routing/wallet_stack.cljs | 2 + .../react_native/js_dependencies.cljs | 2 + 13 files changed, 132 insertions(+), 97 deletions(-) diff --git a/clj-rn.conf.edn b/clj-rn.conf.edn index 7716bb03c3..3c8988bab0 100644 --- a/clj-rn.conf.edn +++ b/clj-rn.conf.edn @@ -43,6 +43,7 @@ "react-native-mail" "react-native-shake" "@react-native-community/netinfo" + "react-native-gesture-handler" "react-native-safe-area-context"] ;; Desktop modules :desktop-modules ["buffer" diff --git a/components/src/status_im/ui/components/react.cljs b/components/src/status_im/ui/components/react.cljs index 5af59c0a9c..c041b82e8c 100644 --- a/components/src/status_im/ui/components/react.cljs +++ b/components/src/status_im/ui/components/react.cljs @@ -274,3 +274,6 @@ (def safe-area-consumer (adapt-class (object/get js-dependencies/safe-area-context "SafeAreaConsumer"))) (def safe-area-view (adapt-class (object/get js-dependencies/safe-area-context "SafeAreaView"))) + + +(def touchable-without-feedback-gesture (adapt-class (object/get js-dependencies/react-native-gesture-handler "TouchableWithoutFeedback"))) diff --git a/react-native/src/mobile/status_im/react_native/js_dependencies.cljs b/react-native/src/mobile/status_im/react_native/js_dependencies.cljs index 6f292d7ef8..5bbb4e1dd7 100644 --- a/react-native/src/mobile/status_im/react_native/js_dependencies.cljs +++ b/react-native/src/mobile/status_im/react_native/js_dependencies.cljs @@ -42,3 +42,5 @@ (def react-navigation-native (js/require "@react-navigation/native")) (def react-navigation-stack (js/require "@react-navigation/stack")) (def react-navigation-bottom-tabs (js/require "@react-navigation/bottom-tabs")) + +(def react-native-gesture-handler (js/require "react-native-gesture-handler")) \ No newline at end of file diff --git a/src/status_im/ui/components/tabbar/core.cljs b/src/status_im/ui/components/tabbar/core.cljs index e381017282..51e13f78a3 100644 --- a/src/status_im/ui/components/tabbar/core.cljs +++ b/src/status_im/ui/components/tabbar/core.cljs @@ -11,19 +11,17 @@ [status-im.i18n :as i18n] [re-frame.core :as re-frame])) -(defonce visible? (animation/create-value 0)) - -(defonce minimized-state (reagent/atom nil)) +(defonce visible-native (animation/create-value 0)) (defonce last-to-value (atom 1)) (defn animate - ([visible duration to] - (animate visible duration to nil)) - ([visible duration to callback] + ([visible-native duration to] + (animate visible-native duration to nil)) + ([visible-native duration to callback] (when (not= to @last-to-value) (reset! last-to-value to) (animation/start - (animation/timing visible + (animation/timing visible-native {:toValue to :duration duration :easing (animation/cubic) @@ -37,12 +35,8 @@ (defn minimize-bar [route-name] (if (main-tab? route-name) - (do - (reset! minimized-state false) - (animate visible? 150 0)) - (do - (reset! minimized-state true) - (animate visible? 150 1)))) + (animate visible-native 150 0) + (animate visible-native 150 1))) (def tabs-list-data (->> @@ -73,31 +67,35 @@ (fn [{:keys [icon label active? nav-stack on-press accessibility-label count-subscription]}] (let [count (when count-subscription @(re-frame/subscribe [count-subscription]))] - [react/touchable-highlight {:style tabs.styles/touchable-container - :on-press on-press - :accessibility-label accessibility-label} - [react/view {:style tabs.styles/tab-container} - [react/view {:style tabs.styles/icon-container} - [vector-icons/icon icon (tabs.styles/icon active?)] - (when count - (cond - (or (pos? count) (pos? (:other count))) - [react/view {:style (if (= nav-stack :chat-stack) - tabs.styles/message-counter - tabs.styles/counter)} - [badge/message-counter (or (:other count) count) true]] - (pos? (:public count)) - [react/view {:style tabs.styles/counter-public-container} - [react/view {:style tabs.styles/counter-public - :accessibility-label :public-unread-badge}]]))] - (when-not platform/desktop? - [react/view {:style tabs.styles/tab-title-container} - [react/text {:style (tabs.styles/tab-title active?)} - label]])]]))) + [react/view {:style tabs.styles/touchable-container} + [react/touchable-without-feedback-gesture + {:style {:height "100%" + :width "100%"} + :on-press on-press + :accessibility-label accessibility-label} + [react/view {:style tabs.styles/tab-container} + [react/view {:style tabs.styles/icon-container} + [vector-icons/icon icon (tabs.styles/icon active?)] + (when count + (cond + (or (pos? count) (pos? (:other count))) + [react/view {:style (if (= nav-stack :chat-stack) + tabs.styles/message-counter + tabs.styles/counter)} + [badge/message-counter (or (:other count) count) true]] + (pos? (:public count)) + [react/view {:style tabs.styles/counter-public-container} + [react/view {:style tabs.styles/counter-public + :accessibility-label :public-unread-badge}]]))] + (when-not platform/desktop? + [react/view {:style tabs.styles/tab-title-container} + [react/text {:style (tabs.styles/tab-title active?)} + label]])]]]))) (defn tabs [] - (let [listeners (atom []) - keyboard-shown? (reagent/atom false)] + (let [listeners (atom []) + keyboard-shown? (reagent/atom false) + keyboard-visible (animation/create-value 0)] (reagent/create-class {:component-did-mount (fn [] @@ -106,10 +104,20 @@ listeners [(.addListener react/keyboard "keyboardDidShow" (fn [] - (reset! keyboard-shown? true))) + (reset! keyboard-shown? true) + (reagent/flush) + (animation/start + (animation/timing keyboard-visible + {:toValue 1 + :duration 200})))) (.addListener react/keyboard "keyboardDidHide" (fn [] - (reset! keyboard-shown? false)))]))) + (animation/start + (animation/timing keyboard-visible + {:toValue 0 + :duration 200}) + #(do (reset! keyboard-shown? false) + (reagent/flush)))))]))) :component-will-unmount (fn [] (when (not-empty @listeners) @@ -118,25 +126,24 @@ (.remove listener))))) :reagent-render (fn [{:keys [navigate index inset]}] - [react/animated-view {:style (tabs.styles/tabs-wrapper @keyboard-shown? @minimized-state inset)} - [react/animated-view {:style (tabs.styles/animated-container visible?)} - [react/view - {:style tabs.styles/tabs-container} - [react/view {:style tabs.styles/tabs} - (for [[route-index - {:keys [nav-stack accessibility-label count-subscription content]}] - tabs-list-data - - :let [{:keys [icon title]} content]] - ^{:key nav-stack} - [tab - {:icon icon - :label title - :on-press #(navigate (name nav-stack)) - :accessibility-label accessibility-label - :count-subscription count-subscription - :active? (= (str index) (str route-index)) - :nav-stack nav-stack}])]]] + [react/animated-view {:style (tabs.styles/tabs-wrapper @keyboard-shown? keyboard-visible) + :pointer-events (if @keyboard-shown? "none" "auto")} + [react/animated-view {:style (tabs.styles/space-handler inset) + :pointer-events "none"}] + [react/animated-view {:style (tabs.styles/animated-container visible-native inset)} + (for [[route-index + {:keys [nav-stack accessibility-label count-subscription content]}] + tabs-list-data + :let [{:keys [icon title]} content]] + ^{:key nav-stack} + [tab + {:icon icon + :label title + :on-press #(navigate (name nav-stack)) + :accessibility-label accessibility-label + :count-subscription count-subscription + :active? (= (str index) (str route-index)) + :nav-stack nav-stack}])] [react/view {:style (tabs.styles/ios-titles-cover inset)}]])}))) diff --git a/src/status_im/ui/components/tabbar/styles.cljs b/src/status_im/ui/components/tabbar/styles.cljs index 17758d64fa..90c854442f 100644 --- a/src/status_im/ui/components/tabbar/styles.cljs +++ b/src/status_im/ui/components/tabbar/styles.cljs @@ -15,7 +15,7 @@ (def tabs-diff (- tabs-height minimized-tabs-height)) (def minimized-tab-ratio - (/ minimized-tabs-height tabs-height)) + (/ tabs-height minimized-tabs-height)) (def counter {:right 0 @@ -81,30 +81,23 @@ {:color (if active? colors/blue colors/gray) :font-size 11}) -(styles/def tabs-container - {:height minimized-tabs-height - :align-self :stretch +(defn animated-container [visible? inset] + {:flex-direction :row + :shadow-radius 4 + :shadow-offset {:width 0 :height -5} + :shadow-opacity 0.3 + :shadow-color "rgba(0, 9, 26, 0.12)" + :elevation 8 :background-color :white - :ios {:shadow-radius 4 - :shadow-offset {:width 0 :height -5} - :shadow-opacity 0.3 - :shadow-color "rgba(0, 9, 26, 0.12)"} - :desktop {:background-color :white - :shadow-radius 4 - :shadow-offset {:width 0 :height -5} - :shadow-opacity 0.3 - :shadow-color "rgba(0, 9, 26, 0.12)"}}) - -(def tabs - {:align-self :stretch - :padding-horizontal 8 - :flex-direction :row}) - -(defn animated-container [visible?] - {:transform [{:translateY - (animation/interpolate visible? - {:inputRange [0 1] - :outputRange [(- tabs-diff) 0]})}]}) + :position :absolute + :left 0 + :right 0 + :height tabs-height + :bottom inset + :transform [{:translateY + (animation/interpolate visible? + {:inputRange [0 1] + :outputRange [0 tabs-diff]})}]}) (defn ios-titles-cover [inset] {:background-color :white @@ -115,11 +108,18 @@ :right 0 :left 0}) -(defn tabs-wrapper [keyboard minimized inset] - (merge {:padding-bottom inset - :elevation 8 - :padding-top (if minimized 0 tabs-diff) - :background-color :white} +(defn tabs-wrapper [keyboard visible] + (merge {:padding-horizontal 8 + :elevation 8 + :left 0 + :right 0 + :bottom 0 + :transform [{:translateY + (animation/interpolate visible + {:inputRange [0 1] + :outputRange [0 tabs-height]})}]} (when keyboard - {:position :absolute - :bottom (- tabs-height)}))) + {:position :absolute}))) + +(defn space-handler [inset] + {:height (+ inset minimized-tabs-height)}) diff --git a/src/status_im/ui/screens/group/views.cljs b/src/status_im/ui/screens/group/views.cljs index 685be25596..d14a1b4e4c 100644 --- a/src/status_im/ui/screens/group/views.cljs +++ b/src/status_im/ui/screens/group/views.cljs @@ -4,7 +4,6 @@ [clojure.string :as string] [re-frame.core :as re-frame] [status-im.i18n :as i18n] - [status-im.ui.components.tabbar.styles :as main-tabs.styles] [status-im.ui.components.styles :as components.styles] [status-im.constants :as constants] [status-im.utils.platform :as utils.platform] @@ -112,7 +111,12 @@ :label :t/invite-friends}])]) (views/defview bottom-container [{:keys [on-press disabled label accessibility-label]}] - [react/view {:style main-tabs.styles/tabs-container} + [react/view {:style {:height 52 + :elevation 8 + :shadow-radius 4 + :shadow-offset {:width 0 :height -5} + :shadow-opacity 0.3 + :shadow-color "rgba(0, 9, 26, 0.12)"}} [react/view {:style components.styles/flex}] [react/view {:style styles/bottom-container} [components.common/bottom-button diff --git a/src/status_im/ui/screens/pairing/views.cljs b/src/status_im/ui/screens/pairing/views.cljs index f6d47d30ef..6ffed47b64 100644 --- a/src/status_im/ui/screens/pairing/views.cljs +++ b/src/status_im/ui/screens/pairing/views.cljs @@ -4,7 +4,6 @@ [status-im.i18n :as i18n] [reagent.core :as reagent] [clojure.string :as string] - [status-im.ui.components.tabbar.styles :as main-tabs.styles] [status-im.ui.components.colors :as colors] [status-im.ui.components.icons.vector-icons :as icons] [status-im.utils.platform :as utils.platform] @@ -52,8 +51,12 @@ (defn footer [syncing] [react/touchable-highlight {:on-press (when-not @syncing synchronize-installations!) - ;; TODO: Inspect the need of coupling with tabbar here - :style main-tabs.styles/tabs-container} + :style {:height 52 + :elevation 8 + :shadow-radius 4 + :shadow-offset {:width 0 :height -5} + :shadow-opacity 0.3 + :shadow-color "rgba(0, 9, 26, 0.12)"}} [react/view {:style styles/footer-content} [react/text diff --git a/src/status_im/ui/screens/routing/browser_stack.cljs b/src/status_im/ui/screens/routing/browser_stack.cljs index 5c8b7fbaae..5de83aeee1 100644 --- a/src/status_im/ui/screens/routing/browser_stack.cljs +++ b/src/status_im/ui/screens/routing/browser_stack.cljs @@ -1,7 +1,8 @@ (ns status-im.ui.screens.routing.browser-stack (:require [status-im.ui.screens.routing.core :as navigation] [status-im.ui.screens.browser.open-dapp.views :as open-dapp] - [status-im.ui.screens.browser.views :as browser])) + [status-im.ui.screens.browser.views :as browser] + [status-im.ui.components.tabbar.styles :as tabbar.styles])) (defonce stack (navigation/create-stack)) @@ -9,6 +10,7 @@ [stack {:initial-route-name :open-dapp :header-mode :none} [{:name :open-dapp + :style {:padding-bottom tabbar.styles/tabs-diff} :component open-dapp/open-dapp} {:name :browser :back-handler :noop diff --git a/src/status_im/ui/screens/routing/chat_stack.cljs b/src/status_im/ui/screens/routing/chat_stack.cljs index a2b3f47c8b..001729bc88 100644 --- a/src/status_im/ui/screens/routing/chat_stack.cljs +++ b/src/status_im/ui/screens/routing/chat_stack.cljs @@ -7,6 +7,7 @@ [status-im.ui.screens.profile.group-chat.views :as profile.group-chat] [status-im.chat.models.loading :as chat.loading] [status-im.ui.screens.group.events :as group.events] + [status-im.ui.components.tabbar.styles :as tabbar.styles] [status-im.ui.screens.stickers.views :as stickers])) (defonce stack (navigation/create-stack)) @@ -16,6 +17,7 @@ :header-mode :none} [{:name :home :on-focus [::chat.loading/offload-all-messages] + :style {:padding-bottom tabbar.styles/tabs-diff} :component home/home} {:name :chat :on-focus [::chat.loading/load-messages] diff --git a/src/status_im/ui/screens/routing/core.cljs b/src/status_im/ui/screens/routing/core.cljs index 47e935d4ed..07506f7572 100644 --- a/src/status_im/ui/screens/routing/core.cljs +++ b/src/status_im/ui/screens/routing/core.cljs @@ -45,14 +45,19 @@ (remove-back-handler-listener "hardwareBackPress" on-back-press)))) #js []))) -(defn wrapped-screen-style [{:keys [insets]} insets-obj] +(defn wrapped-screen-style [{:keys [insets style]} insets-obj] (merge {:background-color :white :flex 1} + style (when (get insets :bottom) - {:padding-bottom (oget insets-obj "bottom")}) + {:padding-bottom (+ (oget insets-obj "bottom") + (get style :padding-bottom) + (get style :padding-vertical))}) (when (get insets :top true) - {:padding-top (oget insets-obj "top")}))) + {:padding-top (+ (oget insets-obj "top") + (get style :padding-top) + (get style :padding-vertical))}))) (defn presentation-type [{:keys [transition] :as opts}] (if (and platform/ios? (= transition :presentation-ios)) diff --git a/src/status_im/ui/screens/routing/profile_stack.cljs b/src/status_im/ui/screens/routing/profile_stack.cljs index 5664d2843a..ca98abe132 100644 --- a/src/status_im/ui/screens/routing/profile_stack.cljs +++ b/src/status_im/ui/screens/routing/profile_stack.cljs @@ -35,6 +35,7 @@ [status-im.ui.screens.profile.tribute-to-talk.views :as tr-to-talk] [status-im.ui.screens.hardwallet.pin.views :as hardwallet.pin] [status-im.ui.screens.hardwallet.settings.views :as hardwallet.settings] + [status-im.ui.components.tabbar.styles :as tabbar.styles] [status-im.ui.screens.routing.core :as navigation])) (defonce stack (navigation/create-stack)) @@ -43,6 +44,7 @@ [stack {:initial-route-name :my-profile :header-mode :none} [{:name :my-profile + :style {:padding-bottom tabbar.styles/tabs-diff} :component profile.user/my-profile} {:name :contacts-list :component contacts-list/contacts-list} diff --git a/src/status_im/ui/screens/routing/wallet_stack.cljs b/src/status_im/ui/screens/routing/wallet_stack.cljs index cbaa26f29f..51e5079dbe 100644 --- a/src/status_im/ui/screens/routing/wallet_stack.cljs +++ b/src/status_im/ui/screens/routing/wallet_stack.cljs @@ -10,6 +10,7 @@ [status-im.ui.screens.wallet.add-new.views :as add-account] [status-im.ui.screens.wallet.account-settings.views :as account-settings] [status-im.ui.screens.wallet.events :as wallet.events] + [status-im.ui.components.tabbar.styles :as tabbar.styles] [status-im.ui.screens.routing.core :as navigation])) (defonce stack (navigation/create-stack)) @@ -18,6 +19,7 @@ [stack {:initial-route-name :wallet :header-mode :none} [{:name :wallet + :style {:padding-bottom tabbar.styles/tabs-diff} :component wallet.accounts/accounts-overview} {:name :wallet-account :component wallet.account/account} diff --git a/test/cljs/status_im/react_native/js_dependencies.cljs b/test/cljs/status_im/react_native/js_dependencies.cljs index 224ca1caa6..6e709bdd23 100644 --- a/test/cljs/status_im/react_native/js_dependencies.cljs +++ b/test/cljs/status_im/react_native/js_dependencies.cljs @@ -66,3 +66,5 @@ (def react-navigation-stack #js {:createStackNavigator identity :TransitionPresets #js {:ModalPresentationIOS #js {}}}) (def react-navigation-bottom-tabs #js {:createBottomTabNavigator identity}) + +(def react-native-gesture-handler #js {})