diff --git a/resources/images/icons/messages-new@2x.png b/resources/images/icons/messages-new@2x.png new file mode 100644 index 00000000000..dc88d8dfd4d Binary files /dev/null and b/resources/images/icons/messages-new@2x.png differ diff --git a/resources/images/icons/messages-new@3x.png b/resources/images/icons/messages-new@3x.png new file mode 100644 index 00000000000..62f1a70604c Binary files /dev/null and b/resources/images/icons/messages-new@3x.png differ diff --git a/resources/images/icons/wallet-new@2x.png b/resources/images/icons/wallet-new@2x.png new file mode 100644 index 00000000000..7b9d408ad4c Binary files /dev/null and b/resources/images/icons/wallet-new@2x.png differ diff --git a/resources/images/icons/wallet-new@3x.png b/resources/images/icons/wallet-new@3x.png new file mode 100644 index 00000000000..e154e4ed3ae Binary files /dev/null and b/resources/images/icons/wallet-new@3x.png differ diff --git a/src/mocks/js_dependencies.cljs b/src/mocks/js_dependencies.cljs index c3c05cf24a5..76f1d23537e 100644 --- a/src/mocks/js_dependencies.cljs +++ b/src/mocks/js_dependencies.cljs @@ -47,6 +47,7 @@ :ValueXY (fn []) :View {} :FlatList {} + :Image {} :ScrollView {} :Text {}} :Easing {:bezier (fn []) diff --git a/src/quo/react_native.cljs b/src/quo/react_native.cljs index ab87e70d36c..8219ad5fc3b 100644 --- a/src/quo/react_native.cljs +++ b/src/quo/react_native.cljs @@ -74,6 +74,9 @@ (def animated-view (reagent/adapt-react-class (.-View ^js animated))) +(def animated-image-view + (reagent/adapt-react-class (.-Image ^js animated))) + (def ui-manager (.-UIManager ^js rn)) (def layout-animation (.-LayoutAnimation ^js rn)) @@ -102,6 +105,8 @@ :property (:opacity layout-animation-properties)} :delete #js {:type (:ease-in-ease-out layout-animation-types) :property (:opacity layout-animation-properties)}}}) +(defonce enable-layout-animations + (when platform/android? (.setLayoutAnimationEnabledExperimental ^js ui-manager true))) (def activity-indicator (reagent/adapt-react-class (.-ActivityIndicator ^js rn))) diff --git a/src/quo2/components/button.cljs b/src/quo2/components/button.cljs index 4371071b65b..d30f4e4669d 100644 --- a/src/quo2/components/button.cljs +++ b/src/quo2/components/button.cljs @@ -105,7 +105,7 @@ [_ _] (let [pressed (reagent/atom false)] (fn [{:keys [on-press disabled type size before after above width - on-long-press accessibility-label icon] + on-long-press accessibility-label icon style] :or {type :primary size 40}} children] @@ -126,17 +126,19 @@ {:on-press-out (fn [] (reset! pressed nil))}) - [rn/view {:style (style-container - type - size - disabled - (get background-color state) - (get border-color state) - icon - above - width - before - after)} + [rn/view {:style (merge + (style-container + type + size + disabled + (get background-color state) + (get border-color state) + icon + above + width + before + after) + style)} (when above [rn/view [quo2.icons/icon above {:container-style {:margin-bottom 2} @@ -168,4 +170,4 @@ [quo2.icons/icon after {:container-style {:margin-left 4 :margin-right (if (= size 40) 12 8)} :color icon-color - :size icon-size}]])]])))) \ No newline at end of file + :size icon-size}]])]])))) diff --git a/src/quo2/components/text.cljs b/src/quo2/components/text.cljs index a094cf4c9bd..26edec109df 100644 --- a/src/quo2/components/text.cljs +++ b/src/quo2/components/text.cljs @@ -29,5 +29,6 @@ (let [this (reagent/current-component) props (reagent/props this) style (text-style props)] - (into [rn/text {:style style}] - (reagent/children this)))) \ No newline at end of file + (into [rn/text (merge {:style style} + (dissoc props :style :size :align :weight :color))] + (reagent/children this)))) diff --git a/src/quo2/foundations/colors.cljs b/src/quo2/foundations/colors.cljs index c229ee76e87..5e1f604a641 100644 --- a/src/quo2/foundations/colors.cljs +++ b/src/quo2/foundations/colors.cljs @@ -34,6 +34,13 @@ (def neutral-50-opa-30 (alpha neutral-50 0.3)) (def neutral-50-opa-40 (alpha neutral-50 0.4)) +;;70 with transparency +(def neutral-70-opa-60 (alpha neutral-70 0.6)) +(def neutral-70-opa-70 (alpha neutral-70 0.7)) +(def neutral-70-opa-80 (alpha neutral-70 0.8)) +(def neutral-70-opa-90 (alpha neutral-70 0.9)) +(def neutral-70-opa-95 (alpha neutral-70 0.95)) + ;;80 with transparency (def neutral-80-opa-60 (alpha neutral-80 0.6)) (def neutral-80-opa-70 (alpha neutral-80 0.7)) @@ -176,5 +183,15 @@ (def info-50-opa-30 (alpha info-50 0.3)) (def info-50-opa-40 (alpha info-50 0.4)) +;;;;Switcher +(def switcher-background "#040B14") + +;;switcher-screen with transparency +(def switcher-background-opa-60 (alpha switcher-background 0.6)) +(def switcher-background-opa-70 (alpha switcher-background 0.7)) +(def switcher-background-opa-80 (alpha switcher-background 0.8)) +(def switcher-background-opa-90 (alpha switcher-background 0.9)) +(def switcher-background-opa-95 (alpha switcher-background 0.95)) + (defn theme-colors [light dark] - (if (theme/dark?) dark light)) \ No newline at end of file + (if (theme/dark?) dark light)) diff --git a/src/quo2/screens/main.cljs b/src/quo2/screens/main.cljs index b92da32453f..33260842873 100644 --- a/src/quo2/screens/main.cljs +++ b/src/quo2/screens/main.cljs @@ -58,7 +58,7 @@ [rn/view (for [{:keys [name]} screens] ^{:key name} - [rn/touchable-opacity {:on-press #(re-frame/dispatch [:navigate-to name])} + [rn/touchable-opacity {:on-press #(re-frame/dispatch [:navigate-to-nav2 name])} [rn/view {:style {:padding-vertical 8}} [quo/text (str "Preview " name)]]])]]) diff --git a/src/status_im/chat/models.cljs b/src/status_im/chat/models.cljs index 0b8bda52e12..915ae52a54d 100644 --- a/src/status_im/chat/models.cljs +++ b/src/status_im/chat/models.cljs @@ -10,6 +10,7 @@ [quo.design-system.colors :as colors] [status-im.constants :as constants] [status-im.navigation :as navigation] + [status-im.navigation2 :as navigation2] [status-im.utils.clocks :as utils.clocks] [status-im.utils.fx :as fx] [status-im.utils.utils :as utils] @@ -259,6 +260,15 @@ (navigation/pop-to-root-tab % :chat-stack)) (navigation/navigate-to-cofx :chat nil)))) +(fx/defn navigate-to-chat-nav2 + "Takes coeffects map and chat-id, returns effects necessary for navigation and preloading data" + {:events [:chat.ui/navigate-to-chat-nav2]} + [{db :db :as cofx} chat-id from-switcher?] + (fx/merge cofx + {:db (assoc db :current-chat-id chat-id)} + (preload-chat-data chat-id) + (navigation2/navigate-to-nav2 :chat chat-id nil from-switcher?))) + (fx/defn handle-clear-history-response {:events [::history-cleared]} [{:keys [db]} chat-id response] diff --git a/src/status_im/events.cljs b/src/status_im/events.cljs index 2a9945ece97..ceebaaff9bf 100644 --- a/src/status_im/events.cljs +++ b/src/status_im/events.cljs @@ -63,7 +63,9 @@ [status-im.multiaccounts.login.core :as login.core] [status-im.signing.core :as signing] status-im.wallet-connect.core - status-im.wallet-connect-legacy.core)) + status-im.wallet-connect-legacy.core + status-im.navigation2 + status-im.navigation2.core)) (re-frame/reg-fx :dismiss-keyboard diff --git a/src/status_im/navigation2.cljs b/src/status_im/navigation2.cljs new file mode 100644 index 00000000000..98e17dab6cd --- /dev/null +++ b/src/status_im/navigation2.cljs @@ -0,0 +1,49 @@ +(ns status-im.navigation2 + (:require [status-im.utils.fx :as fx] + [status-im.utils.datetime :as datetime])) + +(def parent-stack (atom :home-stack)) + +(fx/defn init-root-nav2 + {:events [:init-root-nav2]} + [_ root-id] + {:init-root-fx-nav2 root-id}) + +(fx/defn open-modal-nav2 + {:events [:open-modal-nav2]} + [_ modal] + {:open-modal-fx-nav2 modal}) + +(fx/defn close-modal-nav2 + {:events [:close-modal-nav2]} + [_ modal] + {:close-modal-fx-nav2 modal}) + +(defn navigate-from-home-stack [go-to-view-id id db] + (reset! parent-stack go-to-view-id) + {:navigate-to-fx-nav2 [go-to-view-id id] + :db (assoc-in db [:navigation2/navigation2-stacks id] {:type go-to-view-id + :id id + :clock (datetime/timestamp)})}) + +(defn navigate-from-switcher [go-to-view-id id db from-home?] + (reset! parent-stack go-to-view-id) + {:navigate-from-switcher-fx [go-to-view-id id from-home?] + :db (assoc-in db [:navigation2/navigation2-stacks id] {:type go-to-view-id + :id id + :clock (datetime/timestamp)})}) + +(fx/defn navigate-to-nav2 + {:events [:navigate-to-nav2]} + [{:keys [db] :as cofx} go-to-view-id id screen-params from-switcher?] + (let [view-id (:view-id db) + stacks (:navigation2/navigation2-stacks db) + from-home? (= view-id :home-stack)] + (if from-switcher? + (navigate-from-switcher go-to-view-id id db from-home?) + (if from-home? + (navigate-from-home-stack go-to-view-id id db) + ;; TODO(parvesh) - new stacks created from other screens should be stacked on current stack, instead of creating new entry + (navigate-from-home-stack go-to-view-id id db))))) + + diff --git a/src/status_im/navigation2/core.cljs b/src/status_im/navigation2/core.cljs new file mode 100644 index 00000000000..09e77e5920b --- /dev/null +++ b/src/status_im/navigation2/core.cljs @@ -0,0 +1,77 @@ +(ns status-im.navigation2.core + (:require [re-frame.core :as re-frame] + [status-im.ui.screens.views :as views] + [status-im.navigation2.utils :as nav2-utils] + [status-im.navigation2.roots :as roots] + [status-im.navigation.roots :as nav-roots] + ["react-native-navigation" :refer (Navigation)])) + +(def tab-key-idx {:home 0 + :communities 1 + :wallet 2 + :browser 3}) + +(defonce set-navigation-default-options + (.setDefaultOptions Navigation (clj->js {:options {:topBar {:visible false}}}))) + +;; TODO (parvesh) - improve open-modal and close-modal +(defn open-modal [comp] + (.showModal Navigation + (clj->js {:stack {:children + [{:component + {:name comp + :id comp + :options {:topBar {:visible false}}}}]}}))) + +(defn close-modal [_]) + +(defn close-all-modals [] + (.dismissAllModals Navigation)) + +(defn get-id [comp id] + (str comp "-" id)) + +(defn get-options [show-topbar? options] + (if show-topbar? + (merge options + (nav-roots/status-bar-options) + (nav-roots/merge-top-bar (nav-roots/topbar-options) options)) + {:topBar {:visible false}})) + +(defn change-stack-root [[comp _]] + (let [{:keys [options]} (get views/screens comp)] + (.setStackRoot Navigation + (name comp) + (clj->js {:stack {:id comp + :children [{:component {:id comp + :name comp + :options (get-options false options)}}]}})))) + +(defn navigate [[comp _]] + (let [{:keys [options]} (get views/screens comp)] + (reset! nav2-utils/container-stack-view-id comp) + (.push Navigation + (name :home-stack) + (clj->js {:stack {:id comp + :children [{:component {:id comp + :name comp + :options (get-options false options)}}]}})))) + +(defn navigate-from-switcher [[comp id from-home?]] + (if from-home? + (navigate [comp id]) + (change-stack-root [comp id]))) + +(re-frame/reg-fx + :init-root-fx-nav2 + (fn [new-root-id] + (reset! nav2-utils/container-stack-view-id new-root-id) + (.setRoot Navigation (clj->js (get (roots/roots) new-root-id))))) + +(re-frame/reg-fx :open-modal-fx-nav2 open-modal) + +(re-frame/reg-fx :close-modal-fx-nav2 close-modal) + +(re-frame/reg-fx :navigate-to-fx-nav2 navigate) + +(re-frame/reg-fx :navigate-from-switcher-fx navigate-from-switcher) diff --git a/src/status_im/navigation2/home_stack.cljs b/src/status_im/navigation2/home_stack.cljs new file mode 100644 index 00000000000..5ae554a9e83 --- /dev/null +++ b/src/status_im/navigation2/home_stack.cljs @@ -0,0 +1,27 @@ +(ns status-im.navigation2.home-stack + (:require [quo.react-native :as rn] + [quo2.screens.main :as quo2.preview] + [status-im.switcher.switcher :as switcher] + [status-im.ui.screens.home.views :as home] + [status-im.switcher.constants :as switcher-constants] + [status-im.ui.screens.browser.empty-tab.views :as empty-tab] + [status-im.ui.screens.wallet.accounts.views :as wallet.accounts] + [status-im.switcher.bottom-tabs :as bottom-tabs])) + +;; TODO(parvesh) - improve stack changing performance (load all stacks at once) +(defn stack-view [] + (let [{:keys [width height]} (switcher-constants/dimensions)] + ;; bottom-tabs-height (switcher-constants/bottom-tabs-height)] + [rn/view {:style {:width width + :height (- height 80)}} ;; TODO(parvesh) - add height for ios + (case @bottom-tabs/selected-tab-id + :chats-stack [home/home] + :communities-stack [quo2.preview/main-screen] + :wallet-stack [wallet.accounts/accounts-overview] + :browser-stack [empty-tab/empty-tab])])) + +(defn home [] + [:<> + [stack-view] + [bottom-tabs/bottom-tabs] + [switcher/switcher :home-stack]]) diff --git a/src/status_im/navigation2/roots.cljs b/src/status_im/navigation2/roots.cljs new file mode 100644 index 00000000000..637aa04063e --- /dev/null +++ b/src/status_im/navigation2/roots.cljs @@ -0,0 +1,9 @@ +(ns status-im.navigation2.roots) + +(defn roots [] + {:home-stack + {:root + {:stack {:id :home-stack + :children [{:component {:name :home-stack + :id :home-stack + :options {:topBar {:visible false}}}}]}}}}) diff --git a/src/status_im/navigation2/screens.cljs b/src/status_im/navigation2/screens.cljs new file mode 100644 index 00000000000..726916dbbb4 --- /dev/null +++ b/src/status_im/navigation2/screens.cljs @@ -0,0 +1,6 @@ +(ns status-im.navigation2.screens + (:require [status-im.navigation2.home-stack :as home-stack])) + +(def screens [{:name :home-stack + :insets {:top false} + :component home-stack/home}]) diff --git a/src/status_im/navigation2/stack_with_switcher.cljs b/src/status_im/navigation2/stack_with_switcher.cljs new file mode 100644 index 00000000000..7e9377b631f --- /dev/null +++ b/src/status_im/navigation2/stack_with_switcher.cljs @@ -0,0 +1,7 @@ +(ns status-im.navigation2.stack-with-switcher + (:require [status-im.switcher.switcher :as switcher])) + +(defn overlap-stack [comp view-id] + [:<> + [comp] + [switcher/switcher view-id]]) diff --git a/src/status_im/navigation2/utils.cljs b/src/status_im/navigation2/utils.cljs new file mode 100644 index 00000000000..aebe9a65dd6 --- /dev/null +++ b/src/status_im/navigation2/utils.cljs @@ -0,0 +1,3 @@ +(ns status-im.navigation2.utils) + +(defonce container-stack-view-id (atom nil)) diff --git a/src/status_im/subs.cljs b/src/status_im/subs.cljs index 702fb4e46f9..c399253c134 100644 --- a/src/status_im/subs.cljs +++ b/src/status_im/subs.cljs @@ -87,6 +87,7 @@ (reg-root-key-sub :home-items-show-number :home-items-show-number) (reg-root-key-sub :waku/v2-peer-stats :peer-stats) (reg-root-key-sub :visibility-status-updates :visibility-status-updates) +(reg-root-key-sub :navigation2/navigation2-stacks :navigation2/navigation2-stacks) ;;NOTE this one is not related to ethereum network ;; it is about cellular network/ wifi network @@ -2957,3 +2958,19 @@ :<- [:bookmarks] (fn [bookmarks] (into {} (remove #(:removed (second %)) bookmarks)))) + +;; NAVIGATION2 + + +(re-frame/reg-sub + :navigation2/switcher-cards + :<- [:navigation2/navigation2-stacks] + (fn [stacks [_ toggle-switcher-screen]] + (sort-by :clock > + (reduce (fn [acc stack-vector] + (let [{:keys [type clock id]} (get stack-vector 1)] + (conj acc {:type type + :clock clock + :id id + :toggle-switcher-screen toggle-switcher-screen}))) + '() stacks)))) diff --git a/src/status_im/switcher/animation.cljs b/src/status_im/switcher/animation.cljs new file mode 100644 index 00000000000..dffc7d75d56 --- /dev/null +++ b/src/status_im/switcher/animation.cljs @@ -0,0 +1,47 @@ +(ns status-im.switcher.animation + (:require [quo.react-native :as rn] + [reagent.core :as reagent] + [status-im.switcher.constants :as constants] + [status-im.ui.components.animation :as anim])) + +(def bottom-tabs-opacity (anim/create-value 1)) +(def bottom-tabs-position (anim/create-value 0)) + +;; TODO(parvesh): Use 300, after using dispatch-later for opening card(otherwise pending animation issue) +;; or OnAnimationEnd +(def layout-animation #js {:duration 250 + :create #js {:type (:ease-in-ease-out rn/layout-animation-types) + :property (:scale-xy rn/layout-animation-properties)} + :update #js {:type (:ease-in-ease-out rn/layout-animation-types) + :property (:scale-xy rn/layout-animation-properties)} + :delete #js {:type (:ease-in-ease-out rn/layout-animation-types) + :property (:scale-xy rn/layout-animation-properties)}}) + +(defn animate-layout [show? anim-values] + (let [{:keys [width height]} (constants/dimensions) + target-radius (- (max width height) + constants/switcher-button-radius)] + (rn/configure-next layout-animation) + (reset! (:switcher-screen-radius anim-values) (if show? target-radius 1)) + (reagent/flush))) + +(defn timing-animation [property toValue] + (anim/timing property {:toValue toValue + :duration 300 + :useNativeDriver true})) + +(defn animate-components [show? view-id anim-values] + (anim/start + (anim/parallel + (into + [(timing-animation (:switcher-button-opacity anim-values) (if show? 0 1)) + (timing-animation (:switcher-close-button-icon-opacity anim-values) (if show? 1 0)) + (timing-animation (:switcher-close-button-background-opacity anim-values) (if show? 0.2 0))] + (when (= view-id :home-stack) + [(timing-animation bottom-tabs-opacity (if show? 0 1)) + (timing-animation bottom-tabs-position (if show? (constants/bottom-tabs-height) 0))]))))) + +(defn animate [show? view-id anim-values] + (reagent/flush) + (animate-layout show? anim-values) + (animate-components show? view-id anim-values)) diff --git a/src/status_im/switcher/bottom_tabs.cljs b/src/status_im/switcher/bottom_tabs.cljs new file mode 100644 index 00000000000..32465a15bf1 --- /dev/null +++ b/src/status_im/switcher/bottom_tabs.cljs @@ -0,0 +1,30 @@ +(ns status-im.switcher.bottom-tabs + (:require [quo.react-native :as rn] + [reagent.core :as reagent] + [status-im.switcher.styles :as styles] + [status-im.ui.components.icons.icons :as icons])) + +(def selected-tab-id (reagent/atom :chats-stack)) + +(defn bottom-tab-pressed [tab-id] + (when-not (= tab-id @selected-tab-id) + (reset! selected-tab-id tab-id))) + +;; TODO(parvesh) - reimplement tab with counter, once design is complete +;; (implement natively, for performance improvement) +(defn bottom-tab [icon tab-id] + [rn/touchable-opacity {:style {:padding 15} + :active-opacity 1 + :on-press #(bottom-tab-pressed tab-id)} + [icons/icon icon (styles/bottom-tab-icon + (if (= tab-id @selected-tab-id) + :bottom-tabs-selected-tab + :bottom-tabs-non-selected-tab))]]) + +(defn bottom-tabs [] + [rn/animated-view {:style (styles/bottom-tabs)} + [bottom-tab :main-icons/messages-new :chats-stack] + [bottom-tab :main-icons/communities :communities-stack] + [rn/view {:width 10}] + [bottom-tab :main-icons/wallet-new :wallet-stack] + [bottom-tab :main-icons/browser :browser-stack]]) diff --git a/src/status_im/switcher/cards/messaging_card.cljs b/src/status_im/switcher/cards/messaging_card.cljs new file mode 100644 index 00000000000..642aa810ee9 --- /dev/null +++ b/src/status_im/switcher/cards/messaging_card.cljs @@ -0,0 +1,30 @@ +(ns status-im.switcher.cards.messaging-card + (:require [quo.react-native :as rn] + [quo2.components.text :as text] + [status-im.constants :as constants] + [quo2.components.button :as button] + [status-im.utils.handlers :refer [>evt evt [:chat.ui/navigate-to-chat-nav2 id true])) + +;; TODO - add last message for other content types +(defn last-message [{:keys [content content-type]}] + (cond + (= constants/content-type-text content-type) + [text/text (styles/messaging-card-last-message-text-props) (:text content)])) + +(defn card [{:keys [id toggle-switcher-screen]}] + (let [chat ( + [switcher-screen switcher-opened? view-id anim-values] + [switcher-button switcher-opened? view-id anim-values]])) diff --git a/src/status_im/switcher/switcher_container.cljs b/src/status_im/switcher/switcher_container.cljs new file mode 100644 index 00000000000..136ae0bc0b6 --- /dev/null +++ b/src/status_im/switcher/switcher_container.cljs @@ -0,0 +1,34 @@ +(ns status-im.switcher.switcher-container + (:require [quo.react-native :as rn] + status-im.switcher.cards.messaging-card + [status-im.switcher.styles :as styles] + [status-im.utils.handlers :refer [