new bottom tab (react-navigation)

This commit is contained in:
Roman Volosovskyi 2019-02-18 18:35:42 +02:00
parent 0fe4ac516f
commit b8d4f2911b
No known key found for this signature in database
GPG Key ID: 0238A4B5ECEE70DE
18 changed files with 236 additions and 146 deletions

View File

@ -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

View File

@ -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

View File

@ -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])))})))

View File

@ -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]]))

View File

@ -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

View File

@ -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]

View File

@ -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

View File

@ -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]

View File

@ -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}})

View File

@ -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}))

View File

@ -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})})

View File

@ -27,10 +27,6 @@
:fleet-settings
:currency-settings
:backup-seed
:login
:create-account
:recover
:accounts
:qr-scanner]
config/hardwallet-enabled?

View File

@ -22,7 +22,6 @@
:unsigned-transactions
:transactions-history
:wallet-transaction-details
:login
:wallet-settings-hook]
:config {:initialRouteName :wallet}}
:selection-modal-screen

View File

@ -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

View File

@ -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

View File

@ -174,11 +174,11 @@ class TestChatManagementMultipleDevice(MultipleDeviceTestCase):
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()

View File

@ -25,7 +25,7 @@ class TestCommandsMultipleDevices(MultipleDeviceTestCase):
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()

View File

@ -472,7 +472,8 @@ class BaseView(object):
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