parent
da120feef8
commit
f7076016d8
|
@ -80,6 +80,9 @@
|
|||
(def animated-view-class
|
||||
(reagent/adapt-react-class (.-View animated)))
|
||||
|
||||
(def animated-flat-list-class
|
||||
(reagent/adapt-react-class (.-FlatList animated)))
|
||||
|
||||
(defn animated-view [props & content]
|
||||
(vec (conj content props animated-view-class)))
|
||||
|
||||
|
|
|
@ -522,6 +522,7 @@ var TopLevel = {
|
|||
"version" : function () {},
|
||||
"vibrate" : function () {},
|
||||
"View" : function () {},
|
||||
"FlatList" : function () {},
|
||||
"warn" : function () {},
|
||||
"WebView" : function () {},
|
||||
"WebViewBridgeModule" : function () {},
|
||||
|
|
|
@ -14,8 +14,9 @@
|
|||
(def qr-code (fn [] #js {}))
|
||||
(def react-native
|
||||
#js {:NativeModules #js {}
|
||||
:Animated #js {:View #js {}
|
||||
:Text #js {}}
|
||||
:Animated #js {:View #js {}
|
||||
:FlatList #js {}
|
||||
:Text #js {}}
|
||||
:DeviceEventEmitter #js {:addListener (fn [])}
|
||||
:Dimensions #js {:get (fn [])}})
|
||||
(def vector-icons (fn [] #js {:default #js {}}))
|
||||
|
|
Before Width: | Height: | Size: 223 B After Width: | Height: | Size: 223 B |
Before Width: | Height: | Size: 391 B After Width: | Height: | Size: 391 B |
Before Width: | Height: | Size: 537 B After Width: | Height: | Size: 537 B |
|
@ -87,11 +87,3 @@
|
|||
(update :contacts/blocked disj public-key)
|
||||
(assoc-in [:contacts/contacts public-key] contact))}
|
||||
(contacts-store/save-contact contact))))
|
||||
|
||||
(fx/defn block-contact-confirmation
|
||||
[cofx public-key]
|
||||
{:utils/show-confirmation
|
||||
{:title (i18n/label :t/block-contact)
|
||||
:content (i18n/label :t/block-contact-details)
|
||||
:confirm-button-text (i18n/label :t/to-block)
|
||||
:on-accept #(re-frame/dispatch [:contact.ui/block-contact-confirmed public-key])}})
|
||||
|
|
|
@ -81,6 +81,17 @@
|
|||
(send-contact-request contact)
|
||||
(mailserver/process-next-messages-request)))))
|
||||
|
||||
(fx/defn remove-contact
|
||||
"Remove a contact from current account's contact list"
|
||||
{:events [:contact.ui/remove-contact-pressed]}
|
||||
[{:keys [db] :as cofx} {:keys [public-key] :as contact}]
|
||||
(let [new-contact (update contact
|
||||
:system-tags
|
||||
(fnil #(disj % :contact/added) #{}))]
|
||||
(fx/merge cofx
|
||||
{:db (assoc-in db [:contacts/contacts public-key] new-contact)}
|
||||
(contacts-store/save-contact new-contact))))
|
||||
|
||||
(fx/defn create-contact
|
||||
"Create entry in contacts"
|
||||
[{:keys [db] :as cofx} public-key]
|
||||
|
|
|
@ -1327,11 +1327,6 @@
|
|||
(fn [cofx [_ public-key]]
|
||||
(contact/add-contact cofx public-key)))
|
||||
|
||||
(handlers/register-handler-fx
|
||||
:contact.ui/block-contact-pressed
|
||||
(fn [cofx [_ public-key]]
|
||||
(contact.block/block-contact-confirmation cofx public-key)))
|
||||
|
||||
(handlers/register-handler-fx
|
||||
:contact.ui/block-contact-confirmed
|
||||
(fn [cofx [_ public-key]]
|
||||
|
|
|
@ -42,8 +42,8 @@
|
|||
(defn anim-delay [duration]
|
||||
(.delay react/animated duration))
|
||||
|
||||
(defn event [config]
|
||||
(.event react/animated (clj->js [nil, config])))
|
||||
(defn event [mapping config]
|
||||
(.event react/animated (clj->js mapping) (clj->js config)))
|
||||
|
||||
(defn add-listener [anim-value listener]
|
||||
(.addListener anim-value listener))
|
||||
|
@ -60,6 +60,12 @@
|
|||
(defn create-value [value]
|
||||
(js/ReactNative.Animated.Value. value))
|
||||
|
||||
(defn add [anim-x anim-y]
|
||||
(js/ReactNative.Animated.add. anim-x anim-y))
|
||||
|
||||
(defn subtract [anim-x anim-y]
|
||||
(js/ReactNative.Animated.subtract. anim-x anim-y))
|
||||
|
||||
(defn x [value-xy]
|
||||
(.-x value-xy))
|
||||
|
||||
|
|
|
@ -1,151 +0,0 @@
|
|||
(ns status-im.ui.components.large-toolbar
|
||||
(:require-macros [status-im.utils.views :as views])
|
||||
(:require [cljs-bean.core :refer [->clj ->js]]
|
||||
[reagent.core :as reagent]
|
||||
[status-im.ui.components.animation :as animation]
|
||||
[status-im.ui.components.colors :as colors]
|
||||
[status-im.ui.components.list.views :as list.views]
|
||||
[status-im.ui.components.list-item.views :as list-item]
|
||||
[status-im.ui.components.react :as react]
|
||||
[status-im.ui.components.toolbar.styles :as toolbar.styles]
|
||||
[status-im.ui.components.toolbar.view :as toolbar]
|
||||
[status-im.utils.platform :as platform]))
|
||||
|
||||
(def hidden (reagent/atom 0))
|
||||
(def shown (reagent/atom 100))
|
||||
(def minimized-header-visible? (reagent/atom false))
|
||||
(def initial-on-show-done? (volatile! false))
|
||||
|
||||
(defn animated-content-wrapper [header-in-toolbar has-nav? show?]
|
||||
(let [anim-opacity (animation/create-value 0)
|
||||
to-hide (reagent/atom false)]
|
||||
(reagent/create-class
|
||||
{:component-did-update
|
||||
(fn [comp]
|
||||
(let [new-argv (rest (reagent/argv comp))
|
||||
show? (last new-argv)]
|
||||
(cond
|
||||
(and (not @to-hide) show?)
|
||||
(animation/start
|
||||
(animation/timing
|
||||
anim-opacity
|
||||
{:toValue 1
|
||||
:duration 200
|
||||
:easing (.-ease (animation/easing))
|
||||
:useNativeDriver true})
|
||||
#(reset! to-hide true))
|
||||
|
||||
(and @to-hide (not show?))
|
||||
(animation/start
|
||||
(animation/timing
|
||||
anim-opacity
|
||||
{:toValue 0
|
||||
:duration 200
|
||||
:easing (.-ease (animation/easing))
|
||||
:useNativeDriver true})
|
||||
#(reset! to-hide false)))))
|
||||
|
||||
:reagent-render
|
||||
(fn [header-in-toolbar has-nav? _]
|
||||
[react/animated-view
|
||||
{:style (cond-> {:flex 1
|
||||
:align-self :stretch
|
||||
:opacity anim-opacity}
|
||||
(false? has-nav?)
|
||||
(assoc :margin-left -40 :margin-right 40))}
|
||||
header-in-toolbar])})))
|
||||
|
||||
(defn on-viewable-items-changed [threshold interporlation-step]
|
||||
(fn [info]
|
||||
(let [changed (->> (->clj info)
|
||||
:changed
|
||||
(filter #(= 1 (:index %))))
|
||||
viewable? (when (seq changed)
|
||||
(->> changed
|
||||
first
|
||||
:isViewable))]
|
||||
(when (and @initial-on-show-done? (not (nil? viewable?)))
|
||||
(if (= threshold 0)
|
||||
(if viewable?
|
||||
(reset! minimized-header-visible? false)
|
||||
(reset! minimized-header-visible? true))
|
||||
(if viewable?
|
||||
(do (swap! hidden - interporlation-step) (swap! shown + interporlation-step))
|
||||
(do (swap! hidden + interporlation-step) (swap! shown - interporlation-step))))))))
|
||||
|
||||
(defonce viewability-config-callback-pairs
|
||||
(let [interporlation-step 20]
|
||||
(->js
|
||||
(vec
|
||||
(for [threshold (range 0 (+ 100 interporlation-step) interporlation-step)]
|
||||
{:viewabilityConfig {:itemVisiblePercentThreshold threshold}
|
||||
:onViewableItemsChanged (on-viewable-items-changed threshold interporlation-step)})))))
|
||||
|
||||
;; 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]
|
||||
(let [has-nav? (boolean nav-item)]
|
||||
[toolbar/toolbar
|
||||
{:transparent? true
|
||||
:style {:z-index 100
|
||||
:elevation 9}}
|
||||
nav-item
|
||||
[animated-content-wrapper header-in-toolbar has-nav? @minimized-header-visible?]
|
||||
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
|
||||
(views/defview flat-list-with-large-header [header content list-ref]
|
||||
(views/letsubs [window-width [:dimensions/window-width]]
|
||||
{:component-did-mount #(do (reset! hidden 0) (reset! shown 100)
|
||||
(reset! minimized-header-visible? false)
|
||||
(vreset! initial-on-show-done? false))}
|
||||
(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 {:height 16
|
||||
:opacity (/ @shown 100)
|
||||
:border-bottom-width 1
|
||||
:border-bottom-color colors/gray-lighter}}]
|
||||
wrapped-data (into [header-top-padding header header-bottom] content)
|
||||
status-bar-height (get platform/platform-specific :status-bar-default-height)
|
||||
toolbar-shadow-component-height
|
||||
(+ 50 toolbar.styles/toolbar-height (if (zero? status-bar-height) 50 status-bar-height))]
|
||||
[react/view {:flex 1}
|
||||
;; toolbar shadow
|
||||
[react/animated-view
|
||||
{:style
|
||||
(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 (if (>= @hidden 40) (- (/ @hidden 10) 2) 0)
|
||||
:background-color colors/white}
|
||||
platform/ios?
|
||||
(assoc :opacity (if (>= @hidden 40) (/ @hidden 100) 0)))}]
|
||||
|
||||
[list.views/flat-list
|
||||
{:style {:z-index -1}
|
||||
:data wrapped-data
|
||||
:initial-num-to-render 3
|
||||
:ref #(reset! list-ref %)
|
||||
:render-fn list.views/flat-list-generic-render-fn
|
||||
:key-fn (fn [item idx] (str idx))
|
||||
:on-scroll-begin-drag #(when (false? @initial-on-show-done?)
|
||||
(vreset! initial-on-show-done? true))
|
||||
:viewabilityConfigCallbackPairs viewability-config-callback-pairs
|
||||
:keyboard-should-persist-taps :handled}]])))
|
|
@ -0,0 +1,84 @@
|
|||
(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 (animation/easing))
|
||||
:useNativeDriver true}))
|
||||
|
||||
(defn minimized-toolbar-fade-out [anim-opacity]
|
||||
(animation/timing
|
||||
anim-opacity
|
||||
{:toValue 0
|
||||
:duration 200
|
||||
:easing (.-ease (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})
|
|
@ -0,0 +1,85 @@
|
|||
(ns status-im.ui.components.large-toolbar.view
|
||||
(:require [reagent.core :as reagent]
|
||||
[cljs-bean.core :refer [->clj ->js]]
|
||||
[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.utils.platform :as platform]
|
||||
[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 %)))
|
||||
: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 false)
|
||||
anim-opacity (animation/create-value 0)
|
||||
scroll-y (animation/create-value 0)]
|
||||
(animation/add-listener scroll-y (fn [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]}))
|
|
@ -223,13 +223,15 @@
|
|||
(defn flat-list
|
||||
"A wrapper for FlatList.
|
||||
See https://facebook.github.io/react-native/docs/flatlist.html"
|
||||
[{:keys [data] :as props}]
|
||||
{:pre [(or (nil? data)
|
||||
(sequential? data))]}
|
||||
[flat-list-class
|
||||
(merge (base-list-props props)
|
||||
props
|
||||
{:data (wrap-data data)})])
|
||||
([props] (flat-list props nil))
|
||||
([{:keys [data] :as props} {:keys [animated?]}]
|
||||
(let [class (if animated? react/animated-flat-list-class flat-list-class)]
|
||||
{:pre [(or (nil? data)
|
||||
(sequential? data))]}
|
||||
[class
|
||||
(merge (base-list-props props)
|
||||
props
|
||||
{:data (wrap-data data)})])))
|
||||
|
||||
(defn flat-list-generic-render-fn
|
||||
"A generic status-react specific `render-fn` for `list-item`.
|
||||
|
@ -283,13 +285,14 @@
|
|||
[react/touchable-highlight {:on-press action}
|
||||
[react/view {:accessibility-label accessibility-label}
|
||||
[item
|
||||
[item-icon {:icon icon
|
||||
:style (merge styles/action
|
||||
action-style
|
||||
(when disabled? styles/action-disabled))
|
||||
:icon-opts (merge {:color :white}
|
||||
icon-opts
|
||||
(when disabled? {:color colors/gray}))}]
|
||||
(when icon
|
||||
[item-icon {:icon icon
|
||||
:style (merge styles/action
|
||||
action-style
|
||||
(when disabled? styles/action-disabled))
|
||||
:icon-opts (merge {:color :white}
|
||||
icon-opts
|
||||
(when disabled? {:color colors/gray}))}])
|
||||
(if-not subtext
|
||||
[item-primary-only {:style (merge styles/action-label
|
||||
(action-label-style false)
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
(ns status-im.ui.screens.profile.components.sheets
|
||||
(:require [re-frame.core :as re-frame]
|
||||
[status-im.ui.components.react :as react]
|
||||
[status-im.i18n :as i18n]
|
||||
[status-im.ui.components.colors :as colors]
|
||||
[status-im.ui.components.list-item.views :as list-item]
|
||||
[status-im.ui.screens.profile.components.styles :as styles])
|
||||
(:require-macros [status-im.utils.views :as views]))
|
||||
|
||||
(defn hide-sheet-and-dispatch [event]
|
||||
(re-frame/dispatch [:bottom-sheet/hide-sheet])
|
||||
(re-frame/dispatch event))
|
||||
|
||||
(views/defview add-contact []
|
||||
(views/letsubs [{:keys [public-key]} [:bottom-sheet/options]]
|
||||
[react/view
|
||||
[react/text {:style styles/sheet-text}
|
||||
(i18n/label :t/add-to-contacts-text)]
|
||||
[list-item/list-item
|
||||
{:theme :action
|
||||
:title :t/add-to-contacts
|
||||
:icon :main-icons/add-contact
|
||||
:on-press #(hide-sheet-and-dispatch [:contact.ui/add-to-contact-pressed public-key])}]]))
|
||||
|
||||
(views/defview remove-contact []
|
||||
(views/letsubs [contact [:bottom-sheet/options]]
|
||||
[react/view
|
||||
[react/text {:style styles/sheet-text}
|
||||
(i18n/label :t/remove-from-contacts-text)]
|
||||
[list-item/list-item
|
||||
{:theme :action-destructive
|
||||
:title :t/remove-from-contacts
|
||||
:icon :main-icons/remove-contact
|
||||
:on-press #(hide-sheet-and-dispatch [:contact.ui/remove-contact-pressed contact])}]]))
|
||||
|
||||
(views/defview block-contact []
|
||||
(views/letsubs [{:keys [public-key]} [:bottom-sheet/options]]
|
||||
[react/view
|
||||
[react/text {:style styles/sheet-text}
|
||||
(i18n/label :t/block-contact-details)]
|
||||
[list-item/list-item
|
||||
{:theme :action-destructive
|
||||
:title :t/block-contact
|
||||
:on-press #(hide-sheet-and-dispatch [:contact.ui/block-contact-confirmed public-key])}]]))
|
|
@ -113,3 +113,11 @@
|
|||
|
||||
(def profile-form
|
||||
{:padding-vertical 16})
|
||||
|
||||
;; sheets
|
||||
|
||||
(def sheet-text
|
||||
{:color colors/gray
|
||||
:padding 24
|
||||
:line-height 22
|
||||
:font-size 15})
|
|
@ -2,28 +2,6 @@
|
|||
(:require-macros [status-im.utils.styles :refer [defstyle]])
|
||||
(:require [status-im.ui.components.colors :as colors]))
|
||||
|
||||
(def network-info {:background-color :white})
|
||||
|
||||
(def profile-info-item
|
||||
{:flex-direction :row
|
||||
:align-items :center
|
||||
:padding-left 16})
|
||||
|
||||
(defn profile-info-text-container [options]
|
||||
{:flex 1
|
||||
:padding-right (if options 16 40)})
|
||||
|
||||
(def profile-info-title
|
||||
{:color colors/gray
|
||||
:font-size 14})
|
||||
|
||||
(defstyle profile-setting-spacing
|
||||
{:ios {:height 10}
|
||||
:android {:height 7}})
|
||||
|
||||
(def profile-setting-text
|
||||
{:font-size 17})
|
||||
|
||||
(def action-container
|
||||
{:background-color colors/white})
|
||||
|
||||
|
@ -51,20 +29,14 @@
|
|||
(def action-icon-opts
|
||||
{:color colors/blue})
|
||||
|
||||
(def block-action
|
||||
{:background-color colors/red-transparent-10
|
||||
:border-radius 50})
|
||||
(def block-action-label
|
||||
{:color colors/red
|
||||
:padding-top 26
|
||||
:margin-left 16})
|
||||
|
||||
(defn block-action-label [with-subtext?]
|
||||
{:color colors/red})
|
||||
|
||||
(def block-action-icon-opts
|
||||
{:color colors/red})
|
||||
|
||||
(def profile-setting-text-empty
|
||||
(merge profile-setting-text
|
||||
{:color colors/gray}))
|
||||
|
||||
(def contact-profile-info-container
|
||||
(def contact-profile-details-container
|
||||
{:padding-top 26
|
||||
:background-color colors/white})
|
||||
|
||||
(def contact-profile-detail-share-icon
|
||||
{:color colors/gray-transparent-40})
|
||||
|
|
|
@ -1,105 +1,145 @@
|
|||
(ns status-im.ui.screens.profile.contact.views
|
||||
(:require [re-frame.core :as re-frame]
|
||||
[reagent.core :as reagent]
|
||||
[status-im.i18n :as i18n]
|
||||
[status-im.ui.components.list.views :as list]
|
||||
[status-im.utils.utils :as utils]
|
||||
[status-im.utils.platform :as platform]
|
||||
[status-im.ui.components.tabbar.styles :as tabs.styles]
|
||||
[status-im.ui.components.icons.vector-icons :as icons]
|
||||
[status-im.ui.components.react :as react]
|
||||
[status-im.ui.components.status-bar.view :as status-bar]
|
||||
[status-im.ui.components.large-toolbar.view :as large-toolbar]
|
||||
[status-im.ui.components.toolbar.view :as toolbar]
|
||||
[status-im.ui.screens.profile.components.styles :as profile.components.styles]
|
||||
[status-im.ui.screens.profile.components.views :as profile.components]
|
||||
[status-im.ui.screens.profile.contact.styles :as styles]
|
||||
[status-im.utils.platform :as platform])
|
||||
(:require-macros [status-im.utils.views :refer [defview letsubs]]))
|
||||
|
||||
(defn profile-contact-toolbar []
|
||||
[toolbar/toolbar {}
|
||||
toolbar/default-nav-back
|
||||
[toolbar/content-title ""]])
|
||||
[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])
|
||||
(:require-macros [status-im.utils.views :as views]))
|
||||
|
||||
(defn actions
|
||||
[{:keys [public-key added? tribute-to-talk] :as contact}]
|
||||
(let [{:keys [tribute-status tribute-label]} tribute-to-talk]
|
||||
(concat (if added?
|
||||
[{:label (i18n/label :t/in-contacts)
|
||||
:icon :main-icons/in-contacts
|
||||
:disabled? true
|
||||
:accessibility-label :in-contacts-button}]
|
||||
[{:label (i18n/label :t/add-to-contacts)
|
||||
:icon :main-icons/add-contact
|
||||
:action #(re-frame/dispatch [:contact.ui/add-to-contact-pressed public-key])
|
||||
:accessibility-label :add-to-contacts-button}])
|
||||
[(cond-> {:label (i18n/label :t/send-message)
|
||||
(concat [(cond-> {:label (i18n/label :t/send-message)
|
||||
:icon :main-icons/message
|
||||
:action #(re-frame/dispatch [:contact.ui/send-message-pressed {:public-key public-key}])
|
||||
:accessibility-label :start-conversation-button}
|
||||
(not (#{:none :paid} tribute-status))
|
||||
(assoc :subtext tribute-label))
|
||||
(assoc :subtext tribute-label))]
|
||||
;;TODO hide temporary for v1
|
||||
#_{:label (i18n/label :t/send-transaction)
|
||||
:icon :main-icons/send
|
||||
:action #(re-frame/dispatch [:profile/send-transaction public-key])
|
||||
:accessibility-label :send-transaction-button}
|
||||
{:label (i18n/label :t/share-profile-link)
|
||||
:icon :main-icons/share
|
||||
:action #(re-frame/dispatch [:profile/share-profile-link public-key])
|
||||
:accessibility-label :share-profile-link}])))
|
||||
#_{:label (i18n/label :t/send-transaction)
|
||||
:icon :main-icons/send
|
||||
:action #(re-frame/dispatch [:profile/send-transaction public-key])
|
||||
:accessibility-label :send-transaction-button}
|
||||
(if added?
|
||||
[{:label (i18n/label :t/remove-from-contacts)
|
||||
:icon :main-icons/remove-contact
|
||||
:accessibility-label :in-contacts-button
|
||||
:action #(re-frame/dispatch [:contact.ui/remove-contact-pressed contact])}]
|
||||
;; TODO sheets temporary disabled
|
||||
;:action #(re-frame/dispatch [:bottom-sheet/show-sheet
|
||||
; {:content sheets/remove-contact
|
||||
; :content-height 150}
|
||||
; contact])
|
||||
[{:label (i18n/label :t/add-to-contacts)
|
||||
:icon :main-icons/add-contact
|
||||
:accessibility-label :add-to-contacts-button
|
||||
:action #(re-frame/dispatch [:contact.ui/add-to-contact-pressed public-key])}]))))
|
||||
;; TODO sheets temporary disabled
|
||||
;:action #(re-frame/dispatch [:bottom-sheet/show-sheet
|
||||
; {:content sheets/add-contact
|
||||
; :content-height 150}
|
||||
; contact])
|
||||
|
||||
(defn profile-info-item [{:keys [label value options accessibility-label]}]
|
||||
[react/view styles/profile-info-item
|
||||
[react/view (styles/profile-info-text-container options)
|
||||
[react/text {:style styles/profile-info-title}
|
||||
label]
|
||||
[react/view styles/profile-setting-spacing]
|
||||
[react/text {:style styles/profile-setting-text
|
||||
:accessibility-label accessibility-label
|
||||
:selectable true}
|
||||
value]]])
|
||||
|
||||
(defn profile-info-contact-code-item [public-key]
|
||||
[profile-info-item
|
||||
{:label (i18n/label :t/contact-code)
|
||||
(defn render-detail [{:keys [name public-key] :as detail}]
|
||||
[list-item/list-item
|
||||
{:title name
|
||||
:subtitle (utils/get-shortened-address public-key)
|
||||
:icon [chat-icon/contact-icon-contacts-tab detail]
|
||||
:accessibility-label :profile-public-key
|
||||
:value public-key}])
|
||||
:on-press #(re-frame/dispatch [:show-popover {:view :share-chat-key :address public-key}])
|
||||
:accessories [[icons/icon :main-icons/share styles/contact-profile-detail-share-icon]]}])
|
||||
|
||||
(defn profile-info [{:keys [public-key]}]
|
||||
(defn profile-details-list-view [contact]
|
||||
[list/flat-list {:data [contact]
|
||||
:default-separator? true
|
||||
:key-fn :public-key
|
||||
:render-fn render-detail}])
|
||||
|
||||
(defn profile-details [contact]
|
||||
[react/view
|
||||
[profile-info-contact-code-item public-key]])
|
||||
[list-item/list-item {:type :section-header
|
||||
:title :t/profile-details
|
||||
:title-accessibility-label :profile-details}]
|
||||
[profile-details-list-view contact]])
|
||||
|
||||
(defn block-contact-action [{:keys [blocked? public-key]}]
|
||||
[list/render-action
|
||||
{:label (if blocked?
|
||||
(i18n/label :t/unblock-contact)
|
||||
(i18n/label :t/block-contact))
|
||||
:icon :main-icons/cancel
|
||||
:action (if blocked?
|
||||
#(re-frame/dispatch [:contact.ui/unblock-contact-pressed public-key])
|
||||
#(re-frame/dispatch [:contact.ui/block-contact-pressed public-key]))
|
||||
:accessibility-label (if blocked?
|
||||
:unblock-contact
|
||||
:block-contact)}
|
||||
{:action-style styles/block-action
|
||||
:action-label-style styles/block-action-label
|
||||
:icon-opts styles/block-action-icon-opts}])
|
||||
(defn block-contact-action [{:keys [blocked? public-key] :as contact}]
|
||||
[react/touchable-highlight {:on-press (if blocked?
|
||||
#(re-frame/dispatch [:contact.ui/unblock-contact-pressed public-key])
|
||||
#(re-frame/dispatch [:bottom-sheet/show-sheet
|
||||
{:content sheets/block-contact
|
||||
:content-height 160}
|
||||
contact]))}
|
||||
[react/text {:style styles/block-action-label
|
||||
:accessibility-label (if blocked?
|
||||
:unblock-contact
|
||||
:block-contact)}
|
||||
(if blocked?
|
||||
(i18n/label :t/unblock-contact)
|
||||
(i18n/label :t/block-contact))]])
|
||||
|
||||
(defview profile []
|
||||
(letsubs [contact [:contacts/current-contact]]
|
||||
[react/view profile.components.styles/profile
|
||||
[status-bar/status-bar]
|
||||
[profile-contact-toolbar]
|
||||
[react/scroll-view
|
||||
[react/view profile.components.styles/profile-form
|
||||
[profile.components/profile-header
|
||||
{:contact contact
|
||||
:editing? false
|
||||
:allow-icon-change? false}]]
|
||||
[list/action-list (actions contact)
|
||||
{:container-style styles/action-container
|
||||
:action-style styles/action
|
||||
:action-label-style styles/action-label
|
||||
:action-subtext-style styles/action-subtext
|
||||
:action-separator-style styles/action-separator
|
||||
:icon-opts styles/action-icon-opts}]
|
||||
[react/view {:style {:height 16}}]
|
||||
[block-contact-action contact]
|
||||
[react/view styles/contact-profile-info-container
|
||||
[profile-info contact]]]]))
|
||||
(defn- header-in-toolbar [{:keys [photo-path] :as account}]
|
||||
(let [displayed-name (multiaccounts/displayed-name account)]
|
||||
[react/view {:flex 1
|
||||
:flex-direction :row
|
||||
:align-items :center
|
||||
:align-self :stretch}
|
||||
[photos/photo photo-path {: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}])
|
||||
|
||||
(views/defview profile []
|
||||
(views/letsubs [list-ref (reagent/atom nil)
|
||||
contact [:contacts/current-contact]]
|
||||
(let [header-in-toolbar (header-in-toolbar contact)
|
||||
header (header contact)
|
||||
content
|
||||
[[list/action-list (actions contact)
|
||||
{:container-style styles/action-container
|
||||
:action-style styles/action
|
||||
:action-label-style styles/action-label
|
||||
:action-subtext-style styles/action-subtext
|
||||
:action-separator-style styles/action-separator
|
||||
:icon-opts styles/action-icon-opts}]
|
||||
[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
|
||||
nil
|
||||
header
|
||||
content
|
||||
list-ref)]
|
||||
[react/safe-area-view
|
||||
{:style
|
||||
(merge {:flex 1}
|
||||
(when platform/ios?
|
||||
{:margin-bottom tabs.styles/tabs-diff}))}
|
||||
[status-bar/status-bar {:type :main}]
|
||||
(:minimized-toolbar generated-view)
|
||||
(:content-with-header generated-view)])))
|
|
@ -9,7 +9,8 @@
|
|||
[status-im.ui.components.colors :as colors]
|
||||
[status-im.ui.components.common.common :as components.common]
|
||||
[status-im.ui.components.copyable-text :as copyable-text]
|
||||
[status-im.ui.components.large-toolbar :as large-toolbar]
|
||||
[status-im.ui.components.large-toolbar.view :as large-toolbar]
|
||||
[status-im.ui.components.list-item.views :as list-item]
|
||||
[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]
|
||||
|
@ -203,18 +204,30 @@
|
|||
registrar [:ens.stateofus/registrar]]
|
||||
(let [show-backup-seed? (and (not seed-backed-up?)
|
||||
(not (string/blank? mnemonic)))
|
||||
|
||||
;; toolbar-contents
|
||||
header-in-toolbar (header-in-toolbar multiaccount)
|
||||
toolbar-action-items (toolbar-action-items public-key)
|
||||
|
||||
;; flatlist contents
|
||||
header (header multiaccount)
|
||||
content (flat-list-content
|
||||
preferred-name registrar tribute-to-talk
|
||||
active-contacts-count show-backup-seed?)]
|
||||
active-contacts-count show-backup-seed?)
|
||||
|
||||
;; generated toolbar and content with header
|
||||
generated-view (large-toolbar/generate-view
|
||||
header-in-toolbar
|
||||
nil
|
||||
toolbar-action-items
|
||||
header
|
||||
content
|
||||
list-ref)]
|
||||
[react/safe-area-view
|
||||
{:style
|
||||
(merge {:flex 1}
|
||||
(when platform/ios?
|
||||
{:margin-bottom tabs.styles/tabs-diff}))}
|
||||
[status-bar/status-bar {:type :main}]
|
||||
[large-toolbar/minimized-toolbar
|
||||
(header-in-toolbar multiaccount)
|
||||
nil
|
||||
(toolbar-action-items public-key)]
|
||||
[large-toolbar/flat-list-with-large-header
|
||||
(header multiaccount) content list-ref]])))
|
||||
(:minimized-toolbar generated-view)
|
||||
(:content-with-header generated-view)])))
|
||||
|
|
|
@ -16,8 +16,9 @@
|
|||
|
||||
(def react-native
|
||||
#js {:NativeModules #js {}
|
||||
:Animated #js {:View #js {}
|
||||
:Text #js {}}
|
||||
:Animated #js {:View #js {}
|
||||
:FlatList #js {}
|
||||
:Text #js {}}
|
||||
:DeviceEventEmitter #js {:addListener (fn [])}
|
||||
:Dimensions #js {:get (fn [])}})
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
"add-members": "Add members",
|
||||
"add-network": "Add network",
|
||||
"add-to-contacts": "Add to contacts",
|
||||
"add-to-contacts-text": "By adding a user to your contact list, you share your wallet address",
|
||||
"address": "Address",
|
||||
"advanced": "Advanced",
|
||||
"advanced-settings": "Advanced settings",
|
||||
|
@ -70,7 +71,7 @@
|
|||
"blank-keycard-text": "You can proceed with your keycard once you've generated your keys and name",
|
||||
"blank-keycard-title": "Looks like you’ve tapped \na blank keycard",
|
||||
"block": "Block",
|
||||
"block-contact": "Block contact",
|
||||
"block-contact": "Block this user",
|
||||
"block-contact-details": "Blocking will delete this user's previous messages and stop new ones from reaching you",
|
||||
"blocked-users": "Blocked users",
|
||||
"bootnode-address": "Bootnode address",
|
||||
|
@ -782,6 +783,7 @@
|
|||
"processing": "Processing",
|
||||
"product-information": "Product Information",
|
||||
"profile": "Profile",
|
||||
"profile-details": "Profile details",
|
||||
"public-chat": "Public chat",
|
||||
"public-chats": "Public chats",
|
||||
"public-group-status": "Public",
|
||||
|
@ -825,6 +827,8 @@
|
|||
"remind-me-later": "Remind me later",
|
||||
"remove": "Remove",
|
||||
"remove-from-chat": "Remove from chat",
|
||||
"remove-from-contacts": "Remove from contacts",
|
||||
"remove-from-contacts-text": "By removing a user from your contact list you do not hide your wallet address from them",
|
||||
"remove-network": "Remove network",
|
||||
"remove-token": "Remove token",
|
||||
"removed": "removed",
|
||||
|
@ -1007,7 +1011,7 @@
|
|||
"type-a-message": "Type a message...",
|
||||
"ulc-enabled": "ULC enabled",
|
||||
"unable-to-read-this-code": "Unable to read this code",
|
||||
"unblock-contact": "Unblock contact",
|
||||
"unblock-contact": "Unblock this user",
|
||||
"unknown-status-go-error": "Unknown status-go error",
|
||||
"unlock": "Unlock",
|
||||
"unpair-card": "Unpair card",
|
||||
|
|
Loading…
Reference in New Issue