mirror of
https://github.com/status-im/status-mobile.git
synced 2025-02-26 15:21:08 +00:00
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-messages?
|
||||||
[unread-grey-dot :unviewed-messages-public]))
|
[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
|
(defn communities-membership-list-item
|
||||||
[props
|
[props
|
||||||
{:keys [name
|
{:keys [name
|
||||||
|
@ -5,13 +5,17 @@
|
|||||||
[quo2.components.tags.permission-tag :as permission]
|
[quo2.components.tags.permission-tag :as permission]
|
||||||
[quo2.components.tags.tag :as tag]
|
[quo2.components.tags.tag :as tag]
|
||||||
[quo2.foundations.colors :as colors]
|
[quo2.foundations.colors :as colors]
|
||||||
[quo.gesture-handler :as gesture-handler] ;;TODO move to quo2
|
[quo2.theme :as theme]
|
||||||
[react-native.core :as rn]))
|
[react-native.core :as rn]
|
||||||
|
[react-native.gesture :as gesture]
|
||||||
|
utils.money))
|
||||||
|
|
||||||
(defn community-stats
|
(defn community-stats
|
||||||
[{:keys [icon members-count icon-color]}]
|
[{:keys [icon members-count icon-color accessibility-label]}]
|
||||||
[rn/view (style/stats-count-container)
|
[rn/view
|
||||||
[rn/view {:margin-right 4}
|
{:accessibility-label accessibility-label
|
||||||
|
:style (style/stats-count-container)}
|
||||||
|
[rn/view {:margin-right 2}
|
||||||
[icons/icon icon
|
[icons/icon icon
|
||||||
{:container-style {:align-items :center
|
{:container-style {:align-items :center
|
||||||
:justify-content :center}
|
:justify-content :center}
|
||||||
@ -20,29 +24,32 @@
|
|||||||
:color icon-color}]]
|
:color icon-color}]]
|
||||||
[text/text
|
[text/text
|
||||||
{:weight :regular
|
{:weight :regular
|
||||||
:size :paragraph-1}
|
:size :paragraph-2}
|
||||||
members-count]])
|
members-count]])
|
||||||
|
|
||||||
(defn community-stats-column
|
(defn community-stats-column
|
||||||
[{:keys [type]}]
|
[{:keys [type theme blur? members-count active-count]}]
|
||||||
(let [icon-color (colors/theme-colors colors/neutral-50 colors/neutral-40)]
|
(let [icon-color (if (and (= :dark theme) blur?)
|
||||||
|
colors/white-opa-40
|
||||||
|
(colors/theme-colors colors/neutral-50 colors/neutral-40 theme))]
|
||||||
[rn/view
|
[rn/view
|
||||||
(if (= type :card-view)
|
(if (= type :card-view)
|
||||||
(style/card-stats-container)
|
(style/card-stats-container)
|
||||||
(style/list-stats-container))
|
(style/list-stats-container))
|
||||||
[community-stats
|
[community-stats
|
||||||
{:icon :i/group
|
{:accessibility-label :stats-members-count
|
||||||
:members-count "629.2K" ;;TODO here should be formatted value, use money/format-members from
|
:icon :i/group
|
||||||
;;outside this component
|
:members-count (utils.money/format-amount members-count)
|
||||||
:icon-color icon-color}]
|
:icon-color icon-color}]
|
||||||
[community-stats
|
[community-stats
|
||||||
{:icon :i/lightning
|
{:accessibility-label :stats-active-count
|
||||||
:members-count "112.1K"
|
:icon :i/active-members
|
||||||
:icon-color icon-color}]]))
|
:members-count (utils.money/format-amount active-count)
|
||||||
|
:icon-color icon-color}]]))
|
||||||
|
|
||||||
(defn community-tags
|
(defn community-tags
|
||||||
[{:keys [tags container-style last-item-style]}]
|
[{:keys [tags container-style last-item-style]}]
|
||||||
[gesture-handler/scroll-view
|
[gesture/scroll-view
|
||||||
{:shows-horizontal-scroll-indicator false
|
{:shows-horizontal-scroll-indicator false
|
||||||
:horizontal true
|
:horizontal true
|
||||||
:style container-style}
|
:style container-style}
|
||||||
@ -84,13 +91,16 @@
|
|||||||
:style {:margin-top (if (= size :large) 8 2)}}
|
:style {:margin-top (if (= size :large) 8 2)}}
|
||||||
description])])
|
description])])
|
||||||
|
|
||||||
(defn permission-tag-container
|
(defn- permission-tag-container-internal
|
||||||
[{:keys [locked? tokens on-press]}]
|
[{:keys [locked? blur? tokens on-press theme]}]
|
||||||
[permission/tag
|
[permission/tag
|
||||||
{:background-color (colors/theme-colors
|
{:accessibility-label :permission-tag
|
||||||
colors/neutral-10
|
:background-color (if (and (= :dark theme) blur?)
|
||||||
colors/neutral-80)
|
colors/white-opa-10
|
||||||
:locked? locked?
|
(colors/theme-colors colors/neutral-10 colors/neutral-80 theme))
|
||||||
:tokens tokens
|
:locked? locked?
|
||||||
:size 24
|
:tokens tokens
|
||||||
:on-press on-press}])
|
:size 24
|
||||||
|
:on-press on-press}])
|
||||||
|
|
||||||
|
(def permission-tag-container (theme/with-theme permission-tag-container-internal))
|
||||||
|
70
src/quo2/components/list_items/community/component_spec.cljs
Normal file
70
src/quo2/components/list_items/community/component_spec.cljs
Normal file
@ -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)))))
|
65
src/quo2/components/list_items/community/style.cljs
Normal file
65
src/quo2/components/list_items/community/style.cljs
Normal file
@ -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))))
|
165
src/quo2/components/list_items/community/view.cljs
Normal file
165
src/quo2/components/list_items/community/view.cljs
Normal file
@ -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
|
(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}}]
|
:or {size 24}}]
|
||||||
[base-tag/base-tag
|
[base-tag/base-tag
|
||||||
{:background-color background-color
|
{:accessibility-label accessibility-label
|
||||||
:size size
|
:background-color background-color
|
||||||
:type :permission
|
:size size
|
||||||
:on-press on-press}
|
:type :permission
|
||||||
|
:on-press on-press}
|
||||||
[rn/view
|
[rn/view
|
||||||
{:flex-direction :row
|
{:flex-direction :row
|
||||||
:align-items :center
|
:align-items :center
|
||||||
|
@ -48,6 +48,7 @@
|
|||||||
quo2.components.links.url-preview-list.view
|
quo2.components.links.url-preview-list.view
|
||||||
quo2.components.links.url-preview.view
|
quo2.components.links.url-preview.view
|
||||||
quo2.components.list-items.channel
|
quo2.components.list-items.channel
|
||||||
|
quo2.components.list-items.community.view
|
||||||
quo2.components.list-items.menu-item
|
quo2.components.list-items.menu-item
|
||||||
quo2.components.list-items.preview-list
|
quo2.components.list-items.preview-list
|
||||||
quo2.components.list-items.user-list
|
quo2.components.list-items.user-list
|
||||||
@ -149,7 +150,6 @@
|
|||||||
|
|
||||||
;;;; COMMUNITY
|
;;;; COMMUNITY
|
||||||
(def community-card-view-item quo2.components.community.community-card-view/community-card-view-item)
|
(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
|
(def communities-membership-list-item
|
||||||
quo2.components.community.community-list-view/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)
|
(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 menu-item quo2.components.list-items.menu-item/menu-item)
|
||||||
(def preview-list quo2.components.list-items.preview-list/preview-list)
|
(def preview-list quo2.components.list-items.preview-list/preview-list)
|
||||||
(def user-list quo2.components.list-items.user-list/user-list)
|
(def user-list quo2.components.list-items.user-list/user-list)
|
||||||
|
(def community-list-item quo2.components.list-items.community.view/view)
|
||||||
|
|
||||||
;;;; LOADERS
|
;;;; LOADERS
|
||||||
(def skeleton quo2.components.loaders.skeleton/skeleton)
|
(def skeleton quo2.components.loaders.skeleton/skeleton)
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
[quo2.components.links.link-preview.component-spec]
|
[quo2.components.links.link-preview.component-spec]
|
||||||
[quo2.components.links.url-preview-list.component-spec]
|
[quo2.components.links.url-preview-list.component-spec]
|
||||||
[quo2.components.links.url-preview.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.--tests--.text-component-spec]
|
||||||
[quo2.components.markdown.list.component-spec]
|
[quo2.components.markdown.list.component-spec]
|
||||||
[quo2.components.notifications.notification.component-spec]
|
[quo2.components.notifications.notification.component-spec]
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
(def text (reagent/adapt-react-class (.-Text ^js react-native)))
|
(def text (reagent/adapt-react-class (.-Text ^js react-native)))
|
||||||
(def text-input (reagent/adapt-react-class (.-TextInput ^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-opacity (reagent/adapt-react-class (.-TouchableOpacity ^js react-native)))
|
||||||
(def touchable-highlight (reagent/adapt-react-class (.-TouchableHighlight ^js react-native)))
|
(def touchable-highlight (reagent/adapt-react-class (.-TouchableHighlight ^js react-native)))
|
||||||
(def touchable-without-feedback
|
(def touchable-without-feedback
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
(ns status-im.utils.money
|
(ns status-im.utils.money
|
||||||
(:require ["bignumber.js" :as BigNumber]
|
(:require ["bignumber.js" :as BigNumber]
|
||||||
[clojure.string :as string]
|
[clojure.string :as string]))
|
||||||
[utils.i18n :as i18n]))
|
|
||||||
|
|
||||||
;; The BigNumber version included in web3 sometimes hangs when dividing large
|
;; The BigNumber version included in web3 sometimes hangs when dividing large
|
||||||
;; numbers Hence we want to use these functions instead of fromWei etc, which
|
;; numbers Hence we want to use these functions instead of fromWei etc, which
|
||||||
@ -222,11 +221,3 @@
|
|||||||
(defn div-and-round
|
(defn div-and-round
|
||||||
[bn1 bn2]
|
[bn1 bn2]
|
||||||
(.round (.dividedBy ^js bn1 bn2) 0))
|
(.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)
|
(if (= view-type :card-view)
|
||||||
[quo/community-card-view-item (assoc item :width width :cover cover)
|
[quo/community-card-view-item (assoc item :width width :cover cover)
|
||||||
#(rf/dispatch [:navigate-to :community-overview (:id item)])]
|
#(rf/dispatch [:navigate-to :community-overview (:id item)])]
|
||||||
[quo/communities-list-view-item
|
[quo/community-list-item
|
||||||
{:on-press (fn []
|
{:on-press (fn []
|
||||||
(rf/dispatch [:communities/load-category-states (:id item)])
|
(rf/dispatch [:communities/load-category-states (:id item)])
|
||||||
(rf/dispatch [:dismiss-keyboard])
|
(rf/dispatch [:dismiss-keyboard])
|
||||||
@ -142,7 +142,7 @@
|
|||||||
(get mock-community-item-data :data)
|
(get mock-community-item-data :data)
|
||||||
{:cover cover})
|
{:cover cover})
|
||||||
#(rf/dispatch [:navigate-to :community-overview (:id community)])]
|
#(rf/dispatch [:navigate-to :community-overview (:id community)])]
|
||||||
[quo/communities-list-view-item
|
[quo/community-list-item
|
||||||
{:on-press (fn []
|
{:on-press (fn []
|
||||||
(rf/dispatch [:communities/load-category-states (:id community)])
|
(rf/dispatch [:communities/load-category-states (:id community)])
|
||||||
(rf/dispatch [:dismiss-keyboard])
|
(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.code.snippet :as code-snippet]
|
||||||
[status-im2.contexts.quo-preview.colors.color-picker :as color-picker]
|
[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-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
|
[status-im2.contexts.quo-preview.community.community-membership-list-view :as
|
||||||
community-membership-list-view]
|
community-membership-list-view]
|
||||||
[status-im2.contexts.quo-preview.community.discover-card :as discover-card]
|
[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.channel :as channel]
|
||||||
[status-im2.contexts.quo-preview.list-items.preview-lists :as preview-lists]
|
[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.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.text :as text]
|
||||||
[status-im2.contexts.quo-preview.markdown.list :as markdown-list]
|
[status-im2.contexts.quo-preview.markdown.list :as markdown-list]
|
||||||
[status-im2.contexts.quo-preview.messages.author :as messages-author]
|
[status-im2.contexts.quo-preview.messages.author :as messages-author]
|
||||||
@ -148,9 +148,6 @@
|
|||||||
:community [{:name :community-card-view
|
:community [{:name :community-card-view
|
||||||
:options {:topBar {:visible true}}
|
:options {:topBar {:visible true}}
|
||||||
:component community-card/preview-community-card}
|
: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
|
{:name :community-membership-list-view
|
||||||
:options {:topBar {:visible true}}
|
:options {:topBar {:visible true}}
|
||||||
:component community-membership-list-view/preview-community-list-view}
|
:component community-membership-list-view/preview-community-list-view}
|
||||||
@ -237,6 +234,10 @@
|
|||||||
:list-items [{:name :channel
|
:list-items [{:name :channel
|
||||||
:options {:topBar {:visible true}}
|
:options {:topBar {:visible true}}
|
||||||
:component channel/preview-channel}
|
:component channel/preview-channel}
|
||||||
|
{:name :community-list
|
||||||
|
:options {:insets {:top? true}
|
||||||
|
:topBar {:visible true}}
|
||||||
|
:component community-list/preview}
|
||||||
{:name :preview-lists
|
{:name :preview-lists
|
||||||
:options {:topBar {:visible true}}
|
:options {:topBar {:visible true}}
|
||||||
:component preview-lists/preview-preview-lists}
|
:component preview-lists/preview-preview-lists}
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
(ns status-im2.contexts.quo-preview.preview
|
(ns status-im2.contexts.quo-preview.preview
|
||||||
(:require [clojure.string :as string]
|
(:require [clojure.string :as string]
|
||||||
[quo2.foundations.colors :as colors]
|
[quo2.foundations.colors :as colors]
|
||||||
|
[quo2.theme :as theme]
|
||||||
[react-native.blur :as blur]
|
[react-native.blur :as blur]
|
||||||
[react-native.core :as rn]
|
[react-native.core :as rn]
|
||||||
[reagent.core :as reagent]
|
[reagent.core :as reagent]
|
||||||
[status-im2.common.resources :as resources]
|
[status-im2.common.resources :as resources]
|
||||||
[quo2.theme :as theme])
|
utils.number)
|
||||||
(:require-macros status-im2.contexts.quo-preview.preview))
|
(:require-macros status-im2.contexts.quo-preview.preview))
|
||||||
|
|
||||||
(def container
|
(def container
|
||||||
@ -122,6 +123,25 @@
|
|||||||
(when limit
|
(when limit
|
||||||
{:max-length 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
|
(defn value-for-key
|
||||||
[id v]
|
[id v]
|
||||||
(:value (first (filter #(= (:key %) id) v))))
|
(:value (first (filter #(= (:key %) id) v))))
|
||||||
@ -199,6 +219,7 @@
|
|||||||
(case (:type desc)
|
(case (:type desc)
|
||||||
:boolean [customizer-boolean descriptor]
|
:boolean [customizer-boolean descriptor]
|
||||||
:text [customizer-text descriptor]
|
:text [customizer-text descriptor]
|
||||||
|
:number [customizer-number descriptor]
|
||||||
:select [customizer-select descriptor]
|
:select [customizer-select descriptor]
|
||||||
nil)]))])
|
nil)]))])
|
||||||
|
|
||||||
|
55
src/utils/money.cljs
Normal file
55
src/utils/money.cljs
Normal file
@ -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))))
|
29
src/utils/money_test.cljs
Normal file
29
src/utils/money_test.cljs
Normal file
@ -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…
x
Reference in New Issue
Block a user