Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0f0ce0d7bd | ||
|
|
bc51a59774 | ||
|
|
4639660943 | ||
|
|
b58e7d6fff | ||
|
|
3b593bedac | ||
|
|
944cb6c822 | ||
|
|
8700886ae4 | ||
|
|
c5cc18e9e0 | ||
|
|
2b6f354fb6 | ||
|
|
717a225fd4 |
@@ -138,13 +138,14 @@
|
||||
(fn [{:keys [left-accessories left-component border-bottom
|
||||
right-accessories right-component insets get-layout
|
||||
title subtitle title-component style title-align
|
||||
background]
|
||||
background header-style]
|
||||
:or {title-align :center
|
||||
border-bottom false}}]
|
||||
(let [status-bar-height (get insets :top 0)
|
||||
height (+ header-height status-bar-height)]
|
||||
[reanimated/view {:style (header-wrapper-style {:height height
|
||||
:background background
|
||||
:header-style header-style
|
||||
:border-bottom border-bottom})}
|
||||
[rn/view {:pointer-events :box-none
|
||||
:height status-bar-height}]
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
(ns quo2.components.list-items.index
|
||||
(:require [quo.react-native :as rn]
|
||||
[quo2.components.markdown.text :as text]
|
||||
[quo2.foundations.colors :as colors]))
|
||||
|
||||
(defn index [{:keys [title]}]
|
||||
[rn/view
|
||||
[rn/view {:style {:border-top-width 1
|
||||
:border-color (colors/theme-colors colors/neutral-10 colors/neutral-80)
|
||||
:padding-vertical 8
|
||||
:padding-horizontal 16}}
|
||||
[text/text {:weight :medium
|
||||
:size :paragraph-2
|
||||
:secondary-color (colors/theme-colors colors/neutral-50 colors/neutral-40)}
|
||||
title]]])
|
||||
@@ -0,0 +1,276 @@
|
||||
(ns quo2.components.list-items.list-item
|
||||
(:require [quo.react-native :as rn]
|
||||
[quo.platform :as platform]
|
||||
[quo.haptic :as haptic]
|
||||
[quo.gesture-handler :as gh]
|
||||
[quo.design-system.spacing :as spacing]
|
||||
[quo.design-system.colors :as quo.colors]
|
||||
[quo.components.controls.view :as controls]
|
||||
[quo.components.tooltip :as tooltip]
|
||||
[quo2.components.markdown.text :as text]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[quo2.components.icon :as quo2.icons]
|
||||
[quo.components.animated.pressable :as animated]))
|
||||
|
||||
(defn themes [theme]
|
||||
(case theme
|
||||
:main {:icon-color (:icon-04 @quo.colors/theme)
|
||||
:icon-bg-color (:interactive-02 @quo.colors/theme)
|
||||
:active-background (:interactive-02 @quo.colors/theme)
|
||||
:passive-background (:ui-background @quo.colors/theme)
|
||||
:text-color (:text-01 @quo.colors/theme)}
|
||||
:accent {:icon-color (:icon-04 @quo.colors/theme)
|
||||
:icon-bg-color (:interactive-02 @quo.colors/theme)
|
||||
:active-background (:interactive-02 @quo.colors/theme)
|
||||
:passive-background (:ui-background @quo.colors/theme)
|
||||
:text-color (:text-04 @quo.colors/theme)}
|
||||
:negative {:icon-color (:negative-01 @quo.colors/theme)
|
||||
:icon-bg-color (:negative-02 @quo.colors/theme)
|
||||
:active-background (:negative-02 @quo.colors/theme)
|
||||
:passive-background (:ui-background @quo.colors/theme)
|
||||
:text-color (:negative-01 @quo.colors/theme)}
|
||||
:positive {:icon-color (:positive-01 @quo.colors/theme)
|
||||
:icon-bg-color (:positive-02 @quo.colors/theme)
|
||||
:active-background (:positive-02 @quo.colors/theme)
|
||||
:passive-background (:ui-background @quo.colors/theme)
|
||||
:text-color (:positive-01 @quo.colors/theme)}
|
||||
:disabled {:icon-color (:icon-02 @quo.colors/theme)
|
||||
:icon-bg-color (:ui-01 @quo.colors/theme)
|
||||
:active-background (:ui-01 @quo.colors/theme)
|
||||
:passive-background (:ui-background @quo.colors/theme)
|
||||
:text-color (:text-02 @quo.colors/theme)}))
|
||||
|
||||
(defn size->icon-size [size]
|
||||
(case size
|
||||
:small 36
|
||||
40))
|
||||
|
||||
(defn size->container-size [size]
|
||||
(case size
|
||||
:small 52
|
||||
64))
|
||||
|
||||
(defn size->single-title-size [size]
|
||||
(case size
|
||||
:small :paragraph-1
|
||||
:paragraph-1))
|
||||
|
||||
(defn container [{:keys [size container-style container-padding-horizontal container-padding-vertical]} & children]
|
||||
(into [rn/view {:style (merge (or container-padding-horizontal (:tiny spacing/padding-horizontal))
|
||||
{:min-height (size->container-size size)
|
||||
:padding-vertical (or container-padding-vertical 8)
|
||||
:flex-direction :row
|
||||
:align-items :center
|
||||
:justify-content :space-between} container-style)}]
|
||||
children))
|
||||
|
||||
(defn icon-column
|
||||
[{:keys [icon icon-bg-color icon-color size icon-container-style]}]
|
||||
(when icon
|
||||
(let [icon-size (size->icon-size size)]
|
||||
[rn/view {:style (or icon-container-style (:tiny spacing/padding-horizontal))}
|
||||
(cond
|
||||
(vector? icon)
|
||||
icon
|
||||
(keyword? icon)
|
||||
[rn/view {:style {:width icon-size
|
||||
:height icon-size
|
||||
:align-items :center
|
||||
:justify-content :center
|
||||
:border-radius (/ icon-size 2)
|
||||
:background-color icon-bg-color}}
|
||||
(quo2.icons/icon icon {:color icon-color})])])))
|
||||
|
||||
(defn title-column
|
||||
[{:keys [title text-color subtitle subtitle-max-lines subtitle-secondary
|
||||
title-accessibility-label size text-size title-text-weight
|
||||
right-side-present? title-column-style]}]
|
||||
[rn/view {:style (or title-column-style
|
||||
(merge (:tiny spacing/padding-horizontal)
|
||||
;; make left-side title grow if nothing is present on right-side
|
||||
(when-not right-side-present?
|
||||
{:flex 1
|
||||
:justify-content :center})))}
|
||||
(cond
|
||||
|
||||
(and title subtitle)
|
||||
[:<>
|
||||
;; FIXME(Ferossgp): ReactNative 63 will support view inside text on andrid, remove thess if when migrating
|
||||
(if (string? title)
|
||||
[text/text {:weight (or title-text-weight :medium)
|
||||
:style {:color text-color}
|
||||
:accessibility-label title-accessibility-label
|
||||
:ellipsize-mode :tail
|
||||
:number-of-lines 1
|
||||
:size text-size}
|
||||
title]
|
||||
title)
|
||||
(if (string? subtitle-secondary)
|
||||
[rn/view {:flex-direction :row}
|
||||
[text/text {:style {:max-width "56.5%"}
|
||||
:weight :regular
|
||||
:color :secondary
|
||||
:ellipsize-mode :tail
|
||||
:secondary-color colors/neutral-50
|
||||
:number-of-lines subtitle-max-lines
|
||||
:size text-size}
|
||||
subtitle]
|
||||
[text/text {:style {:width "7%" :text-align :center}
|
||||
:weight :regular
|
||||
:color :secondary
|
||||
:ellipsize-mode :middle
|
||||
:secondary-color colors/neutral-50
|
||||
:number-of-lines subtitle-max-lines
|
||||
:size text-size}
|
||||
"•"]
|
||||
[text/text {:style {:max-width "36.5%"}
|
||||
:weight :regular
|
||||
:color :secondary
|
||||
:ellipsize-mode :middle
|
||||
:secondary-color colors/neutral-50
|
||||
:number-of-lines subtitle-max-lines
|
||||
:size text-size}
|
||||
subtitle-secondary]]
|
||||
(if (string? subtitle)
|
||||
[text/text {:weight :regular
|
||||
:color :secondary
|
||||
:ellipsize-mode :tail
|
||||
:secondary-color colors/neutral-50
|
||||
:number-of-lines subtitle-max-lines
|
||||
:size text-size}
|
||||
subtitle]
|
||||
subtitle))]
|
||||
|
||||
title
|
||||
(if (string? title)
|
||||
[text/text {:weight (or title-text-weight :regular)
|
||||
:number-of-lines 1
|
||||
:style {:color text-color}
|
||||
:title-accessibility-label title-accessibility-label
|
||||
:ellipsize-mode :tail
|
||||
:size (or text-size (size->single-title-size size))}
|
||||
title]
|
||||
title))])
|
||||
|
||||
(defn left-side [props]
|
||||
[rn/view {:style {:flex-direction :row
|
||||
;; Occupy only content width, never grow, but shrink if need be
|
||||
:flex-grow 0
|
||||
:flex-shrink 1
|
||||
:padding-right 16
|
||||
:align-items (or (:left-side-alignment props) :center)}}
|
||||
[icon-column props]
|
||||
[title-column props]])
|
||||
|
||||
(defn right-side [{:keys [chevron active disabled accessory accessory-text accessory-style animated-accessory?]}]
|
||||
(when (or chevron accessory)
|
||||
[rn/view {:style (merge {:align-items :center
|
||||
:justify-content :flex-end
|
||||
:flex-direction :row
|
||||
;; Grow to occupy full space, shrink when need be, but always maitaining 16px left gutter
|
||||
:flex-grow 1
|
||||
:flex-shrink 0
|
||||
:margin-left 16
|
||||
;; When the left-side leaves no room for right-side, the rendered element is pushed out. A flex-basis ensures that there is some room reserved.
|
||||
;; The number 80px was determined by trial and error.
|
||||
:flex-basis 80}
|
||||
accessory-style)}
|
||||
[rn/view {:style (:tiny spacing/padding-horizontal)}
|
||||
(case accessory
|
||||
:radio [controls/radio {:value active :disabled disabled}]
|
||||
:checkbox [(if animated-accessory?
|
||||
controls/animated-checkbox
|
||||
controls/checkbox)
|
||||
{:value active :disabled disabled}]
|
||||
:switch [controls/switch {:value active :disabled disabled}]
|
||||
:text [text/text {:color :secondary
|
||||
:ellipsize-mode :middle
|
||||
:number-of-lines 1}
|
||||
accessory-text]
|
||||
accessory)]
|
||||
(when (and chevron platform/ios?)
|
||||
[rn/view {:style {:padding-right (:tiny spacing/spacing)}}
|
||||
(quo2.icons/icon :main-icons/next {:container-style {:opacity 0.4
|
||||
:align-items :center
|
||||
:justify-content :center}
|
||||
:resize-mode :center
|
||||
:color (:icon-02 @quo.colors/theme)})])]))
|
||||
|
||||
(defn list-item
|
||||
[{:keys [theme accessory disabled subtitle-max-lines icon icon-container-style
|
||||
left-side-alignment icon-color icon-bg-color title-column-style
|
||||
title subtitle subtitle-secondary active on-press on-long-press chevron size text-size
|
||||
accessory-text accessibility-label title-accessibility-label accessory-style
|
||||
haptic-feedback text-color active-background passive-background haptic-type error animated animated-accessory? title-text-weight container-style
|
||||
active-background-enabled background-color container-padding-horizontal container-padding-vertical]
|
||||
:or {subtitle-max-lines 1
|
||||
theme :main
|
||||
haptic-feedback true
|
||||
animated platform/ios?
|
||||
active-background-enabled true
|
||||
haptic-type :selection}}]
|
||||
(let [theme (if disabled :disabled theme)
|
||||
icon-color (or icon-color (:icon-color (themes theme)))
|
||||
icon-bg-color (or icon-bg-color (:icon-bg-color (themes theme)))
|
||||
optional-haptic (fn []
|
||||
(when haptic-feedback
|
||||
(haptic/trigger haptic-type)))
|
||||
component (cond
|
||||
(and (not on-press)
|
||||
(not on-long-press))
|
||||
rn/view
|
||||
animated animated/pressable
|
||||
:else gh/touchable-highlight)]
|
||||
[rn/view {:background-color (cond (not= background-color nil)
|
||||
background-color
|
||||
(and (= accessory :radio) active)
|
||||
active-background
|
||||
:else
|
||||
passive-background)}
|
||||
[component
|
||||
(merge {:type :list-item
|
||||
:disabled disabled
|
||||
:bg-color (when active-background-enabled active-background)
|
||||
:accessibility-label accessibility-label}
|
||||
(when on-press
|
||||
{:on-press (fn []
|
||||
(optional-haptic)
|
||||
(on-press))})
|
||||
(when on-long-press
|
||||
{:on-long-press (fn []
|
||||
(optional-haptic)
|
||||
(on-long-press))}))
|
||||
[container {:size size :container-style container-style :container-padding-vertical container-padding-vertical :container-padding-horizontal container-padding-horizontal}
|
||||
[left-side {:icon-color icon-color
|
||||
:text-color (if on-press
|
||||
text-color
|
||||
(:text-color (themes :main)))
|
||||
:left-side-alignment left-side-alignment
|
||||
:icon-bg-color icon-bg-color
|
||||
:title-accessibility-label title-accessibility-label
|
||||
:icon icon
|
||||
:icon-container-style icon-container-style
|
||||
:title title
|
||||
:title-text-weight title-text-weight
|
||||
:size size
|
||||
:text-size text-size
|
||||
:subtitle subtitle
|
||||
:subtitle-max-lines subtitle-max-lines
|
||||
:subtitle-secondary subtitle-secondary
|
||||
:right-side-present? (or accessory chevron)
|
||||
:title-column-style title-column-style}]
|
||||
[right-side {:chevron chevron
|
||||
:active active
|
||||
:disabled disabled
|
||||
:on-press on-press
|
||||
:accessory-text accessory-text
|
||||
:animated-accessory? animated-accessory?
|
||||
:accessory-style accessory-style
|
||||
:accessory accessory}]]]
|
||||
(when error
|
||||
[tooltip/tooltip (merge {:bottom-value 0}
|
||||
(when accessibility-label
|
||||
{:accessibility-label (str (name accessibility-label) "-error")}))
|
||||
[text/text {:color :negative
|
||||
:size :small}
|
||||
error]])]))
|
||||
@@ -1,11 +1,10 @@
|
||||
(ns quo2.components.markdown.text
|
||||
(:require [react-native.core :as rn]
|
||||
[quo2.theme :as theme]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[quo2.foundations.typography :as typography]
|
||||
[reagent.core :as reagent]))
|
||||
|
||||
(defn text-style [{:keys [size align weight style]}]
|
||||
(defn text-style [{:keys [size align weight style secondary-color]}]
|
||||
(merge (case (or weight :regular)
|
||||
:regular typography/font-regular
|
||||
:medium typography/font-medium
|
||||
@@ -24,7 +23,8 @@
|
||||
{:text-align (or align :auto)}
|
||||
(if (:color style)
|
||||
style
|
||||
(assoc style :color (if (= (theme/get-theme) :dark) colors/white colors/neutral-100)))))
|
||||
(assoc style :color (or secondary-color
|
||||
(colors/theme-colors colors/neutral-100 colors/white))))))
|
||||
|
||||
(defn text []
|
||||
(let [this (reagent/current-component)
|
||||
|
||||
@@ -0,0 +1,270 @@
|
||||
(ns quo2.components.markdown.text-input
|
||||
(:require [clojure.spec.alpha :as s]
|
||||
[reagent.core :as reagent]
|
||||
[oops.core :refer [ocall]]
|
||||
[quo.react-native :as rn]
|
||||
;; TODO(Ferossgp): Move icon component to lib
|
||||
[status-im.ui.components.icons.icons :as icons]
|
||||
[quo.components.tooltip :as tooltip]
|
||||
[quo.platform :as platform]
|
||||
[quo.design-system.typography :as typography]
|
||||
[quo.design-system.spacing :as spacing]
|
||||
[quo.design-system.colors :as colors]
|
||||
[quo.components.text :as text]))
|
||||
|
||||
;; NOTE(Ferossgp): Refactor with hooks when available
|
||||
;; We track all currently mounted text input refs
|
||||
;; in a ref-to-defaultValue map
|
||||
;; so that we can clear them (restore their default values)
|
||||
;; when global react-navigation's onWillBlur event is invoked
|
||||
(defonce text-input-refs (atom {}))
|
||||
|
||||
(s/def ::multiline boolean?)
|
||||
(s/def ::secure-text-entry boolean?)
|
||||
(s/def ::show-cancel boolean?)
|
||||
(s/def ::label (s/nilable (s/or :string string? :component vector?)))
|
||||
(s/def ::cancel-label (s/nilable string?))
|
||||
(s/def ::default-value (s/nilable string?))
|
||||
(s/def ::placeholder (s/nilable string?))
|
||||
(s/def ::keyboard-type (s/nilable (s/or :string string? :keyword keyword?))) ; TODO: make set
|
||||
(s/def ::accessibility-label (s/nilable (s/or :string string? :keyword keyword?)))
|
||||
(s/def ::on-focus fn?)
|
||||
(s/def ::on-blur fn?)
|
||||
(s/def ::on-press fn?)
|
||||
|
||||
(s/def ::accessory (s/keys :opt-un [::on-press
|
||||
::icon
|
||||
::component]))
|
||||
(s/def ::after (s/nilable ::accessory))
|
||||
(s/def ::before (s/nilable ::accessory))
|
||||
|
||||
(s/def ::style (s/nilable map?))
|
||||
(s/def ::input-style ::style)
|
||||
(s/def ::container-style ::style)
|
||||
|
||||
(s/def ::text-input (s/keys :opt-un
|
||||
[::label
|
||||
::multiline
|
||||
::error
|
||||
::style
|
||||
::input-style
|
||||
::keyboard-type
|
||||
::before
|
||||
::after
|
||||
::cancel-label
|
||||
::on-focus
|
||||
::on-blur
|
||||
::container-style
|
||||
::show-cancel
|
||||
::accessibility-label
|
||||
::bottom-value
|
||||
::secure-text-entry]))
|
||||
|
||||
(defn check-spec [spec prop]
|
||||
(if (s/valid? spec prop)
|
||||
true
|
||||
(do
|
||||
(s/explain spec prop)
|
||||
false)))
|
||||
|
||||
(def height 44) ; 22 line-height + 11*2 vertical padding
|
||||
(def multiline-height 88) ; 3 * 22 three line-height + 11* vertical padding
|
||||
|
||||
(defn label-style []
|
||||
{:margin-bottom (:tiny spacing/spacing)})
|
||||
|
||||
(defn text-input-row-style []
|
||||
{:flex-direction :row
|
||||
:align-items :center})
|
||||
|
||||
(defn text-input-view-style [style]
|
||||
(merge {:border-radius 8
|
||||
:flex-direction :row
|
||||
:flex 1
|
||||
:align-items :center
|
||||
:background-color (:ui-01 @colors/theme)}
|
||||
style))
|
||||
|
||||
(defn text-input-style [multiline input-style monospace before after]
|
||||
(merge (if monospace
|
||||
typography/monospace
|
||||
typography/font-regular)
|
||||
{:padding-top 11
|
||||
:padding-bottom 11
|
||||
:font-size 15
|
||||
:margin 0
|
||||
:text-align-vertical :center
|
||||
:flex 1
|
||||
:color (:text-01 @colors/theme)
|
||||
:height height}
|
||||
(when-not before
|
||||
{:padding-left (:base spacing/spacing)})
|
||||
(when-not after
|
||||
{:padding-right (:base spacing/spacing)})
|
||||
(when multiline
|
||||
{:text-align-vertical :top
|
||||
:line-height 22
|
||||
:height multiline-height})
|
||||
input-style))
|
||||
|
||||
(defn cancel-style []
|
||||
{:margin-left (:tiny spacing/spacing)
|
||||
:padding-left (:tiny spacing/spacing)
|
||||
:justify-content :center
|
||||
:align-self :stretch})
|
||||
|
||||
(defn accessory-style []
|
||||
(merge (:base spacing/padding-horizontal)
|
||||
{:flex 1
|
||||
:justify-content :center}))
|
||||
|
||||
(defn accessory-element [{:keys [icon component icon-opts style accessibility-label on-press]}]
|
||||
(let [el (if on-press
|
||||
rn/touchable-opacity
|
||||
rn/view)]
|
||||
[el (merge {:style {:align-self :stretch}}
|
||||
(when on-press
|
||||
{:on-press on-press}))
|
||||
[rn/view (merge {:style (merge (accessory-style)
|
||||
style)}
|
||||
(when accessibility-label
|
||||
{:accessibility-label accessibility-label}))
|
||||
(cond
|
||||
icon
|
||||
[icons/icon icon (merge {:color (:icon-01 @colors/theme)}
|
||||
icon-opts)]
|
||||
component
|
||||
component
|
||||
|
||||
:else
|
||||
nil)]]))
|
||||
|
||||
(defn text-input-raw []
|
||||
(let [focused (reagent/atom nil)
|
||||
visible (reagent/atom false)
|
||||
ref (atom nil)
|
||||
blur (fn []
|
||||
(some-> @ref (ocall "blur")))]
|
||||
(fn [{:keys [label multiline error style input-style keyboard-type before after
|
||||
cancel-label on-focus on-blur show-cancel accessibility-label
|
||||
bottom-value secure-text-entry container-style get-ref on-cancel
|
||||
monospace auto-complete-type auto-correct]
|
||||
:or {cancel-label "Cancel"}
|
||||
:as props}]
|
||||
{:pre [(check-spec ::text-input props)]}
|
||||
(let [show-cancel (if (nil? show-cancel)
|
||||
;; Enabled by default on iOs and disabled on Android
|
||||
platform/ios?
|
||||
show-cancel)
|
||||
after (cond
|
||||
(and secure-text-entry @visible)
|
||||
{:icon :main-icons/hide
|
||||
:on-press #(reset! visible false)}
|
||||
|
||||
(and secure-text-entry (not @visible))
|
||||
{:icon :main-icons/show
|
||||
:on-press #(reset! visible true)}
|
||||
|
||||
:else after)
|
||||
secure (boolean (and secure-text-entry (not @visible))) ; must be a boolean to work on iOS
|
||||
auto-complete (cond
|
||||
(= keyboard-type :visible-password)
|
||||
:off
|
||||
|
||||
secure-text-entry
|
||||
:password
|
||||
|
||||
:else
|
||||
auto-complete-type)
|
||||
auto-correct (and (not= keyboard-type :visible-password) (not secure-text-entry) auto-correct)
|
||||
on-cancel (fn []
|
||||
(when on-cancel
|
||||
(on-cancel))
|
||||
(blur))
|
||||
keyboard-type (cond
|
||||
(and platform/ios? (= keyboard-type :visible-password))
|
||||
:default
|
||||
|
||||
; the correct approach on Android would be keep secure-text-entry on set keyboard type
|
||||
; to visible-password. But until https://github.com/facebook/react-native/issues/27946
|
||||
; is solved that's the second best way.
|
||||
(and platform/android? secure-text-entry)
|
||||
:default
|
||||
|
||||
:else
|
||||
keyboard-type)]
|
||||
[rn/view {:style container-style}
|
||||
(when label
|
||||
[text/text {:style (label-style)}
|
||||
label])
|
||||
[rn/view {:style (text-input-row-style)}
|
||||
[rn/view {:style (text-input-view-style style)
|
||||
:important-for-accessibility (if secure-text-entry
|
||||
:no-hide-descendants
|
||||
:auto)}
|
||||
(when before
|
||||
[accessory-element before])
|
||||
[rn/text-input
|
||||
(merge {:style (text-input-style multiline input-style monospace before after)
|
||||
:ref (fn [r]
|
||||
(reset! ref r)
|
||||
(when get-ref (get-ref r)))
|
||||
:placeholder-text-color (:text-02 @colors/theme)
|
||||
:underline-color-android :transparent
|
||||
:auto-capitalize :none
|
||||
:secure-text-entry secure
|
||||
:auto-correct auto-correct
|
||||
:auto-complete-type auto-complete
|
||||
:on-focus (fn [evt]
|
||||
(when on-focus (on-focus evt))
|
||||
(when show-cancel
|
||||
(rn/configure-next (:ease-in-ease-out rn/layout-animation-presets)))
|
||||
(reset! focused true))
|
||||
:on-blur (fn [evt]
|
||||
(when on-blur (on-blur evt))
|
||||
(when show-cancel
|
||||
(rn/configure-next (:ease-in-ease-out rn/layout-animation-presets)))
|
||||
(reset! focused false))
|
||||
:keyboard-type keyboard-type}
|
||||
(when (and platform/ios? (not after))
|
||||
{:clear-button-mode :while-editing})
|
||||
(dissoc props
|
||||
:style :keyboard-type :on-focus :on-blur
|
||||
:secure-text-entry :ref :get-ref :auto-correct :auto-complete-type))]
|
||||
(when after
|
||||
[accessory-element after])]
|
||||
(when (and show-cancel
|
||||
(not multiline)
|
||||
@focused)
|
||||
[rn/touchable-opacity {:style (cancel-style)
|
||||
:on-press on-cancel}
|
||||
[text/text {:color :link} cancel-label]])
|
||||
(when error
|
||||
[tooltip/tooltip (merge {:bottom-value (if bottom-value bottom-value 0)}
|
||||
(when accessibility-label
|
||||
{:accessibility-label (str (name accessibility-label) "-error")}))
|
||||
[text/text {:color :negative
|
||||
:align :center
|
||||
:size :small}
|
||||
error]])]]))))
|
||||
|
||||
;; TODO(Ferossgp): Refactor me when hooks available
|
||||
(defn text-input [{:keys [preserve-input?]
|
||||
:as props}]
|
||||
(if preserve-input?
|
||||
[text-input-raw props]
|
||||
(let [id (random-uuid)]
|
||||
(reagent/create-class
|
||||
{:component-will-unmount
|
||||
(fn []
|
||||
(swap! text-input-refs dissoc id))
|
||||
:reagent-render
|
||||
(fn [{:keys [get-ref default-value]
|
||||
:as props}]
|
||||
[text-input-raw (merge props
|
||||
{:get-ref (fn [r]
|
||||
;; Store input and its defaultValue
|
||||
;; one we receive a non-nil ref
|
||||
(when r
|
||||
(swap! text-input-refs assoc id {:ref r :value default-value}))
|
||||
(when get-ref (get-ref r)))})])}))))
|
||||
+5
-1
@@ -33,7 +33,9 @@
|
||||
quo2.components.notifications.notification-dot
|
||||
quo2.components.tags.tags
|
||||
quo2.components.tabs.tabs
|
||||
quo2.components.tabs.account-selector))
|
||||
quo2.components.tabs.account-selector
|
||||
quo2.components.list-items.index
|
||||
quo2.components.list-items.list-item))
|
||||
|
||||
(def button quo2.components.buttons.button/button)
|
||||
(def dynamic-button quo2.components.buttons.dynamic-button/dynamic-button)
|
||||
@@ -80,6 +82,8 @@
|
||||
(def channel-list-item quo2.components.list-items.channel/list-item)
|
||||
(def menu-item quo2.components.list-items.menu-item/menu-item)
|
||||
(def preview-list quo2.components.list-items.preview-list/preview-list)
|
||||
(def index quo2.components.list-items.index/index)
|
||||
(def list-item quo2.components.list-items.list-item/list-item)
|
||||
|
||||
;;;; NOTIFICATIONS
|
||||
(def activity-log quo2.components.notifications.activity-logs/activity-log)
|
||||
|
||||
@@ -91,6 +91,11 @@
|
||||
(def white-opa-90 (alpha white 0.9))
|
||||
(def white-opa-95 (alpha white 0.95))
|
||||
|
||||
;;;;Black
|
||||
|
||||
;;Solid
|
||||
(def black "#000000")
|
||||
|
||||
;;;;Primary
|
||||
|
||||
;;Solid
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
(defn default-navigation [modal? {:keys [on-press label icon]}]
|
||||
(cond-> {:icon (if modal? :main-icons/close :main-icons/arrow-left)
|
||||
:accessibility-label :back-button
|
||||
:on-press #(re-frame/dispatch [:navigate-back])}
|
||||
:on-press #(re-frame/dispatch [:bottom-sheet/hide :navigate-back])}
|
||||
on-press
|
||||
(assoc :on-press on-press)
|
||||
|
||||
|
||||
@@ -27,6 +27,12 @@
|
||||
(= view :add-new)
|
||||
(merge home.sheet/add-new)
|
||||
|
||||
(= view :add-new-sheet-view)
|
||||
(merge home.sheet/add-new-sheet)
|
||||
|
||||
(= view :start-a-new-chat)
|
||||
(merge home.sheet/start-a-new-chat)
|
||||
|
||||
(= view :keycard.login/more)
|
||||
(merge keycard/more-sheet)
|
||||
|
||||
|
||||
@@ -1,23 +1,26 @@
|
||||
(ns status-im.ui.screens.home.sheet.views
|
||||
(:require [re-frame.core :as re-frame]
|
||||
[status-im.ui.components.react :as react]
|
||||
[status-im.qr-scanner.core :as qr-scanner]
|
||||
[quo.core :as quo]
|
||||
(:require [quo.core :as quo]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[status-im.utils.re-frame :as rf]
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
[status-im.qr-scanner.core :as qr-scanner]
|
||||
[status-im.ui.components.invite.views :as invite]
|
||||
[status-im.ui.components.react :as rn]
|
||||
[status-im.ui2.screens.chat.components.new-chat :as new-chat-aio]
|
||||
[status-im.utils.config :as config]
|
||||
[status-im.ui.components.invite.views :as invite]))
|
||||
[quo2.components.list-items.list-item :as quo2.list-item]))
|
||||
|
||||
(defn hide-sheet-and-dispatch [event]
|
||||
(re-frame/dispatch [:bottom-sheet/hide])
|
||||
(re-frame/dispatch event))
|
||||
(rf/dispatch [:bottom-sheet/hide])
|
||||
(rf/dispatch event))
|
||||
|
||||
(defn add-new-view []
|
||||
[react/view
|
||||
[react/view {:style {:flex-direction :row
|
||||
:padding-left 16
|
||||
:padding-right 8
|
||||
:justify-content :space-between
|
||||
:align-items :center}}
|
||||
[rn/view
|
||||
[rn/view {:style {:flex-direction :row
|
||||
:padding-left 16
|
||||
:padding-right 8
|
||||
:justify-content :space-between
|
||||
:align-items :center}}
|
||||
[quo/text {:size :large
|
||||
:weight :bold}
|
||||
(i18n/label :t/open-home)]
|
||||
@@ -47,7 +50,7 @@
|
||||
:accessibility-label :join-public-chat-button
|
||||
:icon :main-icons/public-chat
|
||||
:on-press #(hide-sheet-and-dispatch [:open-modal :new-public-chat])}]
|
||||
(when @(re-frame/subscribe [:communities/enabled?])
|
||||
(when (rf/sub [:communities/enabled?])
|
||||
[quo/list-item
|
||||
{:theme :accent
|
||||
:title (i18n/label :t/communities-alpha)
|
||||
@@ -57,5 +60,40 @@
|
||||
[invite/list-item
|
||||
{:accessibility-label :chats-menu-invite-friends-button}]])
|
||||
|
||||
(defn add-new-sheet-view []
|
||||
[rn/view
|
||||
[quo2.list-item/list-item
|
||||
{:theme :main
|
||||
:title (i18n/label :t/new-chat)
|
||||
:icon-bg-color :transparent
|
||||
:icon-container-style {:padding-horizontal 0}
|
||||
:container-padding-horizontal {:padding-horizontal 4}
|
||||
:container-padding-vertical 12
|
||||
:title-column-style {:margin-left 2}
|
||||
:icon-color (colors/theme-colors colors/neutral-50 colors/neutral-40)
|
||||
:accessibility-label :start-a-new-chat
|
||||
:icon :i/new-message
|
||||
:on-press #(hide-sheet-and-dispatch [:bottom-sheet/show-sheet :start-a-new-chat])}]
|
||||
[quo2.list-item/list-item
|
||||
{:theme :main
|
||||
:title (i18n/label :t/add-a-contact)
|
||||
:icon-bg-color :transparent
|
||||
:icon-container-style {:padding-horizontal 0}
|
||||
:container-padding-horizontal {:padding-horizontal 4}
|
||||
:container-padding-vertical 12
|
||||
:title-column-style {:margin-left 2}
|
||||
:icon-color (colors/theme-colors colors/neutral-50 colors/neutral-40)
|
||||
:accessibility-label :add-a-contact
|
||||
:subtitle (i18n/label :t/enter-a-chat-key)
|
||||
:icon :i/add-user
|
||||
:on-press #(hide-sheet-and-dispatch [:open-modal :new-contact])}]])
|
||||
|
||||
|
||||
(def add-new-sheet
|
||||
{:content add-new-sheet-view})
|
||||
|
||||
(def add-new
|
||||
{:content add-new-view})
|
||||
|
||||
(def start-a-new-chat
|
||||
{:content (fn [] [new-chat-aio/contact-toggle-list])})
|
||||
|
||||
@@ -115,7 +115,8 @@
|
||||
[status-im.ui.screens.wallet.buy-crypto.views :as wallet.buy-crypto]
|
||||
[status-im.ui.screens.wallet.recipient.views :as recipient]
|
||||
[status-im.ui.screens.wallet.send.views :as wallet.send]
|
||||
[status-im.ui.screens.wallet.manage-connections.views :as manage-all-connections]))
|
||||
[status-im.ui.screens.wallet.manage-connections.views :as manage-all-connections]
|
||||
[status-im.ui2.screens.chat.components.new-chat :as new-chat-aio]))
|
||||
|
||||
(defn right-button-options [id icon]
|
||||
{:id id
|
||||
@@ -595,11 +596,17 @@
|
||||
:options {:topBar {:visible false}}
|
||||
:component contact/nickname}
|
||||
|
||||
;[Group chat] Edit group chat name
|
||||
{:name :edit-group-chat-name
|
||||
{:name :new-chat-aio
|
||||
:on-focus [::new-chat.events/new-chat-focus]
|
||||
;;TODO accessories
|
||||
:options {:topBar {:visible false}}
|
||||
:component new-chat-aio/contact-toggle-list}
|
||||
|
||||
;[Chat] New Public chat
|
||||
{:name :new-public-chat
|
||||
:insets {:bottom true}
|
||||
:options {:topBar {:title {:text (i18n/label :t/edit-group)}}}
|
||||
:component group-chat/edit-group-chat-name}
|
||||
:options {:topBar {:title {:text (i18n/label :t/new-public-group-chat)}}}
|
||||
:component new-public-chat/new-public-chat}
|
||||
|
||||
;[Group chat] Add participants
|
||||
{:name :add-participants-toggle-list
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
(ns status-im.ui2.components.search-input
|
||||
(:require [reagent.core :as reagent]
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
[quo.design-system.colors :as colors]
|
||||
[quo2.foundations.colors :as quo2.colors]
|
||||
[quo.components.text-input :as text-input]))
|
||||
|
||||
(defn search-input [{:keys [search-active?]}]
|
||||
(let [input-ref (reagent/atom nil)
|
||||
placeholder-text (reagent/atom :t/search-contacts)
|
||||
search-active? (or search-active? (reagent/atom nil))]
|
||||
(fn [{:keys [on-focus on-change search-border-width show-cancel? search-border-radius on-blur on-cancel search-filter auto-focus] :or {show-cancel? false}}]
|
||||
[text-input/text-input {:placeholder (i18n/label @placeholder-text)
|
||||
:placeholder-text-color (if (= @placeholder-text :t/search-contacts)
|
||||
(quo2.colors/theme-colors quo2.colors/neutral-30 quo2.colors/neutral-50)
|
||||
(quo2.colors/theme-colors quo2.colors/neutral-30 quo2.colors/neutral-60))
|
||||
:accessibility-label :search-input
|
||||
:text-padding-left 0
|
||||
:blur-on-submit true
|
||||
:multiline false
|
||||
:get-ref #(reset! input-ref %)
|
||||
:default-value search-filter
|
||||
:auto-focus auto-focus
|
||||
:on-cancel on-cancel
|
||||
:show-cancel show-cancel?
|
||||
:auto-correct false
|
||||
:auto-capitalize :none
|
||||
:container-style {:border-radius (or search-border-radius 10)
|
||||
:border-width (or search-border-width 1)
|
||||
:border-color (:ui-01 @colors/theme)
|
||||
:background-color (quo2.colors/theme-colors quo2.colors/white quo2.colors/neutral-90)
|
||||
:overflow :hidden}
|
||||
:input-style {:height 32
|
||||
:padding-vertical 2
|
||||
:background-color (quo2.colors/theme-colors quo2.colors/white quo2.colors/neutral-90)}
|
||||
:on-focus #(do
|
||||
(when on-focus
|
||||
(on-focus search-filter))
|
||||
(swap! search-active? not)
|
||||
(reset! placeholder-text :t/who-are-you-looking-for))
|
||||
:on-blur #(do
|
||||
(when on-blur
|
||||
(on-blur))
|
||||
(swap! search-active? not)
|
||||
(reset! placeholder-text :t/search-contacts))
|
||||
:on-change (fn [e]
|
||||
(let [^js native-event (.-nativeEvent ^js e)
|
||||
text (.-text native-event)]
|
||||
(when on-change
|
||||
(on-change text))))}])))
|
||||
@@ -0,0 +1,40 @@
|
||||
(ns status-im.ui2.components.toolbar
|
||||
(:require [status-im.ui.components.react :as react]
|
||||
[quo.design-system.colors :as colors]))
|
||||
|
||||
(defn toolbar-container [{:keys [show-border? size center? margin-bottom container-styling]
|
||||
:or {size :default}}]
|
||||
(merge {:align-items :center
|
||||
:padding-horizontal 8
|
||||
:margin-bottom (or margin-bottom 0)
|
||||
:width "100%"
|
||||
:flex-direction :row
|
||||
:justify-content :space-between}
|
||||
container-styling
|
||||
(when center?
|
||||
{:justify-content :center})
|
||||
(when show-border?
|
||||
{:border-top-width 1
|
||||
:border-top-color colors/gray-lighter})
|
||||
(case size
|
||||
:large {:height 60}
|
||||
{:height 52})))
|
||||
|
||||
;; TODO(Ferossgp): Allow components when moving to Quo
|
||||
(defn toolbar [{:keys [center left right show-border? size margin-bottom container-styling]}]
|
||||
(if center
|
||||
[react/view {:style (toolbar-container {:show-border? show-border?
|
||||
:center? true
|
||||
:margin-bottom margin-bottom
|
||||
:size size
|
||||
:container-styling container-styling})}
|
||||
center]
|
||||
[react/view {:style (toolbar-container {:show-border? show-border?
|
||||
:margin-bottom margin-bottom
|
||||
:center? false
|
||||
:size size
|
||||
:container-styling container-styling})}
|
||||
[react/view {:flex-shrink 1}
|
||||
(when left left)]
|
||||
[react/view
|
||||
(when right right)]]))
|
||||
@@ -0,0 +1,283 @@
|
||||
(ns status-im.ui2.screens.chat.components.new-chat
|
||||
(:require [cljs.spec.alpha :as spec]
|
||||
[clojure.string :as string]
|
||||
[re-frame.core :as re-frame]
|
||||
[reagent.core :as reagent]
|
||||
[status-im.constants :as constants]
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
[status-im.ui.components.chat-icon.screen :as chat-icon]
|
||||
[status-im.multiaccounts.core :as multiaccounts]
|
||||
[status-im.ui.components.keyboard-avoid-presentation :as kb-presentation]
|
||||
[status-im.ui.components.invite.views :as invite]
|
||||
[status-im.ui.components.list.views :as list]
|
||||
[status-im.ui.components.react :as react]
|
||||
[status-im.ui2.components.search-input :as search]
|
||||
[status-im.ui2.components.toolbar :as toolbar]
|
||||
[status-im.ui.components.topbar :as topbar]
|
||||
[status-im.ui.screens.group.styles :as styles]
|
||||
[quo.core :as quo]
|
||||
[quo2.core :as quo2]
|
||||
[quo2.components.buttons.button :as button]
|
||||
[status-im.utils.re-frame :as rf]
|
||||
[status-im.utils.debounce :as debounce]
|
||||
[quo2.foundations.colors :as quo2.colors]
|
||||
[status-im.ui2.screens.chat.actions :refer [hide-sheet-and-dispatch]])
|
||||
(:require-macros [status-im.utils.views :as views]))
|
||||
|
||||
(defn- render-contact [row]
|
||||
(let [[first-name second-name] (multiaccounts/contact-two-names row false)]
|
||||
[quo2/list-item
|
||||
{:title first-name
|
||||
:subtitle second-name
|
||||
:icon [chat-icon/contact-icon-contacts-tab
|
||||
(multiaccounts/displayed-photo row)]}]))
|
||||
|
||||
(defn- on-toggle [allow-new-users? checked? public-key]
|
||||
(cond
|
||||
checked?
|
||||
(rf/dispatch [:deselect-contact public-key allow-new-users?])
|
||||
;; Only allow new users if not reached the maximum
|
||||
(and (not checked?)
|
||||
allow-new-users?)
|
||||
(rf/dispatch [:select-contact public-key allow-new-users?])))
|
||||
|
||||
(defn- on-toggle-participant [allow-new-users? checked? public-key]
|
||||
(cond
|
||||
checked?
|
||||
(rf/dispatch [:deselect-participant public-key allow-new-users?])
|
||||
|
||||
;; Only allow new users if not reached the maximum
|
||||
(and (not checked?)
|
||||
allow-new-users?)
|
||||
(rf/dispatch [:select-participant public-key allow-new-users?])))
|
||||
|
||||
(defn- toggle-item []
|
||||
(fn [allow-new-users? subs-name {:keys [public-key] :as contact} on-toggle]
|
||||
(let [contact-selected? @(re-frame/subscribe [subs-name public-key])
|
||||
[first-name second-name] (multiaccounts/contact-two-names contact true)]
|
||||
[quo2/list-item
|
||||
{:title first-name
|
||||
:text-color (quo2.colors/theme-colors quo2.colors/neutral-100 quo2.colors/white)
|
||||
:subtitle second-name
|
||||
:icon [chat-icon/contact-icon-contacts-tab
|
||||
(multiaccounts/displayed-photo contact)]
|
||||
:on-press #(on-toggle allow-new-users? contact-selected? public-key)
|
||||
:active contact-selected?
|
||||
:accessory :checkbox
|
||||
:background-color (quo2.colors/theme-colors quo2.colors/white quo2.colors/neutral-90)}])))
|
||||
|
||||
(defn- group-toggle-contact [{:keys [:allow-new-users?] :as contact} _ _]
|
||||
[toggle-item allow-new-users? :is-contact-selected? contact on-toggle])
|
||||
|
||||
(defn- group-toggle-participant [{:keys [:allow-new-users?] :as contact} _ _]
|
||||
[toggle-item allow-new-users? :is-participant-selected? contact on-toggle-participant])
|
||||
|
||||
(defn toggle-list [{:keys [contacts render-fn]}]
|
||||
[list/section-list
|
||||
{:content-container-style {:padding-vertical 8}
|
||||
:key-fn :id
|
||||
:keyboard-should-persist-taps :always
|
||||
:sticky-section-headers-enabled false
|
||||
:sections contacts
|
||||
:render-section-header-fn quo2/index
|
||||
:render-fn render-fn}])
|
||||
|
||||
(defn no-contacts [{:keys [no-contacts]}]
|
||||
[react/view {:style styles/no-contacts}
|
||||
[react/text
|
||||
{:style (styles/no-contact-text)}
|
||||
no-contacts]
|
||||
[invite/button]])
|
||||
|
||||
(defn filter-contacts [filter-text contacts]
|
||||
(let [lower-filter-text (string/lower-case (str filter-text))
|
||||
filter-fn (fn [{:keys [data]}]
|
||||
(let [{:keys [name alias nickname]} (first data)]
|
||||
(or
|
||||
(string/includes? (string/lower-case (str name)) lower-filter-text)
|
||||
(string/includes? (string/lower-case (str alias)) lower-filter-text)
|
||||
(when nickname
|
||||
(string/includes? (string/lower-case (str nickname)) lower-filter-text)))))]
|
||||
(if filter-text
|
||||
(filter filter-fn contacts)
|
||||
contacts)))
|
||||
|
||||
;; Set name of new group-chat
|
||||
(defn new-group []
|
||||
(let [contacts (rf/sub [:selected-group-contacts])
|
||||
group-name (rf/sub [:new-chat-name])
|
||||
group-name-empty? (not (spec/valid? :global/not-empty-string group-name))]
|
||||
[react/keyboard-avoiding-view {:style styles/group-container
|
||||
:ignore-offset true}
|
||||
[react/view {:flex 1}
|
||||
[topbar/topbar {:use-insets false
|
||||
:title (i18n/label :t/new-group-chat)
|
||||
:subtitle (i18n/label :t/group-chat-members-count
|
||||
{:selected (inc (count contacts))
|
||||
:max constants/max-group-chat-participants})}]
|
||||
[react/view {:style {:padding-top 16
|
||||
:flex 1}}
|
||||
[react/view {:style {:padding-horizontal 16}}
|
||||
[quo/text-input
|
||||
{:auto-focus true
|
||||
:on-change-text #(rf/dispatch [:set :new-chat-name %])
|
||||
:default-value group-name
|
||||
:placeholder (i18n/label :t/set-a-topic)
|
||||
:accessibility-label :chat-name-input}]
|
||||
[react/text {:style (styles/members-title)}
|
||||
(i18n/label :t/members-title)]]
|
||||
[react/view {:style {:margin-top 8
|
||||
:flex 1}}
|
||||
[list/flat-list {:data contacts
|
||||
:key-fn :address
|
||||
:render-fn render-contact
|
||||
:bounces false
|
||||
:keyboard-should-persist-taps :always
|
||||
:enable-empty-sections true}]]]
|
||||
[toolbar/toolbar
|
||||
{:show-border? true
|
||||
:left [quo/button {:type :secondary
|
||||
:before :main-icon/back
|
||||
:accessibility-label :previous-button
|
||||
:on-press #(rf/dispatch [:navigate-back])}
|
||||
(i18n/label :t/back)]
|
||||
:right [quo/button {:type :secondary
|
||||
:accessibility-label :create-group-chat-button
|
||||
:disabled group-name-empty?
|
||||
:on-press #(debounce/dispatch-and-chill [:group-chats.ui/create-pressed group-name]
|
||||
300)}
|
||||
(i18n/label :t/create-group-chat)]}]]]))
|
||||
|
||||
(defn searchable-contact-list []
|
||||
(let [search-value (reagent/atom nil)]
|
||||
(fn [{:keys [contacts no-contacts-label toggle-fn allow-new-users? show-cancel?]}]
|
||||
[react/view {:style {:flex 1}}
|
||||
[react/view {:style (styles/search-container)}
|
||||
[search/search-input {:on-cancel #(reset! search-value nil)
|
||||
:search-border-radius 0
|
||||
:show-cancel show-cancel?
|
||||
:search-border-width 0
|
||||
:on-change #(reset! search-value %)}]]
|
||||
[react/view {:style {:flex 1
|
||||
:padding-vertical 8
|
||||
:background-color (quo2.colors/theme-colors quo2.colors/white quo2.colors/neutral-90)}}
|
||||
(if (seq contacts)
|
||||
[toggle-list {:contacts (filter-contacts @search-value contacts)
|
||||
:render-data allow-new-users?
|
||||
:render-fn toggle-fn}]
|
||||
[no-contacts {:no-contacts no-contacts-label}])]])))
|
||||
|
||||
;; Start group chat
|
||||
(defn contact-toggle-list []
|
||||
(let [contacts (rf/sub [:contacts/sorted-and-grouped-by-first-letter])
|
||||
selected-contacts-count (rf/sub [:selected-contacts-count])
|
||||
window-height (rf/sub [:dimensions/window-height])
|
||||
one-contact-selected? (= selected-contacts-count 1)
|
||||
no-contacts-selected? (zero? selected-contacts-count)
|
||||
{:keys [alias public-key]} (-> contacts first :data first)]
|
||||
[react/view {:style {:height (* window-height 0.95)}}
|
||||
[topbar/topbar {:use-insets false
|
||||
:border-bottom false
|
||||
:style {:top -15}
|
||||
:close-icon-container-props {:style {:width 32
|
||||
:height 32
|
||||
:border-radius 10
|
||||
:justify-content :center
|
||||
:align-items :center
|
||||
:background-color (quo2.colors/theme-colors quo2.colors/neutral-10 quo2.colors/neutral-80)}}
|
||||
:close-icon-props {:size 20
|
||||
:color (quo2.colors/theme-colors quo2.colors/black quo2.colors/white)}
|
||||
:navigation {:sheet? true}
|
||||
:modal? true
|
||||
:background (quo2.colors/theme-colors quo2.colors/white quo2.colors/neutral-90)}]
|
||||
[react/view {:style {:flex-direction :row
|
||||
:justify-content :space-between
|
||||
:align-items :flex-end
|
||||
:padding-horizontal 20}}
|
||||
[quo2/text {:weight :semi-bold
|
||||
:size :heading-1
|
||||
:color (quo2.colors/theme-colors quo2.colors/neutral-40 quo2.colors/neutral-50)}
|
||||
(i18n/label :t/new-chat)]
|
||||
[quo2/text {:size :paragraph-2
|
||||
:weight :regular
|
||||
:secondary-color (quo2.colors/theme-colors quo2.colors/neutral-40 quo2.colors/neutral-50)}
|
||||
(i18n/label :t/selected-count-from-max
|
||||
{:selected (inc selected-contacts-count)
|
||||
:max constants/max-group-chat-participants})]]
|
||||
[react/view {:style {:height 430}}
|
||||
[searchable-contact-list
|
||||
{:contacts contacts
|
||||
:show-cancel? false
|
||||
:no-contacts-label (i18n/label :t/group-chat-no-contacts)
|
||||
:toggle-fn group-toggle-contact}]]
|
||||
(when-not no-contacts-selected?
|
||||
[toolbar/toolbar
|
||||
{:show-border? false
|
||||
:margin-bottom 20
|
||||
:center [button/button {:type :primary
|
||||
:accessibility-label :next-button
|
||||
:on-press #(do
|
||||
(if one-contact-selected?
|
||||
(hide-sheet-and-dispatch [:chat.ui/start-chat public-key])
|
||||
(hide-sheet-and-dispatch [:navigate-to :new-group])))}
|
||||
(if one-contact-selected?
|
||||
(i18n/label :t/chat-with {:selected-user alias})
|
||||
(i18n/label :t/setup-group-chat))]}])]))
|
||||
|
||||
;; Add participants to existing group chat
|
||||
(defn add-participants-toggle-list []
|
||||
(let [contacts (rf/sub [:contacts/all-contacts-not-in-current-chat])
|
||||
current-chat (rf/sub [:chats/current-chat])
|
||||
selected-contacts-count (rf/sub [:selected-participants-count])
|
||||
current-participants-count (count (:contacts current-chat))]
|
||||
[kb-presentation/keyboard-avoiding-view {:style styles/group-container}
|
||||
[topbar/topbar {:use-insets false
|
||||
:border-bottom false
|
||||
:title (i18n/label :t/add-members)
|
||||
:subtitle (i18n/label :t/group-chat-members-count
|
||||
{:selected (+ current-participants-count selected-contacts-count)
|
||||
:max constants/max-group-chat-participants})}]
|
||||
[searchable-contact-list
|
||||
{:contacts contacts
|
||||
:no-contacts-label (i18n/label :t/group-chat-all-contacts-invited)
|
||||
:toggle-fn group-toggle-participant
|
||||
:allow-new-users? (< (+ current-participants-count
|
||||
selected-contacts-count)
|
||||
constants/max-group-chat-participants)}]
|
||||
[toolbar/toolbar
|
||||
{:show-border? true
|
||||
:center [quo/button {:type :secondary
|
||||
:accessibility-label :next-button
|
||||
:disabled (zero? selected-contacts-count)
|
||||
:on-press #(rf/dispatch [:group-chats.ui/add-members-pressed])}
|
||||
(i18n/label :t/add)]}]]))
|
||||
|
||||
(defn edit-group-chat-name []
|
||||
(let [{:keys [name chat-id]} (rf/sub [:chats/current-chat])
|
||||
new-group-chat-name (reagent/atom nil)]
|
||||
[kb-presentation/keyboard-avoiding-view {:style styles/group-container}
|
||||
[react/scroll-view {:style {:padding 16
|
||||
:flex 1}}
|
||||
[quo/text-input
|
||||
{:on-change-text #(reset! new-group-chat-name %)
|
||||
:default-value name
|
||||
:on-submit-editing #(when (seq @new-group-chat-name)
|
||||
(rf/dispatch [:group-chats.ui/name-changed chat-id @new-group-chat-name]))
|
||||
:placeholder (i18n/label :t/enter-contact-code)
|
||||
:accessibility-label :new-chat-name
|
||||
:return-key-type :go}]]
|
||||
[react/view {:style {:flex 1}}]
|
||||
[toolbar/toolbar
|
||||
{:show-border? true
|
||||
:center
|
||||
[quo/button {:type :secondary
|
||||
:accessibility-label :done
|
||||
:disabled (and (<= (count @new-group-chat-name) 1)
|
||||
(not (nil? @new-group-chat-name)))
|
||||
:on-press #(cond
|
||||
(< 1 (count @new-group-chat-name))
|
||||
(rf/dispatch [:group-chats.ui/name-changed chat-id @new-group-chat-name])
|
||||
|
||||
(nil? @new-group-chat-name)
|
||||
(rf/dispatch [:navigate-back]))}
|
||||
(i18n/label :t/done)]}]]))
|
||||
@@ -15,10 +15,11 @@
|
||||
[status-im.add-new.db :as db]
|
||||
[status-im.utils.debounce :as debounce]
|
||||
[status-im.utils.utils :as utils]
|
||||
[status-im.utils.re-frame :as rf]
|
||||
[status-im.ui.components.topbar :as topbar]
|
||||
[status-im.ui.components.plus-button :as components.plus-button]
|
||||
[status-im.ui.components.invite.views :as invite]
|
||||
[status-im.utils.handlers :refer [<sub >evt]]
|
||||
[status-im.utils.handlers :refer [>evt]]
|
||||
[status-im.utils.config :as config]
|
||||
[quo2.components.markdown.text :as quo2.text]
|
||||
[status-im.qr-scanner.core :as qr-scanner]
|
||||
@@ -42,25 +43,38 @@
|
||||
|
||||
(defn home-tooltip-view []
|
||||
[rn/view (styles/chat-tooltip)
|
||||
[rn/view {:style {:width 66 :position :absolute :top -6 :background-color quo.colors/white
|
||||
:align-items :center}}
|
||||
[rn/view {:style {:width 66
|
||||
:position :absolute
|
||||
:top -6
|
||||
:background-color quo.colors/white
|
||||
:align-items :center}}
|
||||
[rn/image {:source (resources/get-image :empty-chats-header)
|
||||
:style {:width 50 :height 50}}]]
|
||||
:style {:width 50
|
||||
:height 50}}]]
|
||||
[rn/touchable-highlight
|
||||
{:style {:position :absolute :right 0 :top 0
|
||||
:width 44 :height 44 :align-items :center :justify-content :center}
|
||||
:on-press #(re-frame/dispatch [:multiaccounts.ui/hide-home-tooltip])
|
||||
{:style {:position :absolute
|
||||
:right 0
|
||||
:top 0
|
||||
:width 44
|
||||
:height 44
|
||||
:align-items :center
|
||||
:justify-content :center}
|
||||
:on-press #(rf/dispatch [:multiaccounts.ui/hide-home-tooltip])
|
||||
:accessibility-label :hide-home-button}
|
||||
[icons/icon :main-icons/close-circle {:color quo.colors/gray}]]
|
||||
[react/i18n-text {:style styles/no-chats-text :key :chat-and-transact}]
|
||||
[react/i18n-text {:style styles/no-chats-text
|
||||
:key :chat-and-transact}]
|
||||
[rn/view {:align-items :center
|
||||
:margin-top 8
|
||||
:margin-bottom 12}
|
||||
[invite/button]]])
|
||||
|
||||
(defn welcome-blank-chats []
|
||||
[rn/view {:style {:flex 1 :align-items :center :justify-content :center}}
|
||||
[icons/icon :main-icons/placeholder20 {:width 120 :height 120}]
|
||||
[rn/view {:style {:flex 1
|
||||
:align-items :center
|
||||
:justify-content :center}}
|
||||
[icons/icon :main-icons/placeholder20 {:width 120
|
||||
:height 120}]
|
||||
[rn/text {:style (merge typography/font-semi-bold typography/paragraph-1)} (i18n/label :t/no-messages)]
|
||||
[rn/text {:style (merge typography/font-regular typography/paragraph-2)} (i18n/label :t/blank-messages-text)]])
|
||||
|
||||
@@ -69,7 +83,8 @@
|
||||
:flex-direction :row
|
||||
:align-items :center
|
||||
:justify-content :center}}
|
||||
[react/i18n-text {:style styles/welcome-blank-text :key :welcome-blank-message}]])
|
||||
[react/i18n-text {:style styles/welcome-blank-text
|
||||
:key :welcome-blank-message}]])
|
||||
|
||||
(defonce search-active? (reagent/atom false))
|
||||
|
||||
@@ -79,25 +94,24 @@
|
||||
[search-input/search-input
|
||||
{:search-active? search-active?
|
||||
:search-filter search-filter
|
||||
:on-cancel #(re-frame/dispatch [:search/home-filter-changed nil])
|
||||
:on-cancel #(rf/dispatch [:search/home-filter-changed nil])
|
||||
:on-blur (fn []
|
||||
(when chats-empty
|
||||
(re-frame/dispatch [:search/home-filter-changed nil]))
|
||||
(re-frame/dispatch [::new-chat/clear-new-identity]))
|
||||
(rf/dispatch [:search/home-filter-changed nil]))
|
||||
(rf/dispatch [::new-chat/clear-new-identity]))
|
||||
:on-focus (fn [search-filter]
|
||||
(when-not search-filter
|
||||
(re-frame/dispatch [:search/home-filter-changed ""])
|
||||
(re-frame/dispatch [::new-chat/clear-new-identity])))
|
||||
(rf/dispatch [:search/home-filter-changed ""])
|
||||
(rf/dispatch [::new-chat/clear-new-identity])))
|
||||
:on-change (fn [text]
|
||||
(re-frame/dispatch [:search/home-filter-changed text])
|
||||
(re-frame/dispatch [:set-in [:contacts/new-identity :state] :searching])
|
||||
(rf/dispatch [:search/home-filter-changed text])
|
||||
(rf/dispatch [:set-in [:contacts/new-identity :state] :searching])
|
||||
(debounce/debounce-and-dispatch [:new-chat/set-new-identity text] 300))}]])
|
||||
|
||||
(defn start-suggestion [search-value]
|
||||
(let [{:keys [state ens-name public-key]}
|
||||
@(re-frame/subscribe [:contacts/new-identity])
|
||||
valid-private? (= state :valid)
|
||||
valid-public? (db/valid-topic? search-value)]
|
||||
(let [{:keys [state ens-name public-key]} (rf/sub [:contacts/new-identity])
|
||||
valid-private? (= state :valid)
|
||||
valid-public? (db/valid-topic? search-value)]
|
||||
(when (or valid-public? valid-private?)
|
||||
[rn/view
|
||||
[quo/list-header (i18n/label :t/search-no-chat-found)]
|
||||
@@ -108,16 +122,16 @@
|
||||
:subtitle (i18n/label :t/join-new-private-chat)
|
||||
:on-press (fn []
|
||||
(debounce/dispatch-and-chill [:contact.ui/contact-code-submitted false] 3000)
|
||||
(re-frame/dispatch [:search/home-filter-changed nil]))}])
|
||||
(rf/dispatch [:search/home-filter-changed nil]))}])
|
||||
(when valid-public?
|
||||
[quo/list-item {:theme :accent
|
||||
:icon :main-icons/public-chat
|
||||
:title (str "#" search-value)
|
||||
:subtitle (i18n/label :t/join-new-public-chat)
|
||||
:on-press (fn []
|
||||
(re-frame/dispatch [:chat.ui/start-public-chat search-value])
|
||||
(re-frame/dispatch [:set :public-group-topic nil])
|
||||
(re-frame/dispatch [:search/home-filter-changed nil]))}])])))
|
||||
(rf/dispatch [:chat.ui/start-public-chat search-value])
|
||||
(rf/dispatch [:set :public-group-topic nil])
|
||||
(rf/dispatch [:search/home-filter-changed nil]))}])])))
|
||||
|
||||
(defn chat-list-key-fn [item]
|
||||
(or (:chat-id item) (:public-key item) (:id item)))
|
||||
@@ -143,7 +157,11 @@
|
||||
(vals @data)))
|
||||
|
||||
(defn contacts-section-header [{:keys [title]}]
|
||||
[rn/view {:style {:border-top-width 1 :border-top-color colors/neutral-20 :padding-vertical 8 :padding-horizontal 20 :margin-top 8}}
|
||||
[rn/view {:style {:border-top-width 1
|
||||
:border-top-color colors/neutral-20
|
||||
:padding-vertical 8
|
||||
:padding-horizontal 20
|
||||
:margin-top 8}}
|
||||
[rn/text {:style (merge typography/font-medium typography/paragraph-2 {:color colors/neutral-50})} title]])
|
||||
|
||||
(defn find-contact-requests [notifications]
|
||||
@@ -162,23 +180,22 @@
|
||||
(defn contact-requests-sheet []
|
||||
[:f>
|
||||
(fn []
|
||||
(let [{window-height :height} (rn/use-window-dimensions)
|
||||
safe-area (safe-area/use-safe-area)
|
||||
notifications (<sub [:activity.center/notifications-grouped-by-date])
|
||||
(let [{window-height :height} (rn/use-window-dimensions)
|
||||
safe-area (safe-area/use-safe-area)
|
||||
notifications (rf/sub [:activity.center/notifications-grouped-by-date])
|
||||
{received-requests :received-requests} (find-contact-requests notifications)
|
||||
sent-requests []]
|
||||
sent-requests []]
|
||||
[rn/view {:style {:margin-left 20
|
||||
:height (- window-height (:top safe-area))}}
|
||||
[rn/touchable-opacity
|
||||
{:on-press #(>evt [:bottom-sheet/hide])
|
||||
:style
|
||||
{:background-color (colors/theme-colors colors/neutral-10 colors/neutral-80)
|
||||
:width 32
|
||||
:height 32
|
||||
:border-radius 10
|
||||
:justify-content :center
|
||||
:align-items :center
|
||||
:margin-bottom 24}}
|
||||
:style {:background-color (colors/theme-colors colors/neutral-10 colors/neutral-80)
|
||||
:width 32
|
||||
:height 32
|
||||
:border-radius 10
|
||||
:justify-content :center
|
||||
:align-items :center
|
||||
:margin-bottom 24}}
|
||||
[quo2.icons/icon :i/close {:color (colors/theme-colors "#000000" "#ffffff")}]]
|
||||
[rn/text {:style (merge
|
||||
typography/heading-1
|
||||
@@ -186,7 +203,8 @@
|
||||
{:color (colors/theme-colors "#000000" "#ffffff")})}
|
||||
(i18n/label :t/pending-requests)]
|
||||
[quo2.tabs/tabs
|
||||
{:style {:margin-top 12 :margin-bottom 20}
|
||||
{:style {:margin-top 12
|
||||
:margin-bottom 20}
|
||||
:size 32
|
||||
:on-change #(reset! selected-requests-tab %)
|
||||
:default-active :received
|
||||
@@ -200,7 +218,7 @@
|
||||
:render-fn received-cr-item/received-cr-item}]]))])
|
||||
|
||||
(defn get-display-name [{:keys [chat-id message]}]
|
||||
(let [name (first (<sub [:contacts/contact-two-names-by-identity chat-id]))
|
||||
(let [name (first (rf/sub [:contacts/contact-two-names-by-identity chat-id]))
|
||||
no-ens-name (str/blank? (get-in message [:content :ens-name]))]
|
||||
(if no-ens-name
|
||||
(first (str/split name " "))
|
||||
@@ -242,14 +260,13 @@
|
||||
[info-count (count requests)]])
|
||||
|
||||
(defn chats []
|
||||
(let [{:keys [items search-filter]} (<sub [:home-items])
|
||||
(let [{:keys [items search-filter]} (rf/sub [:home-items])
|
||||
current-active-tab @selected-tab
|
||||
items (prepare-items current-active-tab items)
|
||||
contacts (<sub [:contacts/active])
|
||||
contacts (rf/sub [:contacts/active])
|
||||
contacts (prepare-contacts contacts)
|
||||
notifications (<sub [:activity.center/notifications-grouped-by-date])
|
||||
notifications (rf/sub [:activity.center/notifications-grouped-by-date])
|
||||
{requests :received-requests new-info :has-unread?} (find-contact-requests notifications)]
|
||||
(println "wtfff" items contacts)
|
||||
[rn/view {:style {:flex 1}}
|
||||
[discover-card/discover-card {:title (i18n/label :t/invite-friends-to-status)
|
||||
:description (i18n/label :t/share-invite-link)}]
|
||||
@@ -274,7 +291,7 @@
|
||||
[list/flat-list
|
||||
{:key-fn chat-list-key-fn
|
||||
:getItemLayout get-item-layout
|
||||
:on-end-reached #(re-frame/dispatch [:chat.ui/show-more-chats])
|
||||
:on-end-reached #(rf/dispatch [:chat.ui/show-more-chats])
|
||||
:keyboard-should-persist-taps :always
|
||||
:data items
|
||||
:render-fn messages-home-item}]
|
||||
@@ -287,8 +304,8 @@
|
||||
:render-section-header-fn contacts-section-header
|
||||
:render-fn contact-item}]]))]))
|
||||
|
||||
(views/defview chats-list []
|
||||
(views/letsubs [loading? [:chats/loading?]]
|
||||
(defn chats-list []
|
||||
(let [loading? (rf/sub [:chats/loading?])]
|
||||
[:<>
|
||||
[connectivity/loading-indicator]
|
||||
(if loading?
|
||||
@@ -296,16 +313,24 @@
|
||||
[rn/activity-indicator {:animating true}]]
|
||||
[chats])]))
|
||||
|
||||
(views/defview plus-button []
|
||||
(views/letsubs [logging-in? [:multiaccounts/login]]
|
||||
(defn plus-button []
|
||||
(let [logging-in? (rf/sub [:multiaccounts/login])]
|
||||
[components.plus-button/plus-button
|
||||
{:on-press (when-not logging-in?
|
||||
#(re-frame/dispatch [:bottom-sheet/show-sheet :add-new {}]))
|
||||
#(rf/dispatch [:bottom-sheet/show-sheet :add-new {}]))
|
||||
:loading logging-in?
|
||||
:accessibility-label :new-chat-button}]))
|
||||
|
||||
(views/defview notifications-button []
|
||||
(views/letsubs [notif-count [:activity.center/notifications-count]]
|
||||
(defn plus-button-new-messages []
|
||||
(let [logging-in? (rf/sub [:multiaccounts/login])]
|
||||
[components.plus-button/plus-button
|
||||
{:on-press (when-not logging-in?
|
||||
#(rf/dispatch [:bottom-sheet/show-sheet :add-new-sheet-view {}]))
|
||||
:loading logging-in?
|
||||
:accessibility-label :new-chat-button}]))
|
||||
|
||||
(defn notifications-button []
|
||||
(let [notif-count (rf/sub [:activity.center/notifications-count])]
|
||||
[rn/view
|
||||
[quo2.button/button {:type :grey
|
||||
:size 32
|
||||
@@ -313,10 +338,10 @@
|
||||
:style {:margin-left 12}
|
||||
:accessibility-label :notifications-button
|
||||
:on-press #(do
|
||||
(re-frame/dispatch [:mark-all-activity-center-notifications-as-read])
|
||||
(rf/dispatch [:mark-all-activity-center-notifications-as-read])
|
||||
(if config/new-activity-center-enabled?
|
||||
(re-frame/dispatch [:navigate-to :activity-center])
|
||||
(re-frame/dispatch [:navigate-to :notifications-center])))}
|
||||
(rf/dispatch [:navigate-to :activity-center])
|
||||
(rf/dispatch [:navigate-to :notifications-center])))}
|
||||
[icons/icon :main-icons/notification2 {:color (colors/theme-colors colors/neutral-100 colors/white)}]]
|
||||
(when (pos? notif-count)
|
||||
[rn/view {:style (merge (styles/counter-public-container) {:top 5 :right 5})
|
||||
@@ -331,7 +356,7 @@
|
||||
:width 32
|
||||
:style {:margin-left 12}
|
||||
:on-press #(do
|
||||
(re-frame/dispatch [::qr-scanner/scan-code
|
||||
(rf/dispatch [::qr-scanner/scan-code
|
||||
{:handler ::qr-scanner/on-scan-success}]))}
|
||||
[icons/icon :main-icons/qr2 {:color (colors/theme-colors colors/neutral-100 colors/white)}]])
|
||||
|
||||
@@ -341,12 +366,12 @@
|
||||
:width 32
|
||||
:accessibility-label "scan-button"
|
||||
:on-press #(do
|
||||
(re-frame/dispatch [::qr-scanner/scan-code
|
||||
{:handler ::qr-scanner/on-scan-success}]))}
|
||||
(rf/dispatch [::qr-scanner/scan-code
|
||||
{:handler ::qr-scanner/on-scan-success}]))}
|
||||
[icons/icon :main-icons/scan2 {:color (colors/theme-colors colors/neutral-100 colors/white)}]])
|
||||
|
||||
(views/defview profile-button []
|
||||
(views/letsubs [{:keys [public-key preferred-name emoji]} [:multiaccount]]
|
||||
(defn profile-button []
|
||||
(let [{:keys [public-key preferred-name emoji]} (rf/sub [:multiaccount])]
|
||||
[rn/view
|
||||
[chat-icon/emoji-chat-icon-view public-key false preferred-name emoji
|
||||
{:size 28
|
||||
@@ -355,7 +380,7 @@
|
||||
(defn home []
|
||||
[:f>
|
||||
(fn []
|
||||
(quo.react/effect! #(re-frame/dispatch [:get-activity-center-notifications]))
|
||||
(quo.react/effect! #(rf/dispatch [:get-activity-center-notifications]))
|
||||
[rn/keyboard-avoiding-view {:style {:flex 1
|
||||
:background-color (colors/theme-colors colors/neutral-5 colors/neutral-95)}
|
||||
:ignore-offset true}
|
||||
@@ -376,6 +401,6 @@
|
||||
:margin-top 15
|
||||
:margin-bottom 20}
|
||||
[quo2.text/text {:size :heading-1 :weight :semi-bold} (i18n/label :t/messages)]
|
||||
[plus-button]]
|
||||
[plus-button-new-messages]]
|
||||
[chats-list]])])
|
||||
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
(ns status-im.ui2.screens.group.styles
|
||||
(:require [quo.design-system.colors :as colors]
|
||||
[quo2.foundations.colors :as quo2.colors]))
|
||||
|
||||
(def group-container
|
||||
{:flex 1
|
||||
:flex-direction :column
|
||||
:background-color (quo2.colors/theme-colors quo2.colors/white quo2.colors/neutral-90)})
|
||||
|
||||
(defn no-contact-text []
|
||||
{:margin-bottom 20
|
||||
:margin-horizontal 50
|
||||
:text-align :center
|
||||
:color colors/gray})
|
||||
|
||||
(def toolbar-header-container
|
||||
{:align-items :center})
|
||||
|
||||
(defn toolbar-sub-header []
|
||||
{:color colors/gray})
|
||||
|
||||
(def no-contacts
|
||||
{:flex 1
|
||||
:justify-content :center
|
||||
:align-items :center})
|
||||
|
||||
(defn search-container []
|
||||
{:border-bottom-color colors/gray-lighter
|
||||
:background-color (quo2.colors/theme-colors quo2.colors/white quo2.colors/neutral-90)
|
||||
:padding-horizontal 20
|
||||
:padding-top 10})
|
||||
|
||||
(defn members-title []
|
||||
{:color colors/gray
|
||||
:margin-top 14
|
||||
:margin-bottom 4})
|
||||
@@ -6,7 +6,8 @@
|
||||
[clojure.string :as string]
|
||||
[status-im.multiaccounts.core :as multiaccounts]
|
||||
[status-im.utils.gfycat.core :as gfycat]
|
||||
[status-im.ethereum.core :as ethereum]))
|
||||
[status-im.ethereum.core :as ethereum]
|
||||
[status-im.constants :as constants]))
|
||||
|
||||
(re-frame/reg-sub
|
||||
::query-current-chat-contacts
|
||||
@@ -68,6 +69,21 @@
|
||||
(fn [contacts]
|
||||
(contact.db/get-active-contacts contacts)))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:contacts/sorted-and-grouped-by-first-letter
|
||||
:<- [:contacts/active]
|
||||
:<- [:selected-contacts-count]
|
||||
(fn [[contacts selected-contacts-count]]
|
||||
(->> contacts
|
||||
(filter :mutual?)
|
||||
(map #(assoc % :allow-new-users? (< selected-contacts-count
|
||||
(dec constants/max-group-chat-participants))))
|
||||
(group-by (comp (fnil string/upper-case "") first :alias))
|
||||
(sort-by (fn [[title]] title))
|
||||
(map (fn [[title data]]
|
||||
{:title title
|
||||
:data data})))))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:contacts/sorted-contacts
|
||||
:<- [:contacts/active]
|
||||
|
||||
@@ -1457,6 +1457,8 @@
|
||||
"set-custom-fee": "Set custom fee",
|
||||
"not-enough-snt": "Not enough SNT",
|
||||
"add-new-contact": "Add new contact",
|
||||
"add-a-contact": "Add a contact",
|
||||
"enter-a-chat-key": "Enter a chat key or scan a QR",
|
||||
"you-dont-have-contacts": "You don’t have any contacts yet.",
|
||||
"set-max": "Set max",
|
||||
"continue-anyway": "Continue anyway",
|
||||
@@ -1853,5 +1855,11 @@
|
||||
"mark-user-untrustworthy": "Mark {{username}} as untrustworthy",
|
||||
"leave-group?": "Leave Group?",
|
||||
"block-user?": "Block User?",
|
||||
"clear-history?": "Clear History?"
|
||||
"clear-history?": "Clear History?",
|
||||
"chat-with": "Chat with {{selected-user}}",
|
||||
"setup-group-chat": "Setup group chat",
|
||||
"search-contacts": "Search contacts",
|
||||
"who-are-you-looking-for": "Who are you looking for ?",
|
||||
"close-contact-search": "Close contact search",
|
||||
"selected-count-from-max": "{{selected}}/{{max}}"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user