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:
Icaro Motta 2023-07-12 11:14:24 +00:00 committed by GitHub
parent 72e56dcf75
commit 6170686e34
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 591 additions and 163 deletions

View File

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

View File

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

View 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)))))

View 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))))

View 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))

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

55
src/utils/money.cljs Normal file
View 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
View 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"))