diff --git a/src/status_im/accounts/login/core.cljs b/src/status_im/accounts/login/core.cljs index e3d19ec16e9..2c9c54d5cdb 100644 --- a/src/status_im/accounts/login/core.cljs +++ b/src/status_im/accounts/login/core.cljs @@ -254,7 +254,7 @@ {:db (assoc-in db [:accounts/login :password] password)} (navigation/navigate-to-cofx :progress nil) (user-login false)) - (navigation/navigate-to-clean cofx :login nil))) + (navigation/navigate-to-cofx cofx :login nil))) (re-frame/reg-fx :accounts.login/login diff --git a/src/status_im/chat/subs.cljs b/src/status_im/chat/subs.cljs index 897aa0e9654..46f7a16d859 100644 --- a/src/status_im/chat/subs.cljs +++ b/src/status_im/chat/subs.cljs @@ -6,7 +6,8 @@ [status-im.chat.constants :as chat.constants] [status-im.chat.db :as chat.db] [status-im.models.transactions :as transactions] - [status-im.utils.platform :as platform])) + [status-im.utils.platform :as platform] + [status-im.ui.components.bottom-bar.styles :as tabs-styles])) (re-frame/reg-sub ::chats :chats) (re-frame/reg-sub ::access-scope->command-id :access-scope->command-id) @@ -75,8 +76,11 @@ :<- [:get :keyboard-height] (fn [kb-height] (cond - (and platform/iphone-x? (> kb-height 0)) (- kb-height 34) - platform/ios? kb-height + (and platform/iphone-x? (> kb-height 0)) + (- kb-height 34 tabs-styles/tabs-height) + platform/ios? (- kb-height (if (> kb-height 0) + tabs-styles/tabs-height + 0)) :default 0))) (re-frame/reg-sub diff --git a/src/status_im/ui/components/bottom_bar/core.cljs b/src/status_im/ui/components/bottom_bar/core.cljs new file mode 100644 index 00000000000..8b94bc4e277 --- /dev/null +++ b/src/status_im/ui/components/bottom_bar/core.cljs @@ -0,0 +1,129 @@ +(ns status-im.ui.components.bottom-bar.core + (:require-macros [status-im.utils.views :as views]) + (:require + [status-im.ui.components.animation :as animation] + [status-im.ui.components.bottom-bar.styles :as tabs.styles] + [reagent.core :as reagent] + [status-im.ui.components.react :as react] + [status-im.utils.platform :as platform] + [status-im.ui.components.icons.vector-icons :as vector-icons] + [status-im.ui.components.common.common :as components.common] + [status-im.i18n :as i18n] + [status-im.ui.components.styles :as common.styles] + [re-frame.core :as re-frame])) + +(defn animate + ([visible duration to] + (animate visible duration to nil)) + ([visible duration to callback] + (animation/start + (animation/timing visible + {:toValue to + :duration duration + :useNativeDriver true}) + callback))) + +(def tabs-list-data + [{:view-id :chat-stack + :content {:title (i18n/label :t/home) + :icon :main-icons/home} + :count-subscription :chats/unread-messages-number + :accessibility-label :home-tab-button} + {:view-id :wallet-stack + :content {:title (i18n/label :t/wallet) + :icon :main-icons/wallet} + :count-subscription :get-wallet-unread-messages-number + :accessibility-label :wallet-tab-button} + {:view-id :profile-stack + :content {:title (i18n/label :t/profile) + :icon :main-icons/profile} + :count-subscription :get-profile-unread-messages-number + :accessibility-label :profile-tab-button}]) + +(defn- tab-content [{:keys [title icon]}] + (fn [active? count] + [react/view {:style tabs.styles/tab-container} + [react/view + [vector-icons/icon icon (tabs.styles/tab-icon active?)]] + [react/view + [react/text {:style (tabs.styles/tab-title active?)} + title]] + (when (pos? count) + [react/view tabs.styles/counter-container + [react/view tabs.styles/counter + [components.common/counter count]]])])) + +(def tabs-list (map #(update % :content tab-content) tabs-list-data)) + +(views/defview tab [view-id content active? accessibility-label count-subscription] + (views/letsubs [count [count-subscription]] + [react/touchable-highlight + (cond-> {:style common.styles/flex + :disabled active? + :on-press #(re-frame/dispatch [:navigate-to-tab view-id])} + accessibility-label + (assoc :accessibility-label accessibility-label)) + [react/view + [content active? count]]])) + +(defn tabs [current-view-id] + [react/view {:style tabs.styles/tabs-container} + (for [{:keys [content view-id accessibility-label count-subscription]} tabs-list] + ^{:key view-id} [tab view-id content (= view-id current-view-id) accessibility-label count-subscription])]) + +(defn tabs-animation-wrapper [visible? keyboard-shown? tab] + [react/animated-view + {:style {:height tabs.styles/tabs-height + :bottom 0 + :left 0 + :right 0 + :position (when keyboard-shown? :absolute) + :transform [{:translateY + (animation/interpolate + visible? + {:inputRange [0 1] + :outputRange [tabs.styles/tabs-height + 0]})}]}} + [react/safe-area-view [tabs tab]]]) + +(def disappearance-duration 150) +(def appearance-duration 100) + +(defn bottom-bar [_] + (let [keyboard-shown? (reagent/atom false) + visible? (animation/create-value 1) + listeners (atom [])] + (reagent/create-class + {:component-will-mount + (fn [] + (when platform/android? + (reset! + listeners + [(.addListener react/keyboard "keyboardDidShow" + (fn [] + (reset! keyboard-shown? true) + (animate visible? + disappearance-duration 0))) + (.addListener react/keyboard "keyboardDidHide" + (fn [] + (reset! keyboard-shown? false) + (animate visible? appearance-duration 1)))]))) + :component-will-unmount + (fn [] + (when (not-empty @listeners) + (doseq [listener @listeners] + (when listener + (.remove listener))))) + :reagent-render + (fn [args] + (let [idx (.. (:navigation args) + -state + -index) + tab (case idx + 0 :chat-stack + 1 :wallet-stack + 2 :profile-stack + :chat-stack)] + (if platform/ios? + [react/safe-area-view [tabs tab]] + [tabs-animation-wrapper visible? @keyboard-shown? tab])))}))) diff --git a/src/status_im/ui/screens/main_tabs/styles.cljs b/src/status_im/ui/components/bottom_bar/styles.cljs similarity index 90% rename from src/status_im/ui/screens/main_tabs/styles.cljs rename to src/status_im/ui/components/bottom_bar/styles.cljs index 6956cc478af..a9408e17370 100644 --- a/src/status_im/ui/screens/main_tabs/styles.cljs +++ b/src/status_im/ui/components/bottom_bar/styles.cljs @@ -1,6 +1,5 @@ -(ns status-im.ui.screens.main-tabs.styles - (:require [status-im.ui.components.styles :as styles] - [status-im.ui.components.colors :as colors] +(ns status-im.ui.components.bottom-bar.styles + (:require [status-im.ui.components.colors :as colors] [status-im.utils.platform :as platform]) (:require-macros [status-im.utils.styles :refer [defnstyle]])) diff --git a/src/status_im/ui/components/desktop/tabs.cljs b/src/status_im/ui/components/desktop/tabs.cljs index 4b470b24091..7bded21a0ed 100644 --- a/src/status_im/ui/components/desktop/tabs.cljs +++ b/src/status_im/ui/components/desktop/tabs.cljs @@ -5,7 +5,7 @@ [taoensso.timbre :as log] [status-im.ui.components.colors :as colors] [status-im.ui.components.react :as react] - [status-im.ui.screens.main-tabs.styles :as tabs.styles]) + [status-im.ui.components.bottom-bar.styles :as tabs.styles]) (:require-macros [status-im.utils.views :as views])) ;;TODO copy-pate with minimum modifications of status-react tabs diff --git a/src/status_im/ui/screens/group/views.cljs b/src/status_im/ui/screens/group/views.cljs index a03977a526e..d8722bcd418 100644 --- a/src/status_im/ui/screens/group/views.cljs +++ b/src/status_im/ui/screens/group/views.cljs @@ -4,7 +4,7 @@ [clojure.string :as string] [re-frame.core :as re-frame] [status-im.i18n :as i18n] - [status-im.ui.screens.main-tabs.styles :as main-tabs.styles] + [status-im.ui.components.bottom-bar.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] diff --git a/src/status_im/ui/screens/main_tabs/views.cljs b/src/status_im/ui/screens/main_tabs/views.cljs index d0ff0e5406b..2c8983723d9 100644 --- a/src/status_im/ui/screens/main_tabs/views.cljs +++ b/src/status_im/ui/screens/main_tabs/views.cljs @@ -1,84 +1,26 @@ (ns status-im.ui.screens.main-tabs.views (:require-macros [status-im.utils.views :as views]) - (:require [status-im.i18n :as i18n] - [re-frame.core :as re-frame] - [status-im.ui.components.icons.vector-icons :as vector-icons] - [status-im.ui.components.react :as react] + (:require [status-im.ui.components.react :as react] [status-im.ui.components.status-bar.view :as status-bar.view] [status-im.ui.components.styles :as common.styles] [status-im.ui.screens.home.views :as home] [status-im.ui.screens.wallet.main.views :as wallet.main] - [status-im.ui.screens.main-tabs.styles :as styles] - [status-im.ui.screens.profile.user.views :as profile.user] - [status-im.ui.components.common.common :as components.common])) - -(def tabs-list-data - [{:view-id :home - :content {:title (i18n/label :t/home) - :icon :main-icons/home} - :count-subscription :chats/unread-messages-number - :accessibility-label :home-tab-button} - {:view-id :wallet - :content {:title (i18n/label :t/wallet) - :icon :main-icons/wallet} - :count-subscription :get-wallet-unread-messages-number - :accessibility-label :wallet-tab-button} - {:view-id :my-profile - :content {:title (i18n/label :t/profile) - :icon :main-icons/profile} - :count-subscription :get-profile-unread-messages-number - :accessibility-label :profile-tab-button}]) - -(defn- tab-content [{:keys [title icon]}] - (fn [active? count] - [react/view {:style styles/tab-container} - [react/view - [vector-icons/icon icon (styles/tab-icon active?)]] - [react/view - [react/text {:style (styles/tab-title active?)} - title]] - (when (pos? count) - [react/view styles/counter-container - [react/view styles/counter - [components.common/counter count]]])])) - -(def tabs-list (map #(update % :content tab-content) tabs-list-data)) - -(views/defview tab [view-id content active? accessibility-label count-subscription] - (views/letsubs [count [count-subscription]] - [react/touchable-highlight - (cond-> {:style common.styles/flex - :disabled active? - :on-press #(re-frame/dispatch [:navigate-to-tab view-id])} - accessibility-label - (assoc :accessibility-label accessibility-label)) - [react/view - [content active? count]]])) - -(defn tabs [current-view-id] - [react/view {:style styles/tabs-container} - (for [{:keys [content view-id accessibility-label count-subscription]} tabs-list] - ^{:key view-id} [tab view-id content (= view-id current-view-id) accessibility-label count-subscription])]) + [status-im.ui.screens.profile.user.views :as profile.user])) (views/defview main-container [view-id] - (views/letsubs - [tab-bar-visible? [:tab-bar-visible?]] - ;; :should-component-update is called only when props are changed, - ;; that's why view-id is passed as a prop here. main-tabs component will be - ;; rendered while next screen from stack navigator is shown, so we have - ;; to prevent re-rendering to avoid no clause exception in case form - {:should-component-update - (fn [_ _ [_ new-view-id]] - (contains? #{:home :wallet :my-profile} new-view-id))} - [react/view common.styles/main-container - (case view-id - :home [home/home-wrapper] - :wallet [wallet.main/wallet] - :my-profile [profile.user/my-profile] - nil) - - (when tab-bar-visible? - [tabs view-id])])) + ;; :should-component-update is called only when props are changed, + ;; that's why view-id is passed as a prop here. main-tabs component will be + ;; rendered while next screen from stack navigator is shown, so we have + ;; to prevent re-rendering to avoid no clause exception in case form + {:should-component-update + (fn [_ _ [_ new-view-id]] + (contains? #{:home :wallet :my-profile} new-view-id))} + [react/view common.styles/main-container + (case view-id + :home [home/home-wrapper] + :wallet [wallet.main/wallet] + :my-profile [profile.user/my-profile] + nil)]) (defn main-tabs [view-id] [react/view common.styles/flex diff --git a/src/status_im/ui/screens/pairing/views.cljs b/src/status_im/ui/screens/pairing/views.cljs index ff101aa6d1a..34286b7e5b9 100644 --- a/src/status_im/ui/screens/pairing/views.cljs +++ b/src/status_im/ui/screens/pairing/views.cljs @@ -5,7 +5,7 @@ [reagent.core :as reagent] [clojure.string :as string] [status-im.utils.config :as config] - [status-im.ui.screens.main-tabs.styles :as main-tabs.styles] + [status-im.ui.components.bottom-bar.styles :as main-tabs.styles] [status-im.ui.components.colors :as colors] [status-im.ui.components.icons.vector-icons :as icons] [status-im.ui.screens.home.styles :as home.styles] diff --git a/src/status_im/ui/screens/routing/chat_stack.cljs b/src/status_im/ui/screens/routing/chat_stack.cljs index 3615fabe41f..c126ce8ae31 100644 --- a/src/status_im/ui/screens/routing/chat_stack.cljs +++ b/src/status_im/ui/screens/routing/chat_stack.cljs @@ -2,46 +2,44 @@ (:require [status-im.utils.config :as config])) (def chat-stack - {:name :chat-stack - :screens [{:name :chat-main-stack - :screens (cond-> - [:home - :chat - :profile - :new - :new-chat - :qr-scanner - :take-picture - :new-group - :add-participants-toggle-list - :contact-toggle-list - :group-chat-profile - :new-public-chat - :open-dapp - :dapp-description - :browser - :stickers - :stickers-pack - :login] - - config/hardwallet-enabled? - (concat [:hardwallet-connect :enter-pin])) - :config {:initialRouteName :home}} - :wallet-modal - :chat-modal - :show-extension-modal - :stickers-pack-modal - {:name :wallet-send-modal-stack - :screens [:wallet-send-transaction-modal - :wallet-transaction-sent-modal - :wallet-transaction-fee] - :config {:initialRouteName :wallet-send-transaction-modal}} - {:name :wallet-send-modal-stack-with-onboarding - :screens [:wallet-onboarding-setup-modal - :wallet-send-transaction-modal - :wallet-transaction-sent-modal - :wallet-transaction-fee] - :config {:initialRouteName :wallet-onboarding-setup-modal}} - :wallet-sign-message-modal] - :config {:mode :modal - :initialRouteName :chat-main-stack}}) + {:name :chat-stack + :screens [{:name :chat-main-stack + :screens (cond-> + [:home + :chat + :profile + :new + :new-chat + :qr-scanner + :take-picture + :new-group + :add-participants-toggle-list + :contact-toggle-list + :group-chat-profile + :new-public-chat + :open-dapp + :dapp-description + :browser + :stickers + :stickers-pack] + config/hardwallet-enabled? + (concat [:hardwallet-connect :enter-pin])) + :config {:initialRouteName :home}} + :wallet-modal + :chat-modal + :show-extension-modal + :stickers-pack-modal + {:name :wallet-send-modal-stack + :screens [:wallet-send-transaction-modal + :wallet-transaction-sent-modal + :wallet-transaction-fee] + :config {:initialRouteName :wallet-send-transaction-modal}} + {:name :wallet-send-modal-stack-with-onboarding + :screens [:wallet-onboarding-setup-modal + :wallet-send-transaction-modal + :wallet-transaction-sent-modal + :wallet-transaction-fee] + :config {:initialRouteName :wallet-onboarding-setup-modal}} + :wallet-sign-message-modal] + :config {:mode :modal + :initialRouteName :chat-main-stack}}) diff --git a/src/status_im/ui/screens/routing/core.cljs b/src/status_im/ui/screens/routing/core.cljs index a72c1f1c1ea..2463fac60e7 100644 --- a/src/status_im/ui/screens/routing/core.cljs +++ b/src/status_im/ui/screens/routing/core.cljs @@ -12,7 +12,8 @@ [status-im.ui.screens.routing.intro-login-stack :as intro-login-stack] [status-im.ui.screens.routing.chat-stack :as chat-stack] [status-im.ui.screens.routing.wallet-stack :as wallet-stack] - [status-im.ui.screens.routing.profile-stack :as profile-stack])) + [status-im.ui.screens.routing.profile-stack :as profile-stack] + [status-im.ui.components.bottom-bar.core :as bottom-bar])) (defn wrap [view-id component] "Wraps screen with main view and adds navigation-events component" @@ -65,6 +66,11 @@ routes (prepare-config config))) +(defn tab-navigator [routes config] + (nav-reagent/tab-navigator + routes + (prepare-config config))) + (declare stack-screens) (defn build-screen [screen] @@ -93,7 +99,10 @@ :else (nav-reagent/stack-screen (wrap screen-name screen-config)))] - [screen-name {:screen res}]))) + [screen-name (cond-> {:screen res} + (:navigation screen-config) + (assoc :navigationOptions + (:navigation screen-config)))]))) (defn stack-screens [screens-map] (->> screens-map @@ -103,10 +112,15 @@ (defn get-main-component [view-id] (log/debug :component view-id) (switch-navigator - (->> [(intro-login-stack/intro-login-stack view-id) - chat-stack/chat-stack - wallet-stack/wallet-stack - profile-stack/profile-stack] - (map build-screen) - (into {})) + (into {} + [(build-screen (intro-login-stack/intro-login-stack view-id)) + [:tabs + {:screen (tab-navigator + (->> [(build-screen chat-stack/chat-stack) + (build-screen wallet-stack/wallet-stack) + (build-screen profile-stack/profile-stack)] + (into {})) + {:initialRouteName :chat-stack + :tabBarComponent (reagent.core/reactify-component + bottom-bar/bottom-bar)})}]]) {:initialRouteName :intro-login-stack})) diff --git a/src/status_im/ui/screens/routing/intro_login_stack.cljs b/src/status_im/ui/screens/routing/intro_login_stack.cljs index 63eaa9f306b..859ab5c7c3d 100644 --- a/src/status_im/ui/screens/routing/intro_login_stack.cljs +++ b/src/status_im/ui/screens/routing/intro_login_stack.cljs @@ -17,8 +17,9 @@ :enter-pin :hardwallet-setup :hardwallet-success])) - :config (when + :config (if ;; add view-id here if you'd like that view to be ;; first view when app is started (#{:intro :login :progress :accounts} view-id) - {:initialRouteName view-id})}) + {:initialRouteName view-id} + {:initialRouteName :login})}) diff --git a/src/status_im/ui/screens/routing/profile_stack.cljs b/src/status_im/ui/screens/routing/profile_stack.cljs index abdeb441387..9885a012e25 100644 --- a/src/status_im/ui/screens/routing/profile_stack.cljs +++ b/src/status_im/ui/screens/routing/profile_stack.cljs @@ -27,10 +27,6 @@ :fleet-settings :currency-settings :backup-seed - :login - :create-account - :recover - :accounts :qr-scanner] config/hardwallet-enabled? diff --git a/src/status_im/ui/screens/routing/wallet_stack.cljs b/src/status_im/ui/screens/routing/wallet_stack.cljs index 8e3baf9d58f..0669108c8ad 100644 --- a/src/status_im/ui/screens/routing/wallet_stack.cljs +++ b/src/status_im/ui/screens/routing/wallet_stack.cljs @@ -22,7 +22,6 @@ :unsigned-transactions :transactions-history :wallet-transaction-details - :login :wallet-settings-hook] :config {:initialRouteName :wallet}} :selection-modal-screen diff --git a/src/status_im/ui/screens/wallet/navigation.cljs b/src/status_im/ui/screens/wallet/navigation.cljs index dc845d414cc..cb1f639b136 100644 --- a/src/status_im/ui/screens/wallet/navigation.cljs +++ b/src/status_im/ui/screens/wallet/navigation.cljs @@ -11,6 +11,13 @@ (re-frame/dispatch [:update-wallet]) (assoc-in db [:wallet :current-tab] 0)) +(defmethod navigation/preload-data! :wallet-stack + [db _] + ;;TODO(goranjovic) - get rid of this preload hook completely + (re-frame/dispatch [:wallet.ui/pull-to-refresh]) + (re-frame/dispatch [:update-wallet]) + (assoc-in db [:wallet :current-tab] 0)) + (defmethod navigation/preload-data! :wallet-modal [db _] ;;TODO(goranjovic) - get rid of this preload hook completely diff --git a/src/status_im/ui/screens/wallet/transactions/styles.cljs b/src/status_im/ui/screens/wallet/transactions/styles.cljs index d0f87150111..685f806a9c6 100644 --- a/src/status_im/ui/screens/wallet/transactions/styles.cljs +++ b/src/status_im/ui/screens/wallet/transactions/styles.cljs @@ -1,7 +1,7 @@ (ns status-im.ui.screens.wallet.transactions.styles (:require-macros [status-im.utils.styles :refer [defnstyle defstyle]]) (:require [status-im.ui.components.colors :as colors] - [status-im.ui.screens.main-tabs.styles :as tabs.styles])) + [status-im.ui.components.bottom-bar.styles :as tabs.styles])) (defnstyle tab [active?] {:flex 1 diff --git a/test/appium/tests/atomic/chats/test_chats_management.py b/test/appium/tests/atomic/chats/test_chats_management.py index 493747ca7ca..32fd72c35d5 100644 --- a/test/appium/tests/atomic/chats/test_chats_management.py +++ b/test/appium/tests/atomic/chats/test_chats_management.py @@ -174,11 +174,11 @@ def test_add_contact_from_public_chat(self): username = chat_element.username.text chat_element.member_photo.click() for element in [chat_1.contact_profile_picture, + chat_1.element_by_text(username, 'text'), chat_1.add_to_contacts, chat_1.profile_send_message, chat_1.profile_send_transaction, - chat_1.profile_address_text, - chat_1.element_by_text(username, 'text')]: + chat_1.profile_address_text]: if not element.scroll_to_element(): self.errors.append('%s is not visible' % element.name) chat_1.add_to_contacts.click() diff --git a/test/appium/tests/atomic/chats/test_commands.py b/test/appium/tests/atomic/chats/test_commands.py index bc6607069bd..f0b909f4b66 100644 --- a/test/appium/tests/atomic/chats/test_commands.py +++ b/test/appium/tests/atomic/chats/test_commands.py @@ -25,7 +25,7 @@ def test_network_mismatch_for_send_request_commands(self): device_1_wallet_view = device_1_home.wallet_button.click() device_1_wallet_view.set_up_wallet() - device_1_wallet_view.get_back_to_home_view() + device_1_wallet_view.home_button.click() public_key = device_2_home.get_public_key() device_2_profile = device_2_home.get_profile_view() diff --git a/test/appium/views/base_view.py b/test/appium/views/base_view.py index 9ef6a3c3072..f82b3139457 100644 --- a/test/appium/views/base_view.py +++ b/test/appium/views/base_view.py @@ -472,7 +472,8 @@ def public_key_to_address(self, public_key): def get_back_to_home_view(self): counter = 0 - while not self.home_button.is_element_displayed(2): + from views.home_view import PlusButton + while not PlusButton(self.driver).is_element_displayed(2): try: if counter >= 5: return