Rewrite Community List component (#16527)
Re-implements the component Community List according to guidelines and, most importantly, fixes a bunch of issues and tries to achieve 100% compatibility with Figma. The new implementation is trying to mirror Figma properties as much as possible. Fixes https://github.com/status-im/status-mobile/issues/16447 Changelog: - Refactor to guidelines. - Fix: original implementation in money/format-amounts had a bug caught by new unit tests ("1000000" was formatted incorrectly). - Fix: Community permission tag correct background when theme is overridden and when blur is enabled. - Fix: Notification dot uses correct colors for dark mode and blur variants. - Fix: Community stats background when blur is enabled. - Fix: Community stats icon color when blur is enabled. - Add: The component's Quo preview screen is smarter and will only show form fields (aka descriptors) that are relevant for each component type (engage, discover or share). - Add: replace hardcoded community statistics with an implementation that actually accepts real numbers and which are correctly formatted. - Add: New Quo descriptor type number. - Add: Component uses correct shadows according to foundations/shadows.
This commit is contained in:
parent
72e56dcf75
commit
6170686e34
|
@ -29,52 +29,6 @@
|
|||
unread-messages?
|
||||
[unread-grey-dot :unviewed-messages-public]))
|
||||
|
||||
(defn communities-list-view-item
|
||||
[props
|
||||
{:keys [name
|
||||
locked?
|
||||
status
|
||||
muted?
|
||||
unread-messages?
|
||||
unread-mentions-count
|
||||
community-icon
|
||||
tokens]}]
|
||||
[rn/view
|
||||
{:style (merge (style/community-card 16)
|
||||
{:margin-bottom 12})}
|
||||
[rn/touchable-highlight
|
||||
(merge {:style {:height 56
|
||||
:border-radius 16}}
|
||||
props)
|
||||
[rn/view {:style style/detail-container}
|
||||
[rn/view (style/list-info-container)
|
||||
[community-icon/community-icon
|
||||
{:images community-icon} 32]
|
||||
[rn/view
|
||||
{:flex 1
|
||||
:margin-horizontal 12}
|
||||
[text/text
|
||||
{:weight :semi-bold
|
||||
:size :paragraph-1
|
||||
:accessibility-label :community-name-text
|
||||
:number-of-lines 1
|
||||
:ellipsize-mode :tail
|
||||
:style {:color (when muted?
|
||||
(colors/theme-colors
|
||||
colors/neutral-40
|
||||
colors/neutral-60))}}
|
||||
name]
|
||||
[community-view/community-stats-column
|
||||
{:type :list-view}]]
|
||||
(if (= status :gated)
|
||||
[community-view/permission-tag-container
|
||||
{:locked? locked?
|
||||
:tokens tokens}]
|
||||
[notification-view
|
||||
{:muted? muted?
|
||||
:unread-mentions-count unread-mentions-count
|
||||
:unread-messages? unread-messages?}])]]]])
|
||||
|
||||
(defn communities-membership-list-item
|
||||
[props
|
||||
{:keys [name
|
||||
|
|
|
@ -5,13 +5,17 @@
|
|||
[quo2.components.tags.permission-tag :as permission]
|
||||
[quo2.components.tags.tag :as tag]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[quo.gesture-handler :as gesture-handler] ;;TODO move to quo2
|
||||
[react-native.core :as rn]))
|
||||
[quo2.theme :as theme]
|
||||
[react-native.core :as rn]
|
||||
[react-native.gesture :as gesture]
|
||||
utils.money))
|
||||
|
||||
(defn community-stats
|
||||
[{:keys [icon members-count icon-color]}]
|
||||
[rn/view (style/stats-count-container)
|
||||
[rn/view {:margin-right 4}
|
||||
[{:keys [icon members-count icon-color accessibility-label]}]
|
||||
[rn/view
|
||||
{:accessibility-label accessibility-label
|
||||
:style (style/stats-count-container)}
|
||||
[rn/view {:margin-right 2}
|
||||
[icons/icon icon
|
||||
{:container-style {:align-items :center
|
||||
:justify-content :center}
|
||||
|
@ -20,29 +24,32 @@
|
|||
:color icon-color}]]
|
||||
[text/text
|
||||
{:weight :regular
|
||||
:size :paragraph-1}
|
||||
:size :paragraph-2}
|
||||
members-count]])
|
||||
|
||||
(defn community-stats-column
|
||||
[{:keys [type]}]
|
||||
(let [icon-color (colors/theme-colors colors/neutral-50 colors/neutral-40)]
|
||||
[{:keys [type theme blur? members-count active-count]}]
|
||||
(let [icon-color (if (and (= :dark theme) blur?)
|
||||
colors/white-opa-40
|
||||
(colors/theme-colors colors/neutral-50 colors/neutral-40 theme))]
|
||||
[rn/view
|
||||
(if (= type :card-view)
|
||||
(style/card-stats-container)
|
||||
(style/list-stats-container))
|
||||
[community-stats
|
||||
{:icon :i/group
|
||||
:members-count "629.2K" ;;TODO here should be formatted value, use money/format-members from
|
||||
;;outside this component
|
||||
:icon-color icon-color}]
|
||||
{:accessibility-label :stats-members-count
|
||||
:icon :i/group
|
||||
:members-count (utils.money/format-amount members-count)
|
||||
:icon-color icon-color}]
|
||||
[community-stats
|
||||
{:icon :i/lightning
|
||||
:members-count "112.1K"
|
||||
:icon-color icon-color}]]))
|
||||
{:accessibility-label :stats-active-count
|
||||
:icon :i/active-members
|
||||
:members-count (utils.money/format-amount active-count)
|
||||
:icon-color icon-color}]]))
|
||||
|
||||
(defn community-tags
|
||||
[{:keys [tags container-style last-item-style]}]
|
||||
[gesture-handler/scroll-view
|
||||
[gesture/scroll-view
|
||||
{:shows-horizontal-scroll-indicator false
|
||||
:horizontal true
|
||||
:style container-style}
|
||||
|
@ -84,13 +91,16 @@
|
|||
:style {:margin-top (if (= size :large) 8 2)}}
|
||||
description])])
|
||||
|
||||
(defn permission-tag-container
|
||||
[{:keys [locked? tokens on-press]}]
|
||||
(defn- permission-tag-container-internal
|
||||
[{:keys [locked? blur? tokens on-press theme]}]
|
||||
[permission/tag
|
||||
{:background-color (colors/theme-colors
|
||||
colors/neutral-10
|
||||
colors/neutral-80)
|
||||
:locked? locked?
|
||||
:tokens tokens
|
||||
:size 24
|
||||
:on-press on-press}])
|
||||
{:accessibility-label :permission-tag
|
||||
:background-color (if (and (= :dark theme) blur?)
|
||||
colors/white-opa-10
|
||||
(colors/theme-colors colors/neutral-10 colors/neutral-80 theme))
|
||||
:locked? locked?
|
||||
:tokens tokens
|
||||
:size 24
|
||||
:on-press on-press}])
|
||||
|
||||
(def permission-tag-container (theme/with-theme permission-tag-container-internal))
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
(ns quo2.components.list-items.community.component-spec
|
||||
(:require [quo2.components.list-items.community.view :as component]
|
||||
[test-helpers.component :as h]))
|
||||
|
||||
(h/describe "Community list"
|
||||
(h/test "default render"
|
||||
(h/render [component/view {}])
|
||||
(h/is-truthy (h/get-by-label-text :community-item-title))
|
||||
(h/is-null (h/query-by-label-text :community-item-info)))
|
||||
|
||||
(h/describe "type share"
|
||||
(h/test "renders"
|
||||
(h/render [component/view
|
||||
{:type :share
|
||||
:title "Title"
|
||||
:subtitle "Subtitle"}])
|
||||
(h/is-truthy (h/get-by-text "# Title"))
|
||||
(h/is-truthy (h/get-by-text "Subtitle"))
|
||||
(h/is-null (h/query-by-label-text :community-item-info))))
|
||||
|
||||
(h/describe "type engage"
|
||||
(h/test "default, no info"
|
||||
(h/render [component/view {:type :engage}])
|
||||
(h/is-null (h/query-by-label-text :community-item-info)))
|
||||
|
||||
(h/test "info is :navigation"
|
||||
(h/render [component/view {:type :engage :info :navigation}])
|
||||
(h/is-truthy (h/get-by-label-text :info-navigation)))
|
||||
|
||||
(h/test "info is :token-gated"
|
||||
(h/render [component/view {:type :engage :info :token-gated}])
|
||||
(h/is-truthy (h/get-by-label-text :info-token-gated)))
|
||||
|
||||
(h/test "info is :muted"
|
||||
(h/render [component/view {:type :engage :info :muted}])
|
||||
(h/is-truthy (h/get-by-label-text :info-muted)))
|
||||
|
||||
(h/test "info is :notification"
|
||||
(h/render [component/view {:type :engage :info :notification}])
|
||||
(h/is-truthy (h/get-by-label-text :info-notification-dot)))
|
||||
|
||||
(h/test "info is :mention"
|
||||
(h/render [component/view
|
||||
{:type :engage
|
||||
:info :mention
|
||||
:unread-count 9000}])
|
||||
(h/is-truthy (h/get-by-text "99+"))))
|
||||
|
||||
(h/describe "type discover"
|
||||
(h/test "default, no info, no member stats"
|
||||
(h/render [component/view {:type :discover}])
|
||||
(h/is-null (h/query-by-label-text :stats-members-count))
|
||||
(h/is-null (h/query-by-label-text :community-item-info)))
|
||||
|
||||
(h/test "info is :token-gated, show member stats"
|
||||
(let [on-press-info (h/mock-fn)]
|
||||
(h/render [component/view
|
||||
{:type :discover
|
||||
:info :token-gated
|
||||
:locked? true
|
||||
:blur? false
|
||||
:on-press-info on-press-info
|
||||
:members {:members-count 9876 :active-count 1350}
|
||||
:tokens [{:id 1 :group [{:id 1}]}]}])
|
||||
(h/is-truthy (h/get-by-label-text :community-item-info))
|
||||
(h/is-truthy (h/get-by-label-text :permission-tag))
|
||||
(h/is-truthy (h/get-by-label-text :stats-members-count))
|
||||
(h/is-truthy (h/get-by-label-text :stats-active-count))
|
||||
(h/fire-event :press (h/get-by-label-text :permission-tag))
|
||||
(h/was-called on-press-info)))))
|
|
@ -0,0 +1,65 @@
|
|||
(ns quo2.components.list-items.community.style
|
||||
(:require [quo2.foundations.colors :as colors]
|
||||
[quo2.foundations.shadows :as shadows]))
|
||||
|
||||
(def logo
|
||||
{:border-radius 50
|
||||
:border-width 0
|
||||
:width 32
|
||||
:height 32
|
||||
:margin-right 10})
|
||||
|
||||
(defn notification-dot
|
||||
[blur? theme]
|
||||
{:width 8
|
||||
:height 8
|
||||
:border-radius 4
|
||||
:background-color (if (and (= :dark theme) blur?)
|
||||
colors/white-opa-40
|
||||
(colors/theme-colors colors/neutral-40 colors/neutral-60 theme))})
|
||||
|
||||
(defn title
|
||||
[{:keys [type info blur? theme]}]
|
||||
{:color (cond
|
||||
(and (= type :engage) (= info :muted))
|
||||
(colors/theme-colors colors/neutral-40 colors/neutral-60 theme)
|
||||
|
||||
(and (= type :engage) (= info :default) (not blur?))
|
||||
(colors/theme-colors colors/neutral-50 colors/neutral-40 theme)
|
||||
|
||||
(and (= type :engage) (= info :default) (= :dark theme) blur?)
|
||||
colors/white-70-blur
|
||||
|
||||
:else
|
||||
(colors/theme-colors colors/neutral-100 colors/white theme))})
|
||||
|
||||
(defn subtitle
|
||||
[blur? theme]
|
||||
{:color (if (and (= :dark theme) blur?)
|
||||
colors/white-opa-40
|
||||
(colors/theme-colors colors/neutral-50 colors/neutral-40 theme))})
|
||||
|
||||
(defn container
|
||||
[{:keys [type pressed? blur? customization-color theme info]}]
|
||||
(merge {:padding-vertical 8
|
||||
:padding-horizontal 12
|
||||
:border-radius 12
|
||||
:flex-direction :row
|
||||
:align-items :center
|
||||
:background-color (cond
|
||||
(and pressed? (= type :engage) (= info :default) (= :dark theme) blur?)
|
||||
colors/white-opa-5
|
||||
|
||||
(and pressed? (#{:engage :share} type))
|
||||
(colors/theme-alpha customization-color 0.05 0.05)
|
||||
|
||||
(and (not pressed?) (= type :discover) (not blur?))
|
||||
(colors/theme-colors colors/white colors/neutral-90 theme)
|
||||
|
||||
(and (not pressed?) (= type :discover) (= :dark theme) blur?)
|
||||
colors/white-opa-5
|
||||
|
||||
(and pressed? (= type :discover))
|
||||
(colors/theme-colors colors/white :transparent theme))}
|
||||
(when (and (= type :discover) (not pressed?))
|
||||
(:shadow-3 shadows/normal-scale))))
|
|
@ -0,0 +1,165 @@
|
|||
(ns quo2.components.list-items.community.view
|
||||
(:require [quo2.components.community.community-view :as community-view]
|
||||
[quo2.components.counter.counter :as counter]
|
||||
[quo2.components.icon :as icons]
|
||||
[quo2.components.list-items.community.style :as style]
|
||||
[quo2.components.markdown.text :as text]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[quo2.theme :as quo.theme]
|
||||
[react-native.core :as rn]
|
||||
[reagent.core :as reagent]))
|
||||
|
||||
(defn- logo-component
|
||||
[logo]
|
||||
[rn/image {:source logo :style style/logo}])
|
||||
|
||||
(defn- title-component
|
||||
[{:keys [title type] :as props}]
|
||||
[text/text
|
||||
{:weight :semi-bold
|
||||
:size :paragraph-1
|
||||
:accessibility-label :community-item-title
|
||||
:number-of-lines 1
|
||||
:ellipsize-mode :tail
|
||||
:style (style/title props)}
|
||||
(if (= type :share)
|
||||
(str "# " title)
|
||||
title)])
|
||||
|
||||
(defn- subtitle-component
|
||||
[subtitle blur? theme]
|
||||
[text/text
|
||||
{:size :paragraph-2
|
||||
:number-of-lines 1
|
||||
:accessibility-label :community-item-subtitle
|
||||
:style (style/subtitle blur? theme)}
|
||||
subtitle])
|
||||
|
||||
(defn- notification-dot
|
||||
[blur? theme]
|
||||
[rn/view
|
||||
{:style (style/notification-dot blur? theme)
|
||||
:accessibility-label :info-notification-dot}])
|
||||
|
||||
(defn- info-component
|
||||
[{:keys [customization-color info type blur? locked? on-press-info theme tokens unread-count]}]
|
||||
(let [component
|
||||
(cond
|
||||
(and (= type :discover) (= info :token-gated) (seq tokens))
|
||||
[community-view/permission-tag-container
|
||||
{:locked? locked?
|
||||
:tokens tokens
|
||||
:on-press on-press-info
|
||||
:theme theme
|
||||
:blur? blur?}]
|
||||
|
||||
(and (= type :engage) (= info :mention) (pos? unread-count))
|
||||
[counter/counter
|
||||
{:type :default
|
||||
:override-bg-color customization-color}
|
||||
unread-count]
|
||||
|
||||
(and (= type :engage) (= info :notification))
|
||||
[notification-dot blur? theme]
|
||||
|
||||
(and (= type :engage) (= info :muted))
|
||||
[icons/icon :i/muted
|
||||
{:accessibility-label :info-muted
|
||||
:color (colors/theme-colors colors/neutral-40 colors/neutral-60 theme)}]
|
||||
|
||||
(and (= type :engage) (= info :token-gated))
|
||||
[icons/icon (if locked? :i/locked :i/unlocked)
|
||||
{:accessibility-label :info-token-gated
|
||||
:color (colors/theme-colors colors/neutral-50 colors/neutral-40 theme)}]
|
||||
|
||||
(and (= type :engage) (= info :navigation))
|
||||
[icons/icon :i/chevron-right
|
||||
{:accessibility-label :info-navigation
|
||||
:color (colors/theme-colors colors/neutral-50 colors/neutral-40 theme)}])]
|
||||
(when component
|
||||
[rn/view
|
||||
{:accessibility-label :community-item-info
|
||||
:style {:margin-left 10}}
|
||||
component])))
|
||||
|
||||
(defn- view-internal
|
||||
"Options:
|
||||
|
||||
:type - :discover/engage/share
|
||||
:info - keyword - Acceptable values vary based on the :type option.
|
||||
|
||||
:title - string
|
||||
:logo - image resource
|
||||
:theme - :light/dark
|
||||
|
||||
:container-style - style map - Override styles in top-level view component.
|
||||
|
||||
:members - {:members-count number, :active-count number} - When non-nil, the
|
||||
statistics component will be shown.
|
||||
|
||||
:tokens - A sequence of maps, e.g. [{:id 1 :group [{:id 1 :token-icon an-icon}]}].
|
||||
|
||||
:locked? - boolean - When true, the permission-tag icon or the token gated
|
||||
icon will appear as closed.
|
||||
|
||||
:blur? - boolean - It will be taken into account when true and dark mode is
|
||||
enabled.
|
||||
|
||||
:customization-color - color - It will be passed down to components that
|
||||
should vary based on a custom color.
|
||||
|
||||
:on-press/:on-long-press - fn - Used by the top-level pressable component.
|
||||
|
||||
:on-press-info - fn - Will be called when the info component is pressed.
|
||||
|
||||
:unread-count - number - When the info is :mention, it will be used to show
|
||||
the number of unread mentions.
|
||||
"
|
||||
[]
|
||||
(let [pressed? (reagent/atom false)]
|
||||
(fn [{:keys [members type info tokens locked? title subtitle
|
||||
logo blur? customization-color
|
||||
on-press on-long-press on-press-info
|
||||
container-style unread-count theme]}]
|
||||
[rn/pressable
|
||||
{:accessibility-label :container
|
||||
:on-press-in (fn [] (reset! pressed? true))
|
||||
:on-press on-press
|
||||
:on-long-press on-long-press
|
||||
:on-press-out (fn [] (reset! pressed? false))
|
||||
:style (merge (style/container {:blur? blur?
|
||||
:customization-color customization-color
|
||||
:info info
|
||||
:type type
|
||||
:pressed? @pressed?
|
||||
:theme theme})
|
||||
container-style)}
|
||||
[logo-component logo]
|
||||
[rn/view {:style {:flex 1}}
|
||||
[title-component
|
||||
{:blur? blur?
|
||||
:info info
|
||||
:theme theme
|
||||
:title title
|
||||
:type type}]
|
||||
(when (and (= type :share) subtitle)
|
||||
[subtitle-component subtitle blur? theme])
|
||||
(when (and members (= type :discover))
|
||||
[community-view/community-stats-column
|
||||
{:type :list-view
|
||||
:theme theme
|
||||
:blur? blur?
|
||||
:members-count (:members-count members)
|
||||
:active-count (:active-count members)}])]
|
||||
[info-component
|
||||
{:blur? blur?
|
||||
:customization-color customization-color
|
||||
:info info
|
||||
:type type
|
||||
:locked? locked?
|
||||
:on-press-info on-press-info
|
||||
:theme theme
|
||||
:tokens tokens
|
||||
:unread-count unread-count}]])))
|
||||
|
||||
(def view (quo.theme/with-theme view-internal))
|
|
@ -132,13 +132,14 @@
|
|||
|
||||
(defn tag
|
||||
[_ _]
|
||||
(fn [{:keys [locked? tokens size background-color on-press]
|
||||
(fn [{:keys [locked? tokens size background-color on-press accessibility-label]
|
||||
:or {size 24}}]
|
||||
[base-tag/base-tag
|
||||
{:background-color background-color
|
||||
:size size
|
||||
:type :permission
|
||||
:on-press on-press}
|
||||
{:accessibility-label accessibility-label
|
||||
:background-color background-color
|
||||
:size size
|
||||
:type :permission
|
||||
:on-press on-press}
|
||||
[rn/view
|
||||
{:flex-direction :row
|
||||
:align-items :center
|
||||
|
|
|
@ -48,6 +48,7 @@
|
|||
quo2.components.links.url-preview-list.view
|
||||
quo2.components.links.url-preview.view
|
||||
quo2.components.list-items.channel
|
||||
quo2.components.list-items.community.view
|
||||
quo2.components.list-items.menu-item
|
||||
quo2.components.list-items.preview-list
|
||||
quo2.components.list-items.user-list
|
||||
|
@ -149,7 +150,6 @@
|
|||
|
||||
;;;; COMMUNITY
|
||||
(def community-card-view-item quo2.components.community.community-card-view/community-card-view-item)
|
||||
(def communities-list-view-item quo2.components.community.community-list-view/communities-list-view-item)
|
||||
(def communities-membership-list-item
|
||||
quo2.components.community.community-list-view/communities-membership-list-item)
|
||||
(def community-stats-column quo2.components.community.community-view/community-stats-column)
|
||||
|
@ -193,6 +193,7 @@
|
|||
(def menu-item quo2.components.list-items.menu-item/menu-item)
|
||||
(def preview-list quo2.components.list-items.preview-list/preview-list)
|
||||
(def user-list quo2.components.list-items.user-list/user-list)
|
||||
(def community-list-item quo2.components.list-items.community.view/view)
|
||||
|
||||
;;;; LOADERS
|
||||
(def skeleton quo2.components.loaders.skeleton/skeleton)
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
[quo2.components.links.link-preview.component-spec]
|
||||
[quo2.components.links.url-preview-list.component-spec]
|
||||
[quo2.components.links.url-preview.component-spec]
|
||||
[quo2.components.list-items.community.component-spec]
|
||||
[quo2.components.markdown.--tests--.text-component-spec]
|
||||
[quo2.components.markdown.list.component-spec]
|
||||
[quo2.components.notifications.notification.component-spec]
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
(def text (reagent/adapt-react-class (.-Text ^js react-native)))
|
||||
(def text-input (reagent/adapt-react-class (.-TextInput ^js react-native)))
|
||||
|
||||
(def pressable (reagent/adapt-react-class (.-Pressable ^js react-native)))
|
||||
(def touchable-opacity (reagent/adapt-react-class (.-TouchableOpacity ^js react-native)))
|
||||
(def touchable-highlight (reagent/adapt-react-class (.-TouchableHighlight ^js react-native)))
|
||||
(def touchable-without-feedback
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
(ns status-im.utils.money
|
||||
(:require ["bignumber.js" :as BigNumber]
|
||||
[clojure.string :as string]
|
||||
[utils.i18n :as i18n]))
|
||||
[clojure.string :as string]))
|
||||
|
||||
;; The BigNumber version included in web3 sometimes hangs when dividing large
|
||||
;; numbers Hence we want to use these functions instead of fromWei etc, which
|
||||
|
@ -222,11 +221,3 @@
|
|||
(defn div-and-round
|
||||
[bn1 bn2]
|
||||
(.round (.dividedBy ^js bn1 bn2) 0))
|
||||
|
||||
(defn format-members
|
||||
[amount]
|
||||
(if (> amount 1000000)
|
||||
(str (with-precision (/ amount 1000000) 1) (i18n/label :t/M))
|
||||
(if (and (> amount 999) (< amount 1000000))
|
||||
(str (with-precision (/ amount 1000) 1) (i18n/label :t/K))
|
||||
amount)))
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
(if (= view-type :card-view)
|
||||
[quo/community-card-view-item (assoc item :width width :cover cover)
|
||||
#(rf/dispatch [:navigate-to :community-overview (:id item)])]
|
||||
[quo/communities-list-view-item
|
||||
[quo/community-list-item
|
||||
{:on-press (fn []
|
||||
(rf/dispatch [:communities/load-category-states (:id item)])
|
||||
(rf/dispatch [:dismiss-keyboard])
|
||||
|
@ -142,7 +142,7 @@
|
|||
(get mock-community-item-data :data)
|
||||
{:cover cover})
|
||||
#(rf/dispatch [:navigate-to :community-overview (:id community)])]
|
||||
[quo/communities-list-view-item
|
||||
[quo/community-list-item
|
||||
{:on-press (fn []
|
||||
(rf/dispatch [:communities/load-category-states (:id community)])
|
||||
(rf/dispatch [:dismiss-keyboard])
|
||||
|
|
|
@ -1,69 +0,0 @@
|
|||
(ns status-im2.contexts.quo-preview.community.community-list-view
|
||||
(:require [quo.previews.preview :as preview]
|
||||
[quo.react-native :as rn]
|
||||
[quo2.components.community.community-list-view :as community-list-view]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[reagent.core :as reagent]
|
||||
[status-im2.contexts.quo-preview.community.data :as data]))
|
||||
|
||||
(def descriptor
|
||||
[{:label "Notifications:"
|
||||
:key :notifications
|
||||
:type :select
|
||||
:options [{:key :muted
|
||||
:value "Muted"}
|
||||
{:key :unread-mentions-count
|
||||
:value "Mention counts"}
|
||||
{:key :unread-messages-count
|
||||
:value "Unread messages"}]}
|
||||
{:label "Status:"
|
||||
:key :status
|
||||
:type :select
|
||||
:options [{:key :gated
|
||||
:value "Gated"}
|
||||
{:key :open
|
||||
:value "Open"}]}
|
||||
{:label "Locked:"
|
||||
:key :locked?
|
||||
:type :boolean}])
|
||||
|
||||
(defn cool-preview
|
||||
[]
|
||||
(let [notifications (reagent/atom (:notifications nil))
|
||||
state (reagent/atom {:locked? true
|
||||
:notifications nil
|
||||
:status (if notifications
|
||||
:gated
|
||||
:open)})]
|
||||
(fn []
|
||||
[rn/touchable-without-feedback {:on-press rn/dismiss-keyboard!}
|
||||
[rn/view {:padding-bottom 150}
|
||||
[rn/view
|
||||
{:flex 1
|
||||
:padding 16}
|
||||
[preview/customizer state descriptor]]
|
||||
[rn/view
|
||||
{:padding-vertical 60
|
||||
:justify-content :center}
|
||||
[community-list-view/communities-list-view-item {}
|
||||
(cond-> (merge @state data/community)
|
||||
(= :muted (:notifications @state))
|
||||
(assoc :muted? true)
|
||||
|
||||
(= :unread-mentions-count (:notifications @state))
|
||||
(assoc :unread-mentions-count 5)
|
||||
|
||||
(= :unread-messages-count (:notifications @state))
|
||||
(assoc :unread-messages? true))]]]])))
|
||||
|
||||
(defn preview-community-list-view
|
||||
[]
|
||||
[rn/view
|
||||
{:background-color (colors/theme-colors colors/neutral-5
|
||||
colors/neutral-95)
|
||||
:flex 1}
|
||||
[rn/flat-list
|
||||
{:flex 1
|
||||
:keyboard-should-persist-taps :always
|
||||
:header [cool-preview]
|
||||
:key-fn str}]])
|
|
@ -0,0 +1,132 @@
|
|||
(ns status-im2.contexts.quo-preview.list-items.community-list
|
||||
(:require [quo2.core :as quo]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[quo2.theme :as quo.theme]
|
||||
[react-native.core :as rn]
|
||||
[reagent.core :as reagent]
|
||||
[status-im2.common.resources :as resources]
|
||||
[status-im2.contexts.quo-preview.community.data :as data]
|
||||
[status-im2.contexts.quo-preview.preview :as preview]))
|
||||
|
||||
(def descriptor-type
|
||||
{:label "Type:"
|
||||
:key :type
|
||||
:type :select
|
||||
:options [{:key :discover :value "Discover"}
|
||||
{:key :engage :value "Engage"}
|
||||
{:key :share :value "Share"}]})
|
||||
|
||||
(def descriptor-locked
|
||||
{:label "Locked?" :key :locked? :type :boolean})
|
||||
|
||||
(def descriptor-unread-count
|
||||
{:label "Unread count:" :key :unread-count :type :number})
|
||||
|
||||
(def descriptor-title
|
||||
{:label "Title:" :key :title :type :text})
|
||||
|
||||
(def descriptor-blur
|
||||
{:label "Blur?" :key :blur? :type :boolean})
|
||||
|
||||
(def descriptor-member-stats
|
||||
[{:label "Total member count:"
|
||||
:key :members-count
|
||||
:type :number}
|
||||
{:label "Active member count:"
|
||||
:key :active-count
|
||||
:type :number}])
|
||||
|
||||
(def descriptors-base
|
||||
[descriptor-type
|
||||
descriptor-title
|
||||
(preview/customization-color-option)])
|
||||
|
||||
(def descriptors-type-discover
|
||||
(conj descriptors-base
|
||||
{:label "Info:"
|
||||
:key :info
|
||||
:type :select
|
||||
:options [{:key :token-gated :value "Token gated"}
|
||||
{:key :default :value "Default"}]}
|
||||
{:label "Member stats?"
|
||||
:key :members?
|
||||
:type :boolean}))
|
||||
|
||||
(def descriptors-type-engage
|
||||
(conj descriptors-base
|
||||
{:label "Info:"
|
||||
:key :info
|
||||
:type :select
|
||||
:options [{:key :notification :value "Notification"}
|
||||
{:key :mention :value "Mention"}
|
||||
{:key :muted :value "Muted"}
|
||||
{:key :token-gated :value "Token gated"}
|
||||
{:key :navigation :value "Navigation"}
|
||||
{:key :default :value "Default"}]}))
|
||||
|
||||
(def descriptors-type-share
|
||||
(conj descriptors-base {:label "Subtitle:" :key :subtitle :type :text}))
|
||||
|
||||
(defn descriptors
|
||||
[{:keys [members? info] :as state}]
|
||||
(let [descs (case (:type state)
|
||||
:discover (cond-> descriptors-type-discover
|
||||
members?
|
||||
(into descriptor-member-stats)
|
||||
|
||||
(= info :token-gated)
|
||||
(into [descriptor-locked]))
|
||||
:engage (cond-> descriptors-type-engage
|
||||
(= info :token-gated)
|
||||
(into [descriptor-locked])
|
||||
|
||||
(= info :mention)
|
||||
(into [descriptor-unread-count]))
|
||||
:share descriptors-type-share
|
||||
nil)]
|
||||
(if (quo.theme/dark?)
|
||||
(into [descriptor-blur] descs)
|
||||
descs)))
|
||||
|
||||
(defn cool-preview
|
||||
[]
|
||||
(let [state (reagent/atom {:blur? false
|
||||
:customization-color :blue
|
||||
:info :token-gated
|
||||
:type :discover
|
||||
:members? false
|
||||
:locked? false
|
||||
:title "Status"
|
||||
:subtitle "Subtitle"
|
||||
:members-count 629200
|
||||
:active-count 112100
|
||||
:unread-count 5})]
|
||||
(fn []
|
||||
(let [customization-color (colors/custom-color-by-theme (:customization-color @state) 50 60)]
|
||||
[rn/touchable-without-feedback {:on-press rn/dismiss-keyboard!}
|
||||
[rn/view {:style {:margin-bottom 20}}
|
||||
[preview/customizer state (descriptors @state)]
|
||||
[rn/view {:style {:margin-vertical 30 :align-items :center}}
|
||||
[quo/community-list-item
|
||||
(merge @state
|
||||
{:container-style {:width 335}
|
||||
:logo (resources/get-mock-image :status-logo)
|
||||
:tokens (:tokens data/community)
|
||||
:customization-color customization-color
|
||||
:on-press #(js/alert "List item pressed")
|
||||
:on-long-press #(js/alert "Long pressed item")
|
||||
:on-press-info #(js/alert "Info pressed")
|
||||
:members (when (:members? @state)
|
||||
{:members-count (:members-count @state)
|
||||
:active-count (:active-count @state)})})]]]]))))
|
||||
|
||||
(defn preview
|
||||
[]
|
||||
[rn/view
|
||||
{:style {:background-color (colors/theme-colors colors/neutral-5 colors/neutral-95)
|
||||
:flex 1}}
|
||||
[rn/flat-list
|
||||
{:style {:flex 1}
|
||||
:keyboard-should-persist-taps :always
|
||||
:header [cool-preview]
|
||||
:key-fn str}]])
|
|
@ -22,7 +22,6 @@
|
|||
[status-im2.contexts.quo-preview.code.snippet :as code-snippet]
|
||||
[status-im2.contexts.quo-preview.colors.color-picker :as color-picker]
|
||||
[status-im2.contexts.quo-preview.community.community-card-view :as community-card]
|
||||
[status-im2.contexts.quo-preview.community.community-list-view :as community-list-view]
|
||||
[status-im2.contexts.quo-preview.community.community-membership-list-view :as
|
||||
community-membership-list-view]
|
||||
[status-im2.contexts.quo-preview.community.discover-card :as discover-card]
|
||||
|
@ -52,6 +51,7 @@
|
|||
[status-im2.contexts.quo-preview.list-items.channel :as channel]
|
||||
[status-im2.contexts.quo-preview.list-items.preview-lists :as preview-lists]
|
||||
[status-im2.contexts.quo-preview.list-items.user-list :as user-list]
|
||||
[status-im2.contexts.quo-preview.list-items.community-list :as community-list]
|
||||
[status-im2.contexts.quo-preview.markdown.text :as text]
|
||||
[status-im2.contexts.quo-preview.markdown.list :as markdown-list]
|
||||
[status-im2.contexts.quo-preview.messages.author :as messages-author]
|
||||
|
@ -148,9 +148,6 @@
|
|||
:community [{:name :community-card-view
|
||||
:options {:topBar {:visible true}}
|
||||
:component community-card/preview-community-card}
|
||||
{:name :community-list-view
|
||||
:options {:topBar {:visible true}}
|
||||
:component community-list-view/preview-community-list-view}
|
||||
{:name :community-membership-list-view
|
||||
:options {:topBar {:visible true}}
|
||||
:component community-membership-list-view/preview-community-list-view}
|
||||
|
@ -237,6 +234,10 @@
|
|||
:list-items [{:name :channel
|
||||
:options {:topBar {:visible true}}
|
||||
:component channel/preview-channel}
|
||||
{:name :community-list
|
||||
:options {:insets {:top? true}
|
||||
:topBar {:visible true}}
|
||||
:component community-list/preview}
|
||||
{:name :preview-lists
|
||||
:options {:topBar {:visible true}}
|
||||
:component preview-lists/preview-preview-lists}
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
(ns status-im2.contexts.quo-preview.preview
|
||||
(:require [clojure.string :as string]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[quo2.theme :as theme]
|
||||
[react-native.blur :as blur]
|
||||
[react-native.core :as rn]
|
||||
[reagent.core :as reagent]
|
||||
[status-im2.common.resources :as resources]
|
||||
[quo2.theme :as theme])
|
||||
utils.number)
|
||||
(:require-macros status-im2.contexts.quo-preview.preview))
|
||||
|
||||
(def container
|
||||
|
@ -122,6 +123,25 @@
|
|||
(when limit
|
||||
{:max-length limit}))]]]))
|
||||
|
||||
(defn customizer-number
|
||||
[{:keys [label state default] :as args}]
|
||||
(let [state* (reagent/cursor state [(:key args)])]
|
||||
[rn/view {:style container}
|
||||
[label-view state label]
|
||||
[rn/view {:style {:flex 0.6}}
|
||||
[rn/text-input
|
||||
(merge
|
||||
{:value (str @state*)
|
||||
:show-cancel false
|
||||
:style {:border-radius 4
|
||||
:border-width 1
|
||||
:color (colors/theme-colors colors/neutral-100 colors/white)
|
||||
:border-color (colors/theme-colors colors/neutral-100 colors/white)}
|
||||
:keyboard-appearance (theme/theme-value :light :dark)
|
||||
:on-change-text (fn [v]
|
||||
(reset! state* (utils.number/parse-int v default))
|
||||
(reagent/flush))})]]]))
|
||||
|
||||
(defn value-for-key
|
||||
[id v]
|
||||
(:value (first (filter #(= (:key %) id) v))))
|
||||
|
@ -199,6 +219,7 @@
|
|||
(case (:type desc)
|
||||
:boolean [customizer-boolean descriptor]
|
||||
:text [customizer-text descriptor]
|
||||
:number [customizer-number descriptor]
|
||||
:select [customizer-select descriptor]
|
||||
nil)]))])
|
||||
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
(ns utils.money
|
||||
(:require ["bignumber.js" :as BigNumber]
|
||||
[clojure.string :as string]
|
||||
[utils.i18n :as i18n]))
|
||||
|
||||
;; The BigNumber version included in web3 sometimes hangs when dividing large
|
||||
;; numbers Hence we want to use these functions instead of fromWei etc, which
|
||||
;; come bundled with web3. See
|
||||
;; https://github.com/MikeMcl/bignumber.js/issues/120 for this regression being
|
||||
;; introduced in some JS environments. It is fixed in the MikeMcl/bignumber.js
|
||||
;; repo, but not in the web3 BigNumber fork:
|
||||
;; https://github.com/ethereum/web3.js/issues/877
|
||||
;;
|
||||
;; Additionally, while it is possible to use the BigNumber constructor without
|
||||
;; stringifying the number, this only works up to some 15 significant digits:
|
||||
;; https://github.com/MikeMcl/bignumber.js/issues/120
|
||||
;;
|
||||
;; Lastly, notice the bad rounding for native Javascript numbers above 17 digits
|
||||
;; that may result in errors earlier up the call chain. Ideally all money-related
|
||||
;; sensitive functions should be moved into this namespace to check for such
|
||||
;; matters:
|
||||
;; (str 111122223333441239) => "111122223333441230"
|
||||
|
||||
(defn normalize
|
||||
"A normalized string representation of an amount"
|
||||
[s]
|
||||
{:pre [(or (nil? s) (string? s))]}
|
||||
(when s
|
||||
(string/replace (string/trim s) #"," ".")))
|
||||
|
||||
(defn bignumber
|
||||
[n]
|
||||
(when n
|
||||
(try
|
||||
(new BigNumber (normalize (str n)))
|
||||
(catch :default _ nil))))
|
||||
|
||||
(defn with-precision
|
||||
[n decimals]
|
||||
(when-let [^js bn (bignumber n)]
|
||||
(.round bn decimals)))
|
||||
|
||||
(defn format-amount
|
||||
"Format `amount` to thousands or millions. Return nil if `amount` is not truthy."
|
||||
[amount]
|
||||
(when amount
|
||||
(cond
|
||||
(> amount 999999)
|
||||
(str (with-precision (/ amount 1000000) 1) (i18n/label :t/M))
|
||||
|
||||
(< 999 amount 1000000)
|
||||
(str (with-precision (/ amount 1000) 1) (i18n/label :t/K))
|
||||
|
||||
:else
|
||||
(str amount))))
|
|
@ -0,0 +1,29 @@
|
|||
(ns utils.money-test
|
||||
(:require [cljs.test :refer-macros [deftest is are]]
|
||||
[utils.money :as money]))
|
||||
|
||||
(deftest normalize
|
||||
(is (= nil (money/normalize nil)))
|
||||
(is (= "1" (money/normalize " 1 ")))
|
||||
(is (= "1.1" (money/normalize "1.1")))
|
||||
(is (= "1.1" (money/normalize "1,1"))))
|
||||
|
||||
(deftest format-amount
|
||||
(are [amount expected]
|
||||
(= expected (money/format-amount amount))
|
||||
nil nil
|
||||
0 "0"
|
||||
1 "1"
|
||||
10 "10"
|
||||
100 "100"
|
||||
999 "999"
|
||||
1000 "1K"
|
||||
1001 "1K"
|
||||
10000 "10K"
|
||||
100000 "100K"
|
||||
999999 "1000K"
|
||||
1000000 "1M"
|
||||
1000000 "1M"
|
||||
1000001 "1M"
|
||||
10000000 "10M"
|
||||
100000000 "100M"))
|
Loading…
Reference in New Issue