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 <feross95@gmail.com>
This commit is contained in:
Gheorghe Pinzaru 2020-04-23 17:05:26 +03:00
parent 01452794a1
commit d35deefcb5
No known key found for this signature in database
GPG Key ID: C9A094959935A952
15 changed files with 303 additions and 620 deletions

View File

@ -10,7 +10,7 @@
(defn header-wrapper-style [{:keys [value offset]}] (defn header-wrapper-style [{:keys [value offset]}]
(merge (merge
{:background-color :white} {:background-color (:ui-background @colors/theme)}
(when (and offset platform/android?) (when (and offset platform/android?)
{:elevation (animated/interpolate {:elevation (animated/interpolate
value value
@ -24,26 +24,21 @@
:outputRange [0 1] :outputRange [0 1]
:extrapolate (:clamp animated/extrapolate)}) :extrapolate (:clamp animated/extrapolate)})
:shadow-radius 16 :shadow-radius 16
:z-index 2
:shadow-color (:shadow-01 @colors/theme) :shadow-color (:shadow-01 @colors/theme)
:shadow-offset {:width 0 :height 4}}))) :shadow-offset {:width 0 :height 4}})))
(defn header-opened-style [{:keys [value offset]}] (defn title-style [layout]
(merge {:flex 1
{:position :absolute :justify-content :center
:top 0 :padding-right (get-in layout [:right :width])})
:left 0
:right 0}
(when offset
{:transform [{:translateY
(animated/interpolate
value
{:inputRange [0 offset]
:outputRange [0 (- header/header-height)]
:extrapolateRight (:clamp animated/extrapolate)})}]})))
(defn header-container [] (defn header-container []
(let [y (animated/value 0) (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}) on-scroll (animated/on-scroll {:y y})
layout (reagent/atom {}) layout (reagent/atom {})
offset (reagent/atom 0) offset (reagent/atom 0)
@ -52,23 +47,32 @@
(fn [{:keys [extended-header] :as props} & children] (fn [{:keys [extended-header] :as props} & children]
[animated/view {:flex 1 [animated/view {:flex 1
:pointer-events :box-none} :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 [animated/view {:pointer-events :box-none
:style (header-wrapper-style {:value y :style (header-wrapper-style {:value y
:offset @offset})} :offset @offset})}
[header/header (merge {:get-layout (fn [el l] (swap! layout assoc el l))} [header/header (merge
(dissoc props :extended-header))] {:get-layout (fn [el l] (swap! layout assoc el l))
[rn/view {:pointer-events :box-none} :title-component [animated/view {:style (title-style @layout)}
[animated/view {:style (header-opened-style {:value y
:offset @offset})
:pointer-events :box-none
:on-layout on-layout}
[extended-header {:value y [extended-header {:value y
:layout @layout :animation animation
:offset @offset}]]]] :minimized true
:offset @offset}]]
:title-align :left}
(dissoc props :extended-header))]]
(into [animated/scroll-view {:on-scroll on-scroll (into [animated/scroll-view {:on-scroll on-scroll
:scrollEventThrottle 1} :scrollEventThrottle 1}
[rn/view {:pointer-events :box-none [rn/view {:pointer-events :box-none}
:height @offset}]] [animated/view {:pointer-events :box-none
:on-layout on-layout}
[extended-header {:value y
:animation animation
:offset @offset}]]]]
children)]))) children)])))
(defn header [{:keys [use-insets] :as props} & children] (defn header [{:keys [use-insets] :as props} & children]

View File

@ -46,11 +46,7 @@
(defn title-style [{:keys [left right]} title-align] (defn title-style [{:keys [left right]} title-align]
(merge (merge
{:position :absolute absolute-fill
:justify-content :center
:top 0
:bottom 0}
(:tiny spacing/padding-horizontal)
(case title-align (case title-align
:left {:left (:width left) :left {:left (:width left)
:right (:width right)} :right (:width right)}
@ -67,7 +63,7 @@
(:tiny spacing/padding-horizontal))) (:tiny spacing/padding-horizontal)))
(def header-action-placeholder (def header-action-placeholder
{:width (:tiny spacing/spacing)}) {:width (:base spacing/spacing)})
(def header-icon-touchable (def header-icon-touchable
(merge (merge

View File

@ -33,7 +33,7 @@
(defn- wrap-render-fn [f] (defn- wrap-render-fn [f]
(fn [data] (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] (defn- wrap-key-fn [f]
(fn [data index] (fn [data index]

View File

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

View File

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

View File

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

View File

@ -1,103 +1,12 @@
(ns status-im.ui.screens.profile.components.views (ns status-im.ui.screens.profile.components.views
(:require [clojure.string :as string] (:require [clojure.string :as string]
[re-frame.core :as re-frame]
[status-im.multiaccounts.core :as multiaccounts]
[status-im.i18n :as i18n] [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.colors :as colors]
[status-im.ui.components.common.common :as common] [status-im.ui.components.common.common :as common]
[status-im.ui.components.icons.vector-icons :as vector-icons] [status-im.ui.components.icons.vector-icons :as vector-icons]
[status-im.ui.components.react :as react] [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])) [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 ;; settings items elements
(defn settings-item (defn settings-item

View File

@ -6,15 +6,14 @@
[status-im.utils.utils :as utils] [status-im.utils.utils :as utils]
[status-im.ui.components.icons.vector-icons :as icons] [status-im.ui.components.icons.vector-icons :as icons]
[status-im.ui.components.react :as react] [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.screens.profile.contact.styles :as styles]
[status-im.ui.components.list-item.views :as list-item] [status-im.ui.components.list-item.views :as list-item]
[status-im.ui.components.chat-icon.screen :as chat-icon] [status-im.ui.components.chat-icon.screen :as chat-icon]
[status-im.ui.screens.profile.components.sheets :as sheets] [status-im.ui.screens.profile.components.sheets :as sheets]
[status-im.ui.screens.chat.photos :as photos] [status-im.ui.components.profile-header.view :as profile-header]
[status-im.multiaccounts.core :as multiaccounts]) [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])) (:require-macros [status-im.utils.views :as views]))
(defn actions (defn actions
@ -89,51 +88,34 @@
(i18n/label :t/unblock-contact) (i18n/label :t/unblock-contact)
(i18n/label :t/block-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/defview profile []
(views/letsubs [list-ref (reagent/atom nil) (views/letsubs [{:keys [ens-verified name public-key]
{:keys [ens-verified name public-key] :as contact} [:contacts/current-contact]] :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 (when contact
(let [ens-name (when (and ens-verified name) name) [react/view
contact (cond-> contact {:style
ens-name (merge {:flex 1})}
(assoc :usernames [ens-name] [quo/animated-header
:ens-name ens-name)) {:use-insets true
header-in-toolbar (header-in-toolbar contact) :right-accessories [{:icon :main-icons/share
header (header contact) :on-press on-share}]
content :left-accessories [{:icon :main-icons/back
[[react/view {:padding-top 12} :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)] (for [{:keys [label subtext accessibility-label icon action disabled?]} (actions contact)]
[list-item/list-item {:theme :action [list-item/list-item {:theme :action
:title label :title label
@ -143,17 +125,7 @@
:disabled? disabled? :disabled? disabled?
:on-press action}])] :on-press action}])]
[react/view styles/contact-profile-details-container [react/view styles/contact-profile-details-container
[profile-details contact]] [profile-details (cond-> contact
[block-contact-action contact]] (and ens-verified name)
generated-view (large-toolbar/generate-view (assoc :ens-name name))]]
header-in-toolbar [block-contact-action contact]]]))))
toolbar/default-nav-back
(toolbar-action-items public-key ens-name)
header
content
list-ref)]
[react/view
{:style
(merge {:flex 1})}
(:minimized-toolbar generated-view)
(:content-with-header generated-view)]))))

View File

@ -14,16 +14,9 @@
[status-im.ui.components.colors :as colors] [status-im.ui.components.colors :as colors]
[re-frame.core :as re-frame] [re-frame.core :as re-frame]
[status-im.i18n :as i18n] [status-im.i18n :as i18n]
[status-im.ui.components.list-item.views :as list-item] [quo.core :as quo]
[status-im.ui.components.topbar :as topbar])) [status-im.ui.components.profile-header.view :as profile-header]
[status-im.ui.components.list-item.views :as list-item]))
(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])}]})])
(defn member-sheet [chat-id member us-admin?] (defn member-sheet [chat-id member us-admin?]
[react/view [react/view
@ -92,24 +85,29 @@
[chat-group-members-view chat-id admin? current-pk]]) [chat-group-members-view chat-id admin? current-pk]])
(defview group-chat-profile [] (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] members [:contacts/current-chat-contacts]
changed-chat [:group-chat-profile/profile]
current-pk [:multiaccount/public-key]] current-pk [:multiaccount/public-key]]
(when current-chat (when current-chat
(let [shown-chat (merge current-chat changed-chat) (let [admin? (get admins current-pk)
admin? (get admins current-pk)
allow-adding-members? (and admin? joined? allow-adding-members? (and admin? joined?
(< (count members) constants/max-group-chat-participants))] (< (count members) constants/max-group-chat-participants))]
[react/view profile.components.styles/profile [react/view profile.components.styles/profile
[group-chat-profile-toolbar (and admin? joined?)] [quo/animated-header
[react/scroll-view {: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 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? (when joined?
[list-item/list-item [list-item/list-item
{:theme :action {:theme :action

View File

@ -2,24 +2,22 @@
(:require [re-frame.core :as re-frame] (:require [re-frame.core :as re-frame]
[reagent.core :as reagent] [reagent.core :as reagent]
[status-im.i18n :as i18n] [status-im.i18n :as i18n]
[status-im.multiaccounts.core :as multiaccounts]
[status-im.ui.components.button :as button] [status-im.ui.components.button :as button]
[status-im.ui.components.colors :as colors] [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.common.common :as components.common]
[status-im.ui.components.copyable-text :as copyable-text] [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-selection :as list-selection]
[status-im.ui.components.list.views :as list.views] [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.qr-code-viewer.views :as qr-code-viewer]
[status-im.ui.components.react :as react] [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.ui.screens.profile.user.styles :as styles]
[status-im.utils.platform :as platform] [status-im.utils.platform :as platform]
[status-im.utils.config :as config] [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.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])) (:require-macros [status-im.utils.views :as views]))
(views/defview share-chat-key [] (views/defview share-chat-key []
@ -61,38 +59,6 @@
;:icon :main-icons/link ;:icon :main-icons/link
:accessibility-label :share-my-contact-code-button}]]]))) :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 (defn tribute-to-talk-item
[opts] [opts]
[list.views/big-list-item [list.views/big-list-item
@ -204,40 +170,44 @@
:on-press :on-press
#(re-frame/dispatch [:multiaccounts.logout.ui/logout-pressed])}]) #(re-frame/dispatch [:multiaccounts.logout.ui/logout-pressed])}])
(defn minimized-toolbar-handler [anim-opacity] (defn content []
(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]
(let [{:keys [preferred-name (let [{:keys [preferred-name
mnemonic mnemonic
keycard-pairing keycard-pairing
notifications-enabled?] notifications-enabled?]}
:as multiaccount} @(re-frame/subscribe [:multiaccount]) @(re-frame/subscribe [:multiaccount])
active-contacts-count @(re-frame/subscribe [:contacts/active-count]) active-contacts-count @(re-frame/subscribe [:contacts/active-count])
tribute-to-talk @(re-frame/subscribe [:tribute-to-talk/profile]) tribute-to-talk @(re-frame/subscribe [:tribute-to-talk/profile])
registrar @(re-frame/subscribe [:ens.stateofus/registrar]) 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 (flat-list-content
preferred-name registrar tribute-to-talk preferred-name registrar tribute-to-talk
active-contacts-count mnemonic active-contacts-count mnemonic
keycard-pairing notifications-enabled?) keycard-pairing notifications-enabled?)))
list-ref
scroll-y]))
(defn my-profile [] (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}} [react/view {:style {:flex 1}}
[minimized-toolbar-handler anim-opacity] [quo/animated-header
[content-with-header list-ref scroll-y]]))) {: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}]]])))

View File

@ -22,8 +22,10 @@
{:name :chat {:name :chat
:component chat/chat} :component chat/chat}
{:name :profile {:name :profile
:insets {:top false}
:component profile.contact/profile} :component profile.contact/profile}
{:name :group-chat-profile {:name :group-chat-profile
:insets {:top false}
:component profile.group-chat/group-chat-profile} :component profile.group-chat/group-chat-profile}
{:name :stickers {:name :stickers
:component stickers/packs} :component stickers/packs}

View File

@ -47,6 +47,7 @@
[stack {:initial-route-name :my-profile [stack {:initial-route-name :my-profile
:header-mode :none} :header-mode :none}
[{:name :my-profile [{:name :my-profile
:insets {:top false}
:style {:padding-bottom tabbar.styles/tabs-diff} :style {:padding-bottom tabbar.styles/tabs-diff}
:component profile.user/my-profile} :component profile.user/my-profile}
{:name :contacts-list {:name :contacts-list
@ -66,6 +67,7 @@
{:name :blocked-users-list {:name :blocked-users-list
:component contacts-list/blocked-users-list} :component contacts-list/blocked-users-list}
{:name :profile {:name :profile
:insets {:top false}
:component profile.contact/profile} :component profile.contact/profile}
{:name :profile-photo-capture {:name :profile-photo-capture
:component photo-capture/profile-photo-capture} :component photo-capture/profile-photo-capture}

View File

@ -1,72 +1,29 @@
(ns status-im.ui.screens.wallet.accounts.styles (ns status-im.ui.screens.wallet.accounts.styles
(:require [quo.animated :as reanimated] (:require [quo.animated :as animated]
[status-im.ui.components.colors :as colors] [status-im.ui.components.colors :as colors]))
[status-im.utils.platform :as platform]))
(def ^:const tabbar-height 56) (defn container [{:keys [minimized]}]
;; TODO(Ferossgp): get layout size of total-value (when-not minimized
(def ^:const value-height (+ 40 22 8)) {:padding-bottom 8
(def ^:const scroll-offset value-height) :padding-horizontal 16}))
(def ^:const minimized-value-line-height 28)
(defn topbar [{:keys [value offset inset-top]}] (defn value-container [{:keys [minimized animation]}]
(merge (when minimized
{:flex-direction :row {:opacity animation}))
: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 [y] (defn value-text [{:keys [minimized]}]
{:position :absolute {:font-size (if minimized 20 32)
:left 8 :line-height 40
:top 0 :color colors/black})
: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 [y] (defn accounts-mnemonic [{:keys [animation]}]
{:font-size (reanimated/interpolate {:opacity (animated/b-interpolate animation 1 0)
y :flex 1
{: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
:justify-content :center :justify-content :center
:opacity (reanimated/interpolate y :position :absolute
{:inputRange [0 scroll-offset] :top 0
:outputRange [1 0]})}) :bottom 0
:left 0})
(defn card-common [] (defn card-common []
{:margin-vertical 16 {:margin-vertical 16

View File

@ -1,6 +1,6 @@
(ns status-im.ui.screens.wallet.accounts.views (ns status-im.ui.screens.wallet.accounts.views
(:require [oops.core :refer [oget]] (:require [quo.animated :as reanimated]
[quo.animated :as reanimated] [quo.core :as quo]
[re-frame.core :as re-frame] [re-frame.core :as re-frame]
[reagent.core :as reagent] [reagent.core :as reagent]
[status-im.i18n :as i18n] [status-im.i18n :as i18n]
@ -10,7 +10,6 @@
[status-im.ui.components.list-item.views :as list-item] [status-im.ui.components.list-item.views :as list-item]
[status-im.ui.components.list.views :as list] [status-im.ui.components.list.views :as list]
[status-im.ui.components.react :as react] [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.sheets :as sheets]
[status-im.ui.screens.wallet.accounts.styles :as styles] [status-im.ui.screens.wallet.accounts.styles :as styles]
[status-im.utils.utils :as utils.utils] [status-im.utils.utils :as utils.utils]
@ -94,20 +93,6 @@
:key-fn :name :key-fn :name
:render-fn (render-asset (:code currency) prices-loading?)}])) :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 [] (defn- request-camera-permissions []
(let [options {:handler :wallet.send/qr-scanner-result}] (let [options {:handler :wallet.send/qr-scanner-result}]
(re-frame/dispatch (re-frame/dispatch
@ -122,58 +107,6 @@
(i18n/label :t/camera-access-error))) (i18n/label :t/camera-access-error)))
50)}]))) 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/defview send-button []
(views/letsubs [account [:multiaccount/default-account]] (views/letsubs [account [:multiaccount/default-account]]
[react/view styles/send-button-container [react/view styles/send-button-container
@ -194,17 +127,66 @@
[account-card account]) [account-card account])
[add-card]]])) [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 [] (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} [react/view {:flex 1}
[accounts-options {:y y}] [quo/animated-header
[reanimated/scroll-view {:on-scroll on-scroll {:extended-header total-value
:style {:padding-top styles/value-height} :use-insets true
:scrollEventThrottle 1} :right-accessories [{:on-press #(request-camera-permissions)
[react/view {:margin-top 8} :icon :main-icons/qr
[accounts]] :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] [assets]
[react/view {:height 68}]] [react/view {:height 68}]]
[send-button]]))) [send-button]])))

View File

@ -104,8 +104,7 @@ class ConfirmLogoutButton(BaseButton):
class DefaultUserNameText(BaseText): class DefaultUserNameText(BaseText):
def __init__(self, driver): def __init__(self, driver):
super(DefaultUserNameText, self).__init__(driver) super(DefaultUserNameText, self).__init__(driver)
self.locator = self.Locator.xpath_selector( self.locator = self.Locator.accessibility_id('default-username')
'//android.widget.ImageView[@content-desc="chat-icon"]/../android.widget.TextView')
class ENSusernames(BaseButton): class ENSusernames(BaseButton):
def __init__(self, driver): def __init__(self, driver):