From d35deefcb59302eb79b2d748a01eae65bd15e44e Mon Sep 17 00:00:00 2001 From: Gheorghe Pinzaru Date: Thu, 23 Apr 2020 17:05:26 +0300 Subject: [PATCH] Use new component to animate headers fix header title spacing Remove translate animation Add press for profile header Rebase Attempt to fix e2e Add back button accessibility-label Signed-off-by: Gheorghe Pinzaru --- src/quo/components/animated_header.cljs | 70 ++++----- src/quo/components/header.cljs | 8 +- src/quo/react_native.cljs | 2 +- .../ui/components/large_toolbar/styles.cljs | 84 ----------- .../ui/components/large_toolbar/view.cljs | 105 ------------- .../ui/components/profile_header/view.cljs | 81 ++++++++++ .../ui/screens/profile/components/views.cljs | 91 ------------ .../ui/screens/profile/contact/views.cljs | 110 ++++++-------- .../ui/screens/profile/group_chat/views.cljs | 40 +++-- .../ui/screens/profile/user/views.cljs | 104 +++++-------- .../ui/screens/routing/chat_stack.cljs | 2 + .../ui/screens/routing/profile_stack.cljs | 2 + .../ui/screens/wallet/accounts/styles.cljs | 83 +++-------- .../ui/screens/wallet/accounts/views.cljs | 138 ++++++++---------- test/appium/views/profile_view.py | 3 +- 15 files changed, 303 insertions(+), 620 deletions(-) delete mode 100644 src/status_im/ui/components/large_toolbar/styles.cljs delete mode 100644 src/status_im/ui/components/large_toolbar/view.cljs create mode 100644 src/status_im/ui/components/profile_header/view.cljs diff --git a/src/quo/components/animated_header.cljs b/src/quo/components/animated_header.cljs index bd62212744..a0d7c3794f 100644 --- a/src/quo/components/animated_header.cljs +++ b/src/quo/components/animated_header.cljs @@ -10,7 +10,7 @@ (defn header-wrapper-style [{:keys [value offset]}] (merge - {:background-color :white} + {:background-color (:ui-background @colors/theme)} (when (and offset platform/android?) {:elevation (animated/interpolate value @@ -24,51 +24,55 @@ :outputRange [0 1] :extrapolate (:clamp animated/extrapolate)}) :shadow-radius 16 - :z-index 2 :shadow-color (:shadow-01 @colors/theme) :shadow-offset {:width 0 :height 4}}))) -(defn header-opened-style [{:keys [value offset]}] - (merge - {:position :absolute - :top 0 - :left 0 - :right 0} - (when offset - {:transform [{:translateY - (animated/interpolate - value - {:inputRange [0 offset] - :outputRange [0 (- header/header-height)] - :extrapolateRight (:clamp animated/extrapolate)})}]}))) +(defn title-style [layout] + {:flex 1 + :justify-content :center + :padding-right (get-in layout [:right :width])}) (defn header-container [] - (let [y (animated/value 0) - on-scroll (animated/on-scroll {:y y}) - layout (reagent/atom {}) - offset (reagent/atom 0) - on-layout (fn [evt] - (reset! offset (oget evt "nativeEvent" "layout" "height")))] + (let [y (animated/value 0) + animation-value (animated/value 0) + animation (animated/with-timing + animation-value + {:duration 250 + :easing (:ease-in animated/easings)}) + on-scroll (animated/on-scroll {:y y}) + layout (reagent/atom {}) + offset (reagent/atom 0) + on-layout (fn [evt] + (reset! offset (oget evt "nativeEvent" "layout" "height")))] (fn [{:keys [extended-header] :as props} & children] [animated/view {:flex 1 :pointer-events :box-none} + [animated/code {:key (str @offset) + :exec (animated/cond* + (animated/and* (animated/greater-or-eq y @offset) + (animated/greater-or-eq y 1)) + (animated/set animation-value 1) + (animated/set animation-value 0))}] [animated/view {:pointer-events :box-none :style (header-wrapper-style {:value y :offset @offset})} - [header/header (merge {:get-layout (fn [el l] (swap! layout assoc el l))} - (dissoc props :extended-header))] - [rn/view {:pointer-events :box-none} - [animated/view {:style (header-opened-style {:value y - :offset @offset}) - :pointer-events :box-none - :on-layout on-layout} - [extended-header {:value y - :layout @layout - :offset @offset}]]]] + [header/header (merge + {:get-layout (fn [el l] (swap! layout assoc el l)) + :title-component [animated/view {:style (title-style @layout)} + [extended-header {:value y + :animation animation + :minimized true + :offset @offset}]] + :title-align :left} + (dissoc props :extended-header))]] (into [animated/scroll-view {:on-scroll on-scroll :scrollEventThrottle 1} - [rn/view {:pointer-events :box-none - :height @offset}]] + [rn/view {:pointer-events :box-none} + [animated/view {:pointer-events :box-none + :on-layout on-layout} + [extended-header {:value y + :animation animation + :offset @offset}]]]] children)]))) (defn header [{:keys [use-insets] :as props} & children] diff --git a/src/quo/components/header.cljs b/src/quo/components/header.cljs index e99f08f742..afd4f0a05a 100644 --- a/src/quo/components/header.cljs +++ b/src/quo/components/header.cljs @@ -46,11 +46,7 @@ (defn title-style [{:keys [left right]} title-align] (merge - {:position :absolute - :justify-content :center - :top 0 - :bottom 0} - (:tiny spacing/padding-horizontal) + absolute-fill (case title-align :left {:left (:width left) :right (:width right)} @@ -67,7 +63,7 @@ (:tiny spacing/padding-horizontal))) (def header-action-placeholder - {:width (:tiny spacing/spacing)}) + {:width (:base spacing/spacing)}) (def header-icon-touchable (merge diff --git a/src/quo/react_native.cljs b/src/quo/react_native.cljs index 80145d3d60..c00d944258 100644 --- a/src/quo/react_native.cljs +++ b/src/quo/react_native.cljs @@ -33,7 +33,7 @@ (defn- wrap-render-fn [f] (fn [data] - (reagent/as-element (f (.-item data) (.-index data) (.-separators data))))) + (reagent/as-element (f (.-item ^js data) (.-index ^js data) (.-separators ^js data))))) (defn- wrap-key-fn [f] (fn [data index] diff --git a/src/status_im/ui/components/large_toolbar/styles.cljs b/src/status_im/ui/components/large_toolbar/styles.cljs deleted file mode 100644 index a73393e040..0000000000 --- a/src/status_im/ui/components/large_toolbar/styles.cljs +++ /dev/null @@ -1,84 +0,0 @@ -(ns status-im.ui.components.large-toolbar.styles - (:require [status-im.ui.components.colors :as colors] - [status-im.ui.components.animation :as animation] - [status-im.ui.components.toolbar.styles :as toolbar.styles] - [status-im.utils.platform :as platform])) - -(defonce toolbar-shadow-component-height - (let [status-bar-height (get platform/platform-specific :status-bar-default-height)] - (+ 50 toolbar.styles/toolbar-height (if (zero? status-bar-height) 50 status-bar-height)))) - -(defonce toolbar-statusbar-height - (+ (get platform/platform-specific :status-bar-default-height) toolbar.styles/toolbar-height)) - -(defn minimized-toolbar-fade-in [anim-opacity] - (animation/timing - anim-opacity - {:toValue 1 - :duration 200 - :easing (.-ease ^js animation/easing) - :useNativeDriver true})) - -(defn minimized-toolbar-fade-out [anim-opacity] - (animation/timing - anim-opacity - {:toValue 0 - :duration 200 - :easing (.-ease ^js animation/easing) - :useNativeDriver true})) - -(defn- ios-shadow-opacity-anim [scroll-y] - (if platform/ios? - (animation/interpolate scroll-y - {:inputRange [0 toolbar-statusbar-height] - :outputRange [0 1] - :extrapolate "clamp"}) - 0)) - -(defn- android-shadow-elevation-anim [scroll-y] - (if platform/android? - (animation/interpolate scroll-y - {:inputRange [0 toolbar-statusbar-height] - :outputRange [0 9] - :extrapolate "clamp"}) - 0)) - -(defn bottom-border-opacity-anim [scroll-y] - (animation/interpolate scroll-y - {:inputRange [0 toolbar-statusbar-height] - :outputRange [1 0] - :extrapolate "clamp"})) - -(defn animated-content-wrapper [anim-opacity] - {:flex 1 - :align-self :stretch - :opacity anim-opacity}) - -(def minimized-toolbar - {:z-index 100 - :elevation 9}) - -(defn flat-list-with-large-header-bottom [scroll-y] - {:height 16 - :opacity (bottom-border-opacity-anim scroll-y) - :border-bottom-width 1 - :border-bottom-color colors/gray-lighter}) - -(defn flat-list-with-large-header-shadow [window-width scroll-y] - (cond-> {:flex 1 - :align-self :stretch - :position :absolute - :height toolbar-shadow-component-height - :width window-width - :top (- toolbar-shadow-component-height) - :shadow-radius 8 - :shadow-offset {:width 0 :height 2} - :shadow-opacity 1 - :shadow-color "rgba(0, 9, 26, 0.12)" - :elevation (android-shadow-elevation-anim scroll-y) - :background-color colors/white} - platform/ios? - (assoc :opacity (ios-shadow-opacity-anim scroll-y)))) - -(def flat-list - {:z-index -1}) diff --git a/src/status_im/ui/components/large_toolbar/view.cljs b/src/status_im/ui/components/large_toolbar/view.cljs deleted file mode 100644 index 0d5ac7fce8..0000000000 --- a/src/status_im/ui/components/large_toolbar/view.cljs +++ /dev/null @@ -1,105 +0,0 @@ -(ns status-im.ui.components.large-toolbar.view - (:require [reagent.core :as reagent] - [status-im.ui.components.list.views :as list.views] - [status-im.ui.components.react :as react] - [status-im.ui.components.toolbar.view :as toolbar] - [status-im.ui.components.large-toolbar.styles :as styles] - [status-im.ui.components.animation :as animation]) - (:require-macros [status-im.utils.views :as views])) - -;; header-in-toolbar - component - small header in toolbar -;; nav-item - component/nil - if nav-item like back button is needed, else nil -;; action-items - status-im.ui.components.toolbar.view/actions -(defn minimized-toolbar [header-in-toolbar nav-item action-items anim-opacity] - (let [has-nav? (boolean nav-item)] - [toolbar/toolbar - {:transparent? true - :style styles/minimized-toolbar} - nav-item - [react/animated-view - {:style (cond-> (styles/animated-content-wrapper anim-opacity) - (false? has-nav?) - (assoc :margin-left -40 :margin-right 40))} - header-in-toolbar] - action-items])) - -;; header - component that serves as large header without any top/bottom padding -;; top(4px high) and bottom(16px high and with border) padding -;; are assumed to be constant -;; this is wrapped with padding components and merged with content -;; content - vector - of the rest(from header) of the list components -;; wrapped header and content form the data prop of flat-list -;; list-ref - atom - a reference to flat-list for the purpose of invoking its -;; methods -;; scroll-y - animated value tracking the y scoll of the main content in flat-list-view -(views/defview flat-list-with-large-header [header content list-ref scroll-y] - (views/letsubs [window-width [:dimensions/window-width]] - (let [header-top-padding [react/view {:height 4}] - ;; header bottom padding with border-bottom - ;; fades out as it approaches toolbar shadow - header-bottom [react/animated-view - {:style (styles/flat-list-with-large-header-bottom scroll-y)}] - wrapped-data (into [header-top-padding header header-bottom] content)] - [react/view {:flex 1} - ;; toolbar shadow - [react/animated-view - {:style (styles/flat-list-with-large-header-shadow window-width scroll-y)}] - - [list.views/flat-list - {:style styles/flat-list - :data wrapped-data - :initial-num-to-render 3 - :ref #(when % (reset! list-ref (.getNode ^js %))) - :render-fn list.views/flat-list-generic-render-fn - :key-fn (fn [item idx] (str idx)) - :scrollEventThrottle 16 - :on-scroll (animation/event - [{:nativeEvent {:contentOffset {:y scroll-y}}}] - {:useNativeDriver true}) - :keyboard-should-persist-taps :handled} - {:animated? true}]]))) - -(defn generate-view - "main function which generates views. - - it will generate and return back: - - minimized-toolbar - - flat-list-with-large-header" - [header-in-toolbar nav-item toolbar-action-items header content list-ref] - (let [to-hide (reagent/atom true) - anim-opacity (animation/create-value 0) - scroll-y (animation/create-value 0)] - (animation/add-listener scroll-y (fn [^js anim] - (cond - (and (>= (.-value anim) 40) (not @to-hide)) - (animation/start - (styles/minimized-toolbar-fade-in anim-opacity) - #(reset! to-hide true)) - - (and (< (.-value anim) 40) @to-hide) - (animation/start - (styles/minimized-toolbar-fade-out anim-opacity) - #(reset! to-hide false))))) - {:minimized-toolbar [minimized-toolbar header-in-toolbar nav-item toolbar-action-items anim-opacity] - :content-with-header [flat-list-with-large-header header content list-ref scroll-y]})) - -(defn add-listener [anim-opacity scroll-y] - (let [to-hide (atom false)] - (animation/add-listener - scroll-y - (fn [^js anim] - (cond - (and (>= (.-value anim) 40) (not @to-hide)) - (animation/start - (styles/minimized-toolbar-fade-in anim-opacity) - #(reset! to-hide true)) - - (and (< (.-value anim) 40) @to-hide) - (animation/start - (styles/minimized-toolbar-fade-out anim-opacity) - #(reset! to-hide false))))))) - -(defn minimized-toolbar-handler [header-in-toolbar nav-item toolbar-action-items anim-opacity] - [minimized-toolbar header-in-toolbar nav-item toolbar-action-items anim-opacity]) - -(defn flat-list-with-header-handler [header content list-ref scroll-y] - [flat-list-with-large-header header content list-ref scroll-y]) diff --git a/src/status_im/ui/components/profile_header/view.cljs b/src/status_im/ui/components/profile_header/view.cljs new file mode 100644 index 0000000000..a03760443a --- /dev/null +++ b/src/status_im/ui/components/profile_header/view.cljs @@ -0,0 +1,81 @@ +(ns status-im.ui.components.profile-header.view + (:require [quo.core :as quo] + [quo.animated :as animated] + [quo.design-system.spacing :as spacing] + [quo.design-system.colors :as colors] + [quo.react-native :as rn] + [status-im.ui.components.icons.vector-icons :as vector-icons] + [status-im.ui.components.chat-icon.screen :as chat-icon.screen])) + +(def avatar-extended-size 64) +(def avatar-minimized-size 40) +(def subtitle-margin 4) + +(defn container-style [{:keys [animation minimized]}] + (merge {:flex-direction :row + :align-items :center} + (if-not minimized + (:base spacing/padding-horizontal) + {:opacity animation}))) + +(defn header-bottom-separator [] + {:margin-bottom (:tiny spacing/spacing) + :height (:small spacing/spacing) + :border-bottom-width 1 + :border-bottom-color (:ui-02 @colors/theme)}) + +(defn header-text [] + {:padding-left (:base spacing/spacing) + :flex 1 + :justify-content :center}) + +(defn header-subtitle [{:keys [minimized]}] + (merge {:padding-right (:large spacing/spacing) + :flex-direction :row + :align-items :center} + (when-not minimized + {:padding-top subtitle-margin}))) + +(defn extended-header [{:keys [title photo color subtitle subtitle-icon on-press]}] + (fn [{:keys [animation minimized]}] + (let [wrapper (if on-press + [rn/touchable-opacity {:on-press on-press}] + [:<>])] + (into + wrapper + [[animated/view {:pointer-events :box-none} + [animated/view {:style (container-style {:animation animation + :minimized minimized}) + :pointer-events :box-none} + [animated/view {:pointer-events :box-none} + [chat-icon.screen/profile-icon-view + photo title color nil + (if minimized avatar-minimized-size avatar-extended-size) + nil]] + [animated/view {:style (header-text) + :pointer-events :box-none} + [quo/text {:animated? true + :number-of-lines (if minimized 1 2) + :size (if minimized :base :x-large) + :weight :bold + :elipsize-mode :tail + :accessibility-role :text + :accessibility-label :default-username} + title] + (when subtitle + [animated/view {:style (header-subtitle {:minimized minimized}) + :pointer-events :box-none} + (when subtitle-icon + [vector-icons/icon subtitle-icon {:color (:icon-02 @colors/theme) + :width 16 + :height 16 + :container-style {:margin-right 4}}]) + [quo/text {:number-of-lines 1 + :ellipsize-mode :middle + :size (if minimized :small :base) + :color :secondary} + subtitle]])]] + (when-not minimized + [animated/view {:pointer-events :none + :style (header-bottom-separator)}])]])))) + diff --git a/src/status_im/ui/screens/profile/components/views.cljs b/src/status_im/ui/screens/profile/components/views.cljs index 7e66f2878a..c763b0842a 100644 --- a/src/status_im/ui/screens/profile/components/views.cljs +++ b/src/status_im/ui/screens/profile/components/views.cljs @@ -1,103 +1,12 @@ (ns status-im.ui.screens.profile.components.views (:require [clojure.string :as string] - [re-frame.core :as re-frame] - [status-im.multiaccounts.core :as multiaccounts] [status-im.i18n :as i18n] - [status-im.ui.components.chat-icon.screen :as chat-icon.screen] [status-im.ui.components.colors :as colors] [status-im.ui.components.common.common :as common] [status-im.ui.components.icons.vector-icons :as vector-icons] [status-im.ui.components.react :as react] - [status-im.utils.gfycat.core :as gfy] - [status-im.ui.screens.profile.user.sheet.views :as sheets] [status-im.ui.screens.profile.components.styles :as styles])) -(defn- names [{:keys [usernames public-key] :as contact}] - (let [generated-name (when public-key (gfy/generate-gfy public-key))] - [react/view styles/profile-header-name-container-with-subtitle - [react/text {:style styles/profile-name-text-with-subtitle - :number-of-lines 2 - :ellipsize-mode :tail} - (multiaccounts/displayed-name contact)] - [react/text {:style styles/profile-three-words - :number-of-lines 1} - (if (seq usernames) - generated-name - public-key)]])) - -(defn chat-key-popover [public-key ens-name] - (re-frame/dispatch [:show-popover - {:view :share-chat-key - :address public-key - :ens-name ens-name}])) - -(defn- profile-header-display - [{:keys [public-key preferred-name ens-name] :as contact} - allow-icon-change? include-remove-action?] - [react/touchable-opacity - {:on-press #(chat-key-popover public-key (or ens-name - preferred-name))} - [react/view (merge styles/profile-header-display {:padding-horizontal 16}) - (if allow-icon-change? - [react/view {:align-items :center - :align-self :stretch - :justify-content :center} - [react/touchable-highlight - {:accessibility-label :edit-profile-photo-button - :on-press - #(re-frame/dispatch - [:bottom-sheet/show-sheet - {:content (sheets/profile-icon-actions include-remove-action?) - :content-height (if include-remove-action? 192 128)}])} - [react/view - [react/view {:background-color colors/white - :border-radius 15 - :width 30 - :height 30 - :justify-content :center - :align-items :center - :position :absolute - :z-index 1 - :top -5 - :right -5} - [react/view {:background-color colors/blue - :border-radius 12 - :width 24 - :height 24 - :justify-content :center - :align-items :center} - [vector-icons/icon :tiny-edit {:color colors/white - :width 16 - :height 16}]]] - [chat-icon.screen/my-profile-icon {:multiaccount contact - :edit? false}]]]] - ;; else - [chat-icon.screen/my-profile-icon {:multiaccount contact - :edit? false}]) - [names contact]]]) - -(defn group-header-display [{:keys [chat-name color contacts]}] - [react/view (merge styles/profile-header-display {:padding-horizontal 16}) - [chat-icon.screen/profile-icon-view nil chat-name color nil 64 nil] - [react/view styles/profile-header-name-container - [react/text {:style styles/profile-name-text - :number-of-lines 2 - :ellipsize-mode :tail} - chat-name] - [react/view {:style {:flex-direction :row - :align-items :center}} - [vector-icons/icon :icons/tiny-group {:color colors/gray - :width 16 - :height 16 - :container-style {:margin-right 4}}] - [react/text {:style {:line-height 22 - :color colors/gray}} - (i18n/label :t/members-count {:count (count contacts)})]]]]) - -(defn profile-header - [{:keys [contact allow-icon-change? include-remove-action?]}] - [profile-header-display contact allow-icon-change? include-remove-action?]) - ;; settings items elements (defn settings-item diff --git a/src/status_im/ui/screens/profile/contact/views.cljs b/src/status_im/ui/screens/profile/contact/views.cljs index eecc40e8ff..fd08ee9246 100644 --- a/src/status_im/ui/screens/profile/contact/views.cljs +++ b/src/status_im/ui/screens/profile/contact/views.cljs @@ -6,15 +6,14 @@ [status-im.utils.utils :as utils] [status-im.ui.components.icons.vector-icons :as icons] [status-im.ui.components.react :as react] - [status-im.ui.components.large-toolbar.view :as large-toolbar] - [status-im.ui.components.toolbar.view :as toolbar] - [status-im.ui.screens.profile.components.views :as profile.components] [status-im.ui.screens.profile.contact.styles :as styles] [status-im.ui.components.list-item.views :as list-item] [status-im.ui.components.chat-icon.screen :as chat-icon] [status-im.ui.screens.profile.components.sheets :as sheets] - [status-im.ui.screens.chat.photos :as photos] - [status-im.multiaccounts.core :as multiaccounts]) + [status-im.ui.components.profile-header.view :as profile-header] + [status-im.utils.gfycat.core :as gfy] + [status-im.multiaccounts.core :as multiaccounts] + [quo.core :as quo]) (:require-macros [status-im.utils.views :as views])) (defn actions @@ -89,71 +88,44 @@ (i18n/label :t/unblock-contact) (i18n/label :t/block-contact))]]) -(defn- header-in-toolbar [{:keys [public-key ens-name] :as account}] - (let [displayed-name (multiaccounts/displayed-name account)] - [react/touchable-opacity {:on-press #(profile.components/chat-key-popover public-key ens-name) - :style {:flex 1 - :flex-direction :row - :align-items :center - :align-self :stretch}} - ;;TODO this should be done in a subscription - [photos/photo (multiaccounts/displayed-photo account) - {:size 40}] - [react/text {:style {:typography :title-bold - :line-height 21 - :margin-right 40 - :margin-left 16 - :text-align :left} - :accessibility-label :account-name} - displayed-name]])) - -(defn- header [account] - [profile.components/profile-header - {:contact account - :allow-icon-change? false - :include-remove-action? false}]) - -(defn- toolbar-action-items [public-key ens-name] - [toolbar/actions - [{:icon :main-icons/share - :icon-opts {:width 24 - :height 24} - :handler #(profile.components/chat-key-popover public-key ens-name)}]]) - -;;TO-DO Rework generate-view to use 3 functions from large-toolbar (views/defview profile [] - (views/letsubs [list-ref (reagent/atom nil) - {:keys [ens-verified name public-key] :as contact} [:contacts/current-contact]] - (when contact - (let [ens-name (when (and ens-verified name) name) - contact (cond-> contact - ens-name - (assoc :usernames [ens-name] - :ens-name ens-name)) - header-in-toolbar (header-in-toolbar contact) - header (header contact) - content - [[react/view {:padding-top 12} - (for [{:keys [label subtext accessibility-label icon action disabled?]} (actions contact)] - [list-item/list-item {:theme :action - :title label - :subtitle subtext - :icon icon - :accessibility-label accessibility-label - :disabled? disabled? - :on-press action}])] - [react/view styles/contact-profile-details-container - [profile-details contact]] - [block-contact-action contact]] - generated-view (large-toolbar/generate-view - header-in-toolbar - toolbar/default-nav-back - (toolbar-action-items public-key ens-name) - header - content - list-ref)] + (views/letsubs [{:keys [ens-verified name public-key] + :as contact} [:contacts/current-contact]] + (let [on-share #(re-frame/dispatch [:show-popover (merge + {:view :share-chat-key + :address public-key} + (when (and ens-verified name) + {:ens-name name}))])] + (when contact [react/view {:style (merge {:flex 1})} - (:minimized-toolbar generated-view) - (:content-with-header generated-view)])))) + [quo/animated-header + {:use-insets true + :right-accessories [{:icon :main-icons/share + :on-press on-share}] + :left-accessories [{:icon :main-icons/back + :accessibility-label :back-button + :on-press #(re-frame/dispatch [:navigate-back])}] + :extended-header (profile-header/extended-header + {:on-press on-share + :title (multiaccounts/displayed-name contact) + :photo (multiaccounts/displayed-photo contact) + :subtitle (if (and ens-verified public-key) + (gfy/generate-gfy public-key) + public-key)})} + + [react/view {:padding-top 12} + (for [{:keys [label subtext accessibility-label icon action disabled?]} (actions contact)] + [list-item/list-item {:theme :action + :title label + :subtitle subtext + :icon icon + :accessibility-label accessibility-label + :disabled? disabled? + :on-press action}])] + [react/view styles/contact-profile-details-container + [profile-details (cond-> contact + (and ens-verified name) + (assoc :ens-name name))]] + [block-contact-action contact]]])))) diff --git a/src/status_im/ui/screens/profile/group_chat/views.cljs b/src/status_im/ui/screens/profile/group_chat/views.cljs index 411a192bdb..c5dc6c97f5 100644 --- a/src/status_im/ui/screens/profile/group_chat/views.cljs +++ b/src/status_im/ui/screens/profile/group_chat/views.cljs @@ -14,16 +14,9 @@ [status-im.ui.components.colors :as colors] [re-frame.core :as re-frame] [status-im.i18n :as i18n] - [status-im.ui.components.list-item.views :as list-item] - [status-im.ui.components.topbar :as topbar])) - -(defn group-chat-profile-toolbar [admin?] - [topbar/topbar - (when admin? - {:accessories - [{:icon :icons/edit - :accessibility-label :edit-button - :handler #(re-frame/dispatch [:navigate-to :edit-group-chat-name])}]})]) + [quo.core :as quo] + [status-im.ui.components.profile-header.view :as profile-header] + [status-im.ui.components.list-item.views :as list-item])) (defn member-sheet [chat-id member us-admin?] [react/view @@ -92,24 +85,29 @@ [chat-group-members-view chat-id admin? current-pk]]) (defview group-chat-profile [] - (letsubs [{:keys [admins chat-id joined?] :as current-chat} [:chats/current-chat] + (letsubs [{:keys [admins chat-id joined? chat-name color contacts] :as current-chat} [:chats/current-chat] members [:contacts/current-chat-contacts] - changed-chat [:group-chat-profile/profile] current-pk [:multiaccount/public-key]] (when current-chat - (let [shown-chat (merge current-chat changed-chat) - admin? (get admins current-pk) + (let [admin? (get admins current-pk) allow-adding-members? (and admin? joined? (< (count members) constants/max-group-chat-participants))] [react/view profile.components.styles/profile - [group-chat-profile-toolbar (and admin? joined?)] - [react/scroll-view + [quo/animated-header + {:use-insets true + :left-accessories [{:icon :main-icons/back + :accessibility-label :back-button + :on-press #(re-frame/dispatch [:navigate-back])}] + :right-accessories (when (and admin? joined?) + [{:icon :icons/edit + :accessibility-label :edit-button + :on-press #(re-frame/dispatch [:navigate-to :edit-group-chat-name])}]) + :extended-header (profile-header/extended-header + {:title chat-name + :color color + :subtitle (i18n/label :t/members-count {:count (count contacts)}) + :subtitle-icon :icons/tiny-group})} [react/view profile.components.styles/profile-form - [react/view {:style {:border-bottom-width 1 - :padding-bottom 15 - :margin-bottom 8 - :border-bottom-color colors/gray-lighter}} - [profile.components/group-header-display shown-chat]] (when joined? [list-item/list-item {:theme :action diff --git a/src/status_im/ui/screens/profile/user/views.cljs b/src/status_im/ui/screens/profile/user/views.cljs index 486e0d00e7..f669dd36e4 100644 --- a/src/status_im/ui/screens/profile/user/views.cljs +++ b/src/status_im/ui/screens/profile/user/views.cljs @@ -2,24 +2,22 @@ (:require [re-frame.core :as re-frame] [reagent.core :as reagent] [status-im.i18n :as i18n] - [status-im.multiaccounts.core :as multiaccounts] [status-im.ui.components.button :as button] [status-im.ui.components.colors :as colors] + [status-im.multiaccounts.core :as multiaccounts] [status-im.ui.components.common.common :as components.common] [status-im.ui.components.copyable-text :as copyable-text] - [status-im.ui.components.large-toolbar.view :as large-toolbar] [status-im.ui.components.list-selection :as list-selection] [status-im.ui.components.list.views :as list.views] [status-im.ui.components.qr-code-viewer.views :as qr-code-viewer] [status-im.ui.components.react :as react] - [status-im.ui.components.toolbar.view :as toolbar] - [status-im.ui.screens.chat.photos :as photos] - [status-im.ui.screens.profile.components.views :as profile.components] [status-im.ui.screens.profile.user.styles :as styles] [status-im.utils.platform :as platform] [status-im.utils.config :as config] + [quo.core :as quo] + [status-im.utils.gfycat.core :as gfy] [status-im.utils.universal-links.core :as universal-links] - [status-im.ui.components.animation :as animation]) + [status-im.ui.components.profile-header.view :as profile-header]) (:require-macros [status-im.utils.views :as views])) (views/defview share-chat-key [] @@ -61,38 +59,6 @@ ;:icon :main-icons/link :accessibility-label :share-my-contact-code-button}]]]))) -(defn- header [account photo-added?] - [profile.components/profile-header - {:contact account - ;;set to true if we want to re-enable custom icon - :allow-icon-change? false - :include-remove-action? photo-added?}]) - -(defn- header-in-toolbar [{:keys [public-key preferred-name] :as account}] - (let [displayed-name (multiaccounts/displayed-name account)] - [react/touchable-opacity - {:on-press #(profile.components/chat-key-popover public-key preferred-name) - :style {:flex 1}} - [react/view {:style {:flex 1 - :flex-direction :row - :align-items :center - :align-self :stretch}} - ;;TODO this should be done in a subscription - [photos/photo (multiaccounts/displayed-photo account) {:size 40}] - [react/text {:style {:typography :title-bold - :line-height 21 - :margin-right 40 - :margin-left 16 - :text-align :left}} - displayed-name]]])) - -(defn- toolbar-action-items [public-key preferred-name] - [toolbar/actions - [{:icon :main-icons/share - :icon-opts {:width 24 - :height 24} - :handler #(profile.components/chat-key-popover public-key preferred-name)}]]) - (defn tribute-to-talk-item [opts] [list.views/big-list-item @@ -204,40 +170,44 @@ :on-press #(re-frame/dispatch [:multiaccounts.logout.ui/logout-pressed])}]) -(defn minimized-toolbar-handler [anim-opacity] - (let [{:keys [public-key preferred-name] - :as multiaccount} @(re-frame/subscribe [:multiaccount])] - [large-toolbar/minimized-toolbar-handler - (header-in-toolbar multiaccount) - nil - (toolbar-action-items public-key preferred-name) - anim-opacity])) - -(defn content-with-header [list-ref scroll-y] +(defn content [] (let [{:keys [preferred-name mnemonic keycard-pairing - notifications-enabled?] - :as multiaccount} @(re-frame/subscribe [:multiaccount]) + notifications-enabled?]} + @(re-frame/subscribe [:multiaccount]) + active-contacts-count @(re-frame/subscribe [:contacts/active-count]) tribute-to-talk @(re-frame/subscribe [:tribute-to-talk/profile]) - registrar @(re-frame/subscribe [:ens.stateofus/registrar]) - photo-added? @(re-frame/subscribe [:profile/photo-added?])] - [large-toolbar/flat-list-with-header-handler - (header multiaccount photo-added?) - (flat-list-content - preferred-name registrar tribute-to-talk - active-contacts-count mnemonic - keycard-pairing notifications-enabled?) - list-ref - scroll-y])) + registrar @(re-frame/subscribe [:ens.stateofus/registrar])] + (flat-list-content + preferred-name registrar tribute-to-talk + active-contacts-count mnemonic + keycard-pairing notifications-enabled?))) (defn my-profile [] - (let [list-ref (reagent/atom nil) - anim-opacity (animation/create-value 0) - scroll-y (animation/create-value 0)] - (large-toolbar/add-listener anim-opacity scroll-y) - (fn [] + (fn [] + (let [{:keys [public-key ens-verified preferred-name] + :as account} @(re-frame/subscribe [:multiaccount]) + on-share #(re-frame/dispatch [:show-popover + {:view :share-chat-key + :address public-key + :ens-name preferred-name}])] [react/view {:style {:flex 1}} - [minimized-toolbar-handler anim-opacity] - [content-with-header list-ref scroll-y]]))) + [quo/animated-header + {:right-accessories [{:icon :main-icons/share + :on-press on-share}] + :use-insets true + :extended-header (profile-header/extended-header + {:on-press on-share + :title (multiaccounts/displayed-name account) + :photo (multiaccounts/displayed-photo account) + :subtitle (if (and ens-verified public-key) + (gfy/generate-gfy public-key) + public-key)})} + [list.views/flat-list + {:data (content) + :initial-num-to-render 3 + :render-fn list.views/flat-list-generic-render-fn + :key-fn (fn [_ idx] (str idx)) + :keyboard-should-persist-taps :handled}]]]))) diff --git a/src/status_im/ui/screens/routing/chat_stack.cljs b/src/status_im/ui/screens/routing/chat_stack.cljs index 5d5e5a3452..0f8ee81a4f 100644 --- a/src/status_im/ui/screens/routing/chat_stack.cljs +++ b/src/status_im/ui/screens/routing/chat_stack.cljs @@ -22,8 +22,10 @@ {:name :chat :component chat/chat} {:name :profile + :insets {:top false} :component profile.contact/profile} {:name :group-chat-profile + :insets {:top false} :component profile.group-chat/group-chat-profile} {:name :stickers :component stickers/packs} diff --git a/src/status_im/ui/screens/routing/profile_stack.cljs b/src/status_im/ui/screens/routing/profile_stack.cljs index 00de459350..0f41c2484a 100644 --- a/src/status_im/ui/screens/routing/profile_stack.cljs +++ b/src/status_im/ui/screens/routing/profile_stack.cljs @@ -47,6 +47,7 @@ [stack {:initial-route-name :my-profile :header-mode :none} [{:name :my-profile + :insets {:top false} :style {:padding-bottom tabbar.styles/tabs-diff} :component profile.user/my-profile} {:name :contacts-list @@ -66,6 +67,7 @@ {:name :blocked-users-list :component contacts-list/blocked-users-list} {:name :profile + :insets {:top false} :component profile.contact/profile} {:name :profile-photo-capture :component photo-capture/profile-photo-capture} diff --git a/src/status_im/ui/screens/wallet/accounts/styles.cljs b/src/status_im/ui/screens/wallet/accounts/styles.cljs index 0827151cd7..ca5e955857 100644 --- a/src/status_im/ui/screens/wallet/accounts/styles.cljs +++ b/src/status_im/ui/screens/wallet/accounts/styles.cljs @@ -1,72 +1,29 @@ (ns status-im.ui.screens.wallet.accounts.styles - (:require [quo.animated :as reanimated] - [status-im.ui.components.colors :as colors] - [status-im.utils.platform :as platform])) + (:require [quo.animated :as animated] + [status-im.ui.components.colors :as colors])) -(def ^:const tabbar-height 56) -;; TODO(Ferossgp): get layout size of total-value -(def ^:const value-height (+ 40 22 8)) -(def ^:const scroll-offset value-height) -(def ^:const minimized-value-line-height 28) +(defn container [{:keys [minimized]}] + (when-not minimized + {:padding-bottom 8 + :padding-horizontal 16})) -(defn topbar [{:keys [value offset inset-top]}] - (merge - {:flex-direction :row - :padding-horizontal 8 - :background-color colors/white - :padding-top inset-top} - (when platform/android? - {:elevation (reanimated/interpolate - value - {:inputRange [0 offset] - :outputRange [0 4] - :extrapolate (:clamp reanimated/extrapolate)})}) - (when platform/ios? - {:shadow-opacity (reanimated/interpolate - value - {:inputRange [0 offset] - :outputRange [0 1] - :extrapolate (:clamp reanimated/extrapolate)}) - :shadow-radius 16 - :z-index 2 - :shadow-color (if (colors/dark?) - "rgba(0, 0, 0, 0.75)" - "rgba(0, 9, 26, 0.12)") - :shadow-offset {:width 0 :height 4}}))) +(defn value-container [{:keys [minimized animation]}] + (when minimized + {:opacity animation})) -(defn value-container [y] - {:position :absolute - :left 8 - :top 0 - :transform [{:translateY - (reanimated/interpolate - y - {:inputRange [0 scroll-offset] - :outputRange [scroll-offset - (/ (- tabbar-height - minimized-value-line-height) 2)] - :extrapolateRight (:clamp reanimated/extrapolate)})}]}) +(defn value-text [{:keys [minimized]}] + {:font-size (if minimized 20 32) + :line-height 40 + :color colors/black}) -(defn value-text [y] - {:font-size (reanimated/interpolate - y - {:inputRange [0 scroll-offset] - :outputRange [32 20] - :extrapolate (:clamp reanimated/extrapolate)}) - :color colors/black - :font-weight "600"}) - -(defn value-helper [y] - {:opacity (reanimated/interpolate y - {:inputRange [0 scroll-offset] - :outputRange [1 0]})}) - -(defn accounts-mnemonic [y] - {:flex 1 +(defn accounts-mnemonic [{:keys [animation]}] + {:opacity (animated/b-interpolate animation 1 0) + :flex 1 :justify-content :center - :opacity (reanimated/interpolate y - {:inputRange [0 scroll-offset] - :outputRange [1 0]})}) + :position :absolute + :top 0 + :bottom 0 + :left 0}) (defn card-common [] {:margin-vertical 16 diff --git a/src/status_im/ui/screens/wallet/accounts/views.cljs b/src/status_im/ui/screens/wallet/accounts/views.cljs index b74ab4d936..d94fbcff7c 100644 --- a/src/status_im/ui/screens/wallet/accounts/views.cljs +++ b/src/status_im/ui/screens/wallet/accounts/views.cljs @@ -1,6 +1,6 @@ (ns status-im.ui.screens.wallet.accounts.views - (:require [oops.core :refer [oget]] - [quo.animated :as reanimated] + (:require [quo.animated :as reanimated] + [quo.core :as quo] [re-frame.core :as re-frame] [reagent.core :as reagent] [status-im.i18n :as i18n] @@ -10,7 +10,6 @@ [status-im.ui.components.list-item.views :as list-item] [status-im.ui.components.list.views :as list] [status-im.ui.components.react :as react] - [status-im.ui.components.toolbar.styles :as toolbar.styles] [status-im.ui.screens.wallet.accounts.sheets :as sheets] [status-im.ui.screens.wallet.accounts.styles :as styles] [status-im.utils.utils :as utils.utils] @@ -94,20 +93,6 @@ :key-fn :name :render-fn (render-asset (:code currency) prices-loading?)}])) -(views/defview total-value [{:keys [y]}] - (views/letsubs [currency [:wallet/currency] - portfolio-value [:portfolio-value]] - [reanimated/view {:style (styles/value-container y)} - [reanimated/text {:style (styles/value-text y)} - portfolio-value - [reanimated/text {:style {:color colors/gray}} - (str " " (:code currency))]] - [reanimated/view {:style (styles/value-helper y)} - [react/text {:style {:color colors/gray - :font-size 15 - :line-height 22}} - (i18n/label :t/wallet-total-value)]]])) - (defn- request-camera-permissions [] (let [options {:handler :wallet.send/qr-scanner-result}] (re-frame/dispatch @@ -122,58 +107,6 @@ (i18n/label :t/camera-access-error))) 50)}]))) -(views/defview accounts-options [{:keys [y]}] - (views/letsubs [{:keys [mnemonic]} [:multiaccount] - empty-balances? [:empty-balances?]] - ;; TODO(Ferossgp): Use topbar component here - [react/safe-area-consumer - (fn [insets] - (reagent/as-element - [reanimated/view {:style (styles/topbar {:inset-top (oget insets "top") - :value y - :offset styles/scroll-offset})} - [react/view {:flex 1 - :height styles/tabbar-height - :padding-horizontal 8 - :flex-direction :row - :align-items :center} - [total-value {:y y}] - (when (and mnemonic - (not empty-balances?)) - [reanimated/view {:style (styles/accounts-mnemonic y)} - [react/touchable-highlight - {:on-press #(re-frame/dispatch [:navigate-to :backup-seed])} - [react/view {:flex-direction :row :align-items :center} - [react/view {:width 14 - :height 14 - :background-color colors/gray - :border-radius 7 - :align-items :center - :justify-content :center - :margin-right 9} - [react/text {:style {:color colors/white - :font-size 13 - :font-weight "700"}} - "!"]] - [react/text {:style {:color colors/gray} - :accessibility-label :back-up-your-seed-phrase-warning} - (i18n/label :t/back-up-your-seed-phrase)]]]])] - [react/touchable-highlight - {:on-press #(request-camera-permissions)} - [react/view {:height toolbar.styles/toolbar-height - :padding-horizontal 8 - :align-items :center - :justify-content :center} - [icons/icon :main-icons/qr {:accessibility-label :accounts-qr-code}]]] - [react/touchable-highlight - {:on-press #(re-frame/dispatch [:bottom-sheet/show-sheet - {:content (sheets/accounts-options mnemonic)}])} - [react/view {:height toolbar.styles/toolbar-height - :padding-horizontal 8 - :align-items :center - :justify-content :center} - [icons/icon :main-icons/more {:accessibility-label :accounts-more-options}]]]]))])) - (views/defview send-button [] (views/letsubs [account [:multiaccount/default-account]] [react/view styles/send-button-container @@ -194,17 +127,66 @@ [account-card account]) [add-card]]])) +(views/defview total-value [{:keys [animation minimized]}] + (views/letsubs [currency [:wallet/currency] + portfolio-value [:portfolio-value] + empty-balances? [:empty-balances?] + {:keys [mnemonic]} [:multiaccount]] + [reanimated/view {:style (styles/container {:minimized minimized}) + :pointer-events :none} + + (when (and mnemonic minimized (not empty-balances?)) + [reanimated/view {:style (styles/accounts-mnemonic {:animation animation})} + [react/touchable-highlight + {:on-press #(re-frame/dispatch [:navigate-to :backup-seed])} + [react/view {:flex-direction :row :align-items :center} + [react/view {:width 14 + :height 14 + :background-color colors/gray + :border-radius 7 + :align-items :center + :justify-content :center + :margin-right 9} + [react/text {:style {:color colors/white + :font-size 13 + :font-weight "700"}} + "!"]] + [react/text {:style {:color colors/gray} + :accessibility-label :back-up-your-seed-phrase-warning} + (i18n/label :t/back-up-your-seed-phrase)]]]]) + + [reanimated/view {:style (styles/value-container {:minimized minimized + :animation animation})} + [reanimated/view {:style {:justify-content :center}} + [quo/text {:animated? true + :weight :semi-bold + :style (styles/value-text {:minimized minimized})} + portfolio-value + [quo/text {:animated? true + :size :inherit + :weight :inherit + :color :secondary} + (str " " (:code currency))]]]] + (when-not minimized + [reanimated/view + [quo/text {:color :secondary} + (i18n/label :t/wallet-total-value)]])])) + (defn accounts-overview [] - (let [y (reanimated/value 0) - on-scroll (reanimated/on-scroll {:y y})] - (fn [] + (fn [] + (let [{:keys [mnemonic]} @(re-frame/subscribe [:multiaccount])] [react/view {:flex 1} - [accounts-options {:y y}] - [reanimated/scroll-view {:on-scroll on-scroll - :style {:padding-top styles/value-height} - :scrollEventThrottle 1} - [react/view {:margin-top 8} - [accounts]] + [quo/animated-header + {:extended-header total-value + :use-insets true + :right-accessories [{:on-press #(request-camera-permissions) + :icon :main-icons/qr + :accessibility-label :accounts-qr-code} + {:on-press #(re-frame/dispatch [:bottom-sheet/show-sheet + {:content (sheets/accounts-options mnemonic)}]) + :icon :main-icons/more + :accessibility-label :accounts-more-options}]} + [accounts] [assets] [react/view {:height 68}]] [send-button]]))) diff --git a/test/appium/views/profile_view.py b/test/appium/views/profile_view.py index 2f4c7a3a55..88e0c0ad5b 100644 --- a/test/appium/views/profile_view.py +++ b/test/appium/views/profile_view.py @@ -104,8 +104,7 @@ class ConfirmLogoutButton(BaseButton): class DefaultUserNameText(BaseText): def __init__(self, driver): super(DefaultUserNameText, self).__init__(driver) - self.locator = self.Locator.xpath_selector( - '//android.widget.ImageView[@content-desc="chat-icon"]/../android.widget.TextView') + self.locator = self.Locator.accessibility_id('default-username') class ENSusernames(BaseButton): def __init__(self, driver):