From 1ba7c338682dc2fc2f9e2b047025f97e3b83a7f5 Mon Sep 17 00:00:00 2001 From: Roman Volosovskyi Date: Wed, 20 Dec 2017 20:45:42 +0200 Subject: [PATCH] improve navigation duration --- src/status_im/chat/screen.cljs | 25 ++- src/status_im/chat/styles/screen.cljs | 9 + .../ui/components/chat_icon/screen.cljs | 11 +- src/status_im/ui/components/react.cljs | 51 ++++- src/status_im/ui/screens/main_tabs/views.cljs | 30 ++- src/status_im/ui/screens/views.cljs | 75 ++++++- .../wallet/choose_recipient/views.cljs | 22 +- .../ui/screens/wallet/request/views.cljs | 4 +- src/status_im/ui/screens/wallet/styles.cljs | 6 + .../ui/screens/wallet/transactions/views.cljs | 6 +- src/status_im/utils/views.clj | 207 ++++++++++++++++++ 11 files changed, 409 insertions(+), 37 deletions(-) diff --git a/src/status_im/chat/screen.cljs b/src/status_im/chat/screen.cljs index 29ac8782fb..8ef204a50f 100644 --- a/src/status_im/chat/screen.cljs +++ b/src/status_im/chat/screen.cljs @@ -73,6 +73,24 @@ :group-chat group-chat :current-public-key current-public-key)]) + +(defview messages-view-animation [message-view] + ;; smooths out appearance of message-view + (letsubs [opacity (anim/create-value 0) + duration (if platform/android? 100 200) + timeout (if platform/android? 50 0)] + {:component-did-mount (fn [component] + (anim/start + (anim/anim-sequence + [(anim/anim-delay timeout) + (anim/spring opacity {:toValue 1 + :duration duration})])))} + [react/with-activity-indicator + {:style style/message-view-preview + :preview [react/view style/message-view-preview]} + [react/animated-view {:style (style/message-view-animated opacity)} + message-view]])) + (defview messages-view [group-chat] (letsubs [messages [:get-current-chat-messages] current-public-key [:get-current-public-key]] @@ -91,7 +109,8 @@ show-actions? [:get-current-chat-ui-prop :show-actions?] show-bottom-info? [:get-current-chat-ui-prop :show-bottom-info?] show-emoji? [:get-current-chat-ui-prop :show-emoji?] - layout-height [:get :layout-height]] + layout-height [:get :layout-height] + current-view [:get :view-id]] {:component-did-mount #(re-frame/dispatch [:check-and-open-dapp!]) :component-will-unmount #(re-frame/dispatch [:set-chat-ui-props {:show-emoji? false}])} [react/view {:style style/chat-view @@ -100,7 +119,9 @@ (when (not= height layout-height) (re-frame/dispatch [:set-layout-height height]))))} [chat-toolbar] - [messages-view group-chat] + (when (= :chat current-view) + [messages-view-animation + [messages-view group-chat]]) [input/container {:text-empty? (string/blank? input-text)}] (when show-actions? [actions/actions-view]) diff --git a/src/status_im/chat/styles/screen.cljs b/src/status_im/chat/styles/screen.cljs index ae06d0d9fb..809b31e787 100644 --- a/src/status_im/chat/styles/screen.cljs +++ b/src/status_im/chat/styles/screen.cljs @@ -204,3 +204,12 @@ (defstyle actions-list-view {:ios {:border-bottom-color component.styles/color-gray3 :border-bottom-width 0.5}}) + +(def message-view-preview + {:flex 1 + :align-items :center + :justify-content :center}) + +(defn message-view-animated [opacity] + {:opacity opacity + :flex 1}) diff --git a/src/status_im/ui/components/chat_icon/screen.cljs b/src/status_im/ui/components/chat_icon/screen.cljs index 2848f7a253..01e1295b44 100644 --- a/src/status_im/ui/components/chat_icon/screen.cljs +++ b/src/status_im/ui/components/chat_icon/screen.cljs @@ -13,11 +13,12 @@ (first name)]]) (defn chat-icon [photo-path {:keys [size accessibility-label]}] - (let [photo (if (string/starts-with? photo-path "contacts://") - (->> (string/replace photo-path #"contacts://" "") - (keyword) - (get resources/contacts)) - {:uri photo-path})] + (let [photo (when photo-path + (if (string/starts-with? photo-path "contacts://") + (->> (string/replace photo-path #"contacts://" "") + (keyword) + (get resources/contacts)) + {:uri photo-path}))] [react/image {:source photo :style (styles/image-style size) :accessibility-label (or accessibility-label :chat-icon)}])) diff --git a/src/status_im/ui/components/react.cljs b/src/status_im/ui/components/react.cljs index b7b7dc93fa..2ff7c7ac82 100644 --- a/src/status_im/ui/components/react.cljs +++ b/src/status_im/ui/components/react.cljs @@ -71,7 +71,7 @@ ;; Accessor methods for React Components (defn add-font-style [style-key {:keys [font] :as opts :or {font :default}}] - (let [font (get-in platform-specific [:fonts (keyword font)]) + (let [font (get-in platform-specific [:fonts (keyword font)]) style (get opts style-key)] (-> opts (dissoc :font) @@ -88,17 +88,18 @@ [text-class (add-font-style :style opts)] ts)))))) + (defn text-input [{:keys [font style] :as opts :or {font :default}} text] (let [font (get-in platform-specific [:fonts (keyword font)])] [text-input-class (merge - {:underline-color-android :transparent - :placeholder-text-color st/text2-color - :placeholder (i18n/label :t/type-a-message) - :value text} - (-> opts - (dissoc :font) - (assoc :style (merge style font))))])) + {:underline-color-android :transparent + :placeholder-text-color st/text2-color + :placeholder (i18n/label :t/type-a-message) + :value text} + (-> opts + (dissoc :font) + (assoc :style (merge style font))))])) (defn icon ([n] (icon n st/icon-default)) @@ -188,7 +189,8 @@ [view props])] (vec (concat view-element children)))) -(views/defview with-activity-indicator [{:keys [timeout style enabled?]} comp] +(views/defview with-activity-indicator + [{:keys [timeout style enabled? preview]} comp] (views/letsubs [loading (r/atom true)] {:component-did-mount (fn [] @@ -199,6 +201,33 @@ (reset! loading false)) timeout)))} (if (and (not enabled?) @loading) - [view {:style style} - [activity-indicator {:animating true}]] + (or preview + [view {:style (or style {:justify-content :center + :align-items :center})} + [activity-indicator {:animating true}]]) comp))) + +(defn navigation-wrapper + "Wraps component so that it will be shown only when current-screen is one of views" + [{:keys [component views current-view hide?] + :or {hide? false}}] + (let [current-view? (if (set? views) + (views current-view) + (= views current-view)) + + style (if current-view? + {:flex 1} + {:opacity 0 + :flex 0}) + + component' (if (fn? component) [component] component)] + + (when (or (not hide?) (and hide? current-view?)) + (if hide? + component' + [view style (if (fn? component) [component] component)])))) + +(defn with-empty-preview [comp] + [with-activity-indicator + {:preview [view {}]} + comp]) diff --git a/src/status_im/ui/screens/main_tabs/views.cljs b/src/status_im/ui/screens/main_tabs/views.cljs index 79c1751552..4368d29109 100644 --- a/src/status_im/ui/screens/main_tabs/views.cljs +++ b/src/status_im/ui/screens/main_tabs/views.cljs @@ -54,8 +54,28 @@ [react/view common.styles/flex [status-bar.view/status-bar {:type (if (= view-id :wallet) :wallet :main)}] [react/view common.styles/main-container - [(case view-id - :home home.views/home - :wallet wallet.views/wallet - :my-profile profile.views/my-profile)] - [tabs view-id]]])) \ No newline at end of file + + [react/with-activity-indicator + {:enabled? (= :home view-id) + :preview [react/view {}]} + [react/navigation-wrapper + {:component home.views/home + :views :home + :current-view view-id}]] + + [react/with-activity-indicator + {:enabled? (= :wallet view-id) + :preview [react/view {}]} + [react/navigation-wrapper + {:component wallet.views/wallet + :views :wallet + :current-view view-id}]] + + [react/with-activity-indicator + {:enabled? (= :my-profile view-id) + :preview [react/view {}]} + [react/navigation-wrapper + {:component profile.views/my-profile + :views :my-profile + :current-view view-id}]] + [tabs view-id]]])) diff --git a/src/status_im/ui/screens/views.cljs b/src/status_im/ui/screens/views.cljs index 108fa0ffe7..56f875419f 100644 --- a/src/status_im/ui/screens/views.cljs +++ b/src/status_im/ui/screens/views.cljs @@ -1,8 +1,8 @@ (ns status-im.ui.screens.views - (:require-macros [status-im.utils.views :refer [defview letsubs]]) + (:require-macros [status-im.utils.views :refer [defview letsubs] :as views]) (:require [re-frame.core :refer [dispatch]] [status-im.utils.platform :refer [android?]] - [status-im.ui.components.react :refer [view modal]] + [status-im.ui.components.react :refer [view modal] :as react] [status-im.ui.components.styles :as common-styles] [status-im.ui.screens.main-tabs.views :refer [main-tabs]] [status-im.ui.components.context-menu :refer [menu-context]] @@ -60,10 +60,72 @@ current-view :chat)) +;;; defines hierarchy of views, when parent screen is opened children screens +;;; are pre-rendered, currently it is: +;;; +;;; root- +;;; | +;;; - main-tabs - +;;; | | +;;; - chat | +;;; wallet +;;; - wallet-send-transaction - +;;; | | +;;; | - choose-recipient +;;; | | +;;; | - wallet-transaction-sent +;;; | +;;; - transactions-history, unsigned-transactions +;;; | +;;; - wallet-request-transaction - +;;; | | +;;; | - choose-recipient +;;; | +;;; my-profile +;;; - edit-my-profile - +;;; | +;;; - profile-photo-capture +(views/compile-views root-view + [{:views #{:home :wallet :my-profile} + :component main-tabs} + + {:view :chat + :hide? (not android?) + :component chat} + + {:view :wallet-send-transaction + :parent :wallet + :component send-transaction} + + {:view :wallet-request-transaction + :parent :wallet + :component request-transaction} + + {:view :wallet-request-assets + :parent :wallet-request-transaction + :component wallet.components/request-assets} + + {:view :choose-recipient + :parent :wallet-send-transaction + :component choose-recipient} + + {:view :wallet-transaction-sent + :parent :wallet-send-transaction + :component transaction-sent} + + {:views #{:transactions-history :unsigned-transactions} + :parent :wallet + :component wallet-transactions/transactions} + + {:view :profile-photo-capture + :parent :my-profile + :component profile-photo-capture}]) + (defview main [] (letsubs [signed-up? [:signed-up?] view-id [:get :view-id] modal-view [:get :modal]] + {:component-will-update (fn [] (react/dismiss-keyboard!))} (when view-id (let [current-view (validate-current-view view-id signed-up?)] (let [component (case current-view @@ -109,7 +171,14 @@ (throw (str "Unknown view: " current-view)))] [(if android? menu-context view) common-styles/flex [view common-styles/flex - [component] + (if (and signed-up? + (#{:home :wallet :my-profile :chat :wallet-send-transaction + :choose-recipient :wallet-transaction-sent :transactions-history + :unsigned-transactions :wallet-request-transaction :edit-my-profile + :profile-photo-capture :wallet-request-assets} + current-view)) + [root-view] + [component]) (when modal-view [view common-styles/modal [modal {:animation-type :slide diff --git a/src/status_im/ui/screens/wallet/choose_recipient/views.cljs b/src/status_im/ui/screens/wallet/choose_recipient/views.cljs index b9fba1b796..0db613b9dc 100644 --- a/src/status_im/ui/screens/wallet/choose_recipient/views.cljs +++ b/src/status_im/ui/screens/wallet/choose_recipient/views.cljs @@ -73,7 +73,8 @@ (defview choose-recipient [] (letsubs [camera-dimensions [:wallet.send/camera-dimensions] camera-flashlight [:wallet.send/camera-flashlight] - camera-permitted? [:wallet.send/camera-permitted?]] + camera-permitted? [:wallet.send/camera-permitted?] + view [:get :view-id]] [react/view {:style styles/wallet-container} [status-bar/status-bar {:type :wallet}] [toolbar-view camera-flashlight] @@ -83,12 +84,17 @@ (re-frame/dispatch [:wallet.send/set-camera-dimensions {:width (.-width layout) :height (.-height layout)}]))} - (when (or platform/android? - camera-permitted?) - [camera/camera {:style styles/preview - :aspect :fill - :captureAudio false - :torchMode (camera/set-torch camera-flashlight) - :onBarCodeRead #(re-frame/dispatch [:wallet/fill-request-from-url (camera/get-qr-code-data %) nil])}]) + + (when (and + (= view :choose-recipient) + (or platform/android? + camera-permitted?)) + [react/with-activity-indicator + {} + [camera/camera {:style styles/preview + :aspect :fill + :captureAudio false + :torchMode (camera/set-torch camera-flashlight) + :onBarCodeRead #(re-frame/dispatch [:wallet/fill-request-from-url (camera/get-qr-code-data %) nil])}]]) [viewfinder camera-dimensions]] [recipient-buttons]])) diff --git a/src/status_im/ui/screens/wallet/request/views.cljs b/src/status_im/ui/screens/wallet/request/views.cljs index 09d3ba0105..7c385ea525 100644 --- a/src/status_im/ui/screens/wallet/request/views.cljs +++ b/src/status_im/ui/screens/wallet/request/views.cljs @@ -58,7 +58,9 @@ [react/view components.styles/flex [react/view styles/network-container [react/view styles/qr-container - [qr-code amount symbol]]] + [react/with-activity-indicator + {:style wallet.styles/qr-code-preview} + [qr-code amount symbol]]]] [react/view wallet.styles/amount-container [components/amount-input {:error amount-error diff --git a/src/status_im/ui/screens/wallet/styles.cljs b/src/status_im/ui/screens/wallet/styles.cljs index 29cda1a02a..7c5c722853 100644 --- a/src/status_im/ui/screens/wallet/styles.cljs +++ b/src/status_im/ui/screens/wallet/styles.cljs @@ -82,3 +82,9 @@ (def choose-asset-container {:margin-top 16 :margin-horizontal 15}) + +(def qr-code-preview + {:width 256 + :height 256 + :justify-content :center + :align-items :center}) diff --git a/src/status_im/ui/screens/wallet/transactions/views.cljs b/src/status_im/ui/screens/wallet/transactions/views.cljs index 725f43e9d8..6cddd9d0b0 100644 --- a/src/status_im/ui/screens/wallet/transactions/views.cljs +++ b/src/status_im/ui/screens/wallet/transactions/views.cljs @@ -35,7 +35,8 @@ [toolbar/content-title (i18n/label :t/transactions)] (case current-tab :transactions-history [toolbar/actions [(history-action (not (all-checked? filter-data)))]] - :unsigned-transactions nil)]) ;; TODO (andrey) implement [unsigned-action unsigned-transactions-count] + :unsigned-transactions nil + nil)]) ;; TODO (andrey) implement [unsigned-action unsigned-transactions-count] (defn action-buttons [{:keys [id] :as transaction}] @@ -197,7 +198,8 @@ [tabs current-tab] [(case current-tab :transactions-history history-list - :unsigned-transactions unsigned-list)]])) + :unsigned-transactions unsigned-list + react/view)]])) (defn- pretty-print-asset [symbol amount] (case symbol diff --git a/src/status_im/utils/views.clj b/src/status_im/utils/views.clj index 5c5ebc71b0..e0f2332833 100644 --- a/src/status_im/utils/views.clj +++ b/src/status_im/utils/views.clj @@ -66,3 +66,210 @@ (fn ~params (let [~@vars-bindings] ~body))}))))))) + +(defn check-view [all {:keys [view views component hide? parent]}] + (let [parent (or parent :root) + views (or views #{view}) + comp {:views views + :component component + :hide? hide?}] + (-> all + (assoc-in [:components views] comp) + (update-in [:view->children parent] + (fn [children] + (let [children (or children [])] + (conj children views)))) + (update :view->components + (fn [view->components] + (reduce (fn [view->components view] + (assoc view->components view views)) + view->components views)))))) + +(defn -build-tree [views] + (reduce check-view {:components {} + :view->components {} + :view->children {}} views)) + +(defn -get-all-views + [{:keys [view->children] :as config} component] + (let [children (reduce clojure.set/union (keep view->children component))] + (reduce clojure.set/union + (concat [component] + children + (map (fn [child] + (-get-all-views config child)) + (reduce clojure.set/union + (map view->children component))))))) + +(defn -get-grandchildren [{:keys [view->children] :as config} children] + (into {} + (filter + (fn [[_ children]] + (not (nil? children))) + (map (fn [child] + [child (get view->children child)]) + (reduce clojure.set/union children))))) + +(defn -generate-component + [{:keys [components view->children] :as config} view-sym component-name] + (let [component-config (get components component-name) + children (get view->children component-name) + grandchildren (-get-grandchildren config children)] + `[status-im.ui.components.react/view + {:flex + ~(if (= :root component-name) + 1 + `(if (contains? + ~(disj (-get-all-views config #{component-name}) component-name) + ~view-sym) + 1 + 0))} + ~@(when component-config + `[status-im.ui.components.react/navigation-wrapper + ~(assoc component-config :current-view view-sym)]) + ~@(map (fn [child] + `[status-im.ui.components.react/navigation-wrapper + ~(assoc (get components child) :current-view view-sym)]) + children) + ~@(map (fn [[component-name children]] + `[status-im.ui.components.react/navigation-wrapper + {:component [status-im.ui.components.react/with-empty-preview + ~(-generate-component config view-sym component-name)] + :views ~(conj + (reduce + clojure.set/union + (map (fn [child] + (-get-all-views config child)) + children)) + component-name) + :hide? true + :current-view ~view-sym}]) + grandchildren)])) + +(defn -compile-views [n views] + (let [view-sym (gensym "view-id")] + `(defview ~n [] + (letsubs [~view-sym [:get :view-id]] + ~(let [tree (-build-tree views)] + (-generate-component tree view-sym :root)))))) + +(defmacro compile-views + [n views] + (-compile-views n views)) + + +(comment + + (-compile-views + 'root-view + '[{:views #{:home :wallet :my-profile} + :component main-tabs} + + {:view :chat + :hide? (not android?) + :component chat} + + {:view :wallet-send-transaction + :parent :wallet + :component send-transaction} + + {:view :wallet-request-transaction + :parent :wallet + :component request-transaction} + + {:view :choose-recipient + :parent :wallet-send-transaction + :component choose-recipient} + + {:view :wallet-transaction-sent + :parent :wallet-send-transaction + :component transaction-sent} + + {:views #{:transactions-history :unsigned-transactions} + :parent :wallet + :component wallet-transactions/transactions} + + {:view :edit-my-profile + :parent :profile + :component edit-my-profile}]) + + (status-im.utils.views/defview + root-view + [] + (status-im.utils.views/letsubs + [view-id76826 [:get :view-id]] + [status-im.ui.components.react/view + {:flex 1} + [status-im.ui.components.react/navigation-wrapper + {:views #{:home :wallet :my-profile}, + :component main-tabs, + :hide? nil, + :current-view view-id76826}] + [status-im.ui.components.react/navigation-wrapper + {:views #{:chat}, + :component chat, + :hide? (not android?), + :current-view view-id76826}] + [status-im.ui.components.react/navigation-wrapper + {:hide? true, + :component [status-im.ui.components.react/with-empty-preview + [status-im.ui.components.react/view + {:flex (if + (clojure.core/contains? + #{:wallet-request-transaction + :wallet-send-transaction + :choose-recipient + :wallet-transaction-sent + :transactions-history + :unsigned-transactions} + view-id76826) + 1 + 0)} + [status-im.ui.components.react/navigation-wrapper + {:views #{:wallet-send-transaction}, + :component send-transaction, + :hide? nil, + :current-view view-id76826}] + [status-im.ui.components.react/navigation-wrapper + {:views #{:wallet-request-transaction}, + :component request-transaction, + :hide? nil, + :current-view view-id76826}] + [status-im.ui.components.react/navigation-wrapper + {:views #{:transactions-history :unsigned-transactions}, + :component wallet-transactions/transactions, + :hide? nil, + :current-view view-id76826}] + [status-im.ui.components.react/navigation-wrapper + {:hide? true, + :component [status-im.ui.components.react/with-empty-preview + [status-im.ui.components.react/view + {:flex (if + (clojure.core/contains? + #{:choose-recipient + :wallet-transaction-sent} + view-id76826) + 1 + 0)} + [status-im.ui.components.react/navigation-wrapper + {:views #{:choose-recipient}, + :component choose-recipient, + :hide? nil, + :current-view view-id76826}] + [status-im.ui.components.react/navigation-wrapper + {:views #{:wallet-transaction-sent}, + :component transaction-sent, + :hide? nil, + :current-view view-id76826}]]], + :current-view view-id76826, + :views #{:wallet-send-transaction + :choose-recipient + :wallet-transaction-sent}}]]], + :current-view view-id76826, + :views #{:wallet-request-transaction + :wallet + :wallet-send-transaction + :choose-recipient + :wallet-transaction-sent + :transactions-history + :unsigned-transactions}}]])))