From 6170686e34ff645737c39e60916900aa67a64331 Mon Sep 17 00:00:00 2001 From: Icaro Motta Date: Wed, 12 Jul 2023 11:14:24 +0000 Subject: [PATCH] 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. --- .../community/community_list_view.cljs | 46 ----- .../components/community/community_view.cljs | 60 ++++--- .../list_items/community/component_spec.cljs | 70 ++++++++ .../list_items/community/style.cljs | 65 +++++++ .../components/list_items/community/view.cljs | 165 ++++++++++++++++++ src/quo2/components/tags/permission_tag.cljs | 11 +- src/quo2/core.cljs | 3 +- src/quo2/core_spec.cljs | 1 + src/react_native/core.cljs | 1 + src/status_im/utils/money.cljs | 11 +- .../contexts/communities/discover/view.cljs | 4 +- .../community/community_list_view.cljs | 69 -------- .../list_items/community_list.cljs | 132 ++++++++++++++ src/status_im2/contexts/quo_preview/main.cljs | 9 +- .../contexts/quo_preview/preview.cljs | 23 ++- src/utils/money.cljs | 55 ++++++ src/utils/money_test.cljs | 29 +++ 17 files changed, 591 insertions(+), 163 deletions(-) create mode 100644 src/quo2/components/list_items/community/component_spec.cljs create mode 100644 src/quo2/components/list_items/community/style.cljs create mode 100644 src/quo2/components/list_items/community/view.cljs delete mode 100644 src/status_im2/contexts/quo_preview/community/community_list_view.cljs create mode 100644 src/status_im2/contexts/quo_preview/list_items/community_list.cljs create mode 100644 src/utils/money.cljs create mode 100644 src/utils/money_test.cljs diff --git a/src/quo2/components/community/community_list_view.cljs b/src/quo2/components/community/community_list_view.cljs index e6045f17e3..dc988ebdeb 100644 --- a/src/quo2/components/community/community_list_view.cljs +++ b/src/quo2/components/community/community_list_view.cljs @@ -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 diff --git a/src/quo2/components/community/community_view.cljs b/src/quo2/components/community/community_view.cljs index cb015c013c..6f2286b761 100644 --- a/src/quo2/components/community/community_view.cljs +++ b/src/quo2/components/community/community_view.cljs @@ -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)) diff --git a/src/quo2/components/list_items/community/component_spec.cljs b/src/quo2/components/list_items/community/component_spec.cljs new file mode 100644 index 0000000000..4d2d1d2210 --- /dev/null +++ b/src/quo2/components/list_items/community/component_spec.cljs @@ -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))))) diff --git a/src/quo2/components/list_items/community/style.cljs b/src/quo2/components/list_items/community/style.cljs new file mode 100644 index 0000000000..77b97ced94 --- /dev/null +++ b/src/quo2/components/list_items/community/style.cljs @@ -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)))) diff --git a/src/quo2/components/list_items/community/view.cljs b/src/quo2/components/list_items/community/view.cljs new file mode 100644 index 0000000000..18c2aa1ca7 --- /dev/null +++ b/src/quo2/components/list_items/community/view.cljs @@ -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)) diff --git a/src/quo2/components/tags/permission_tag.cljs b/src/quo2/components/tags/permission_tag.cljs index 6be49d7a06..b8df7df0f1 100644 --- a/src/quo2/components/tags/permission_tag.cljs +++ b/src/quo2/components/tags/permission_tag.cljs @@ -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 diff --git a/src/quo2/core.cljs b/src/quo2/core.cljs index 2358888711..cb61296d72 100644 --- a/src/quo2/core.cljs +++ b/src/quo2/core.cljs @@ -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) diff --git a/src/quo2/core_spec.cljs b/src/quo2/core_spec.cljs index c865df9a95..ed9b67fa8c 100644 --- a/src/quo2/core_spec.cljs +++ b/src/quo2/core_spec.cljs @@ -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] diff --git a/src/react_native/core.cljs b/src/react_native/core.cljs index 725a9f4dea..a7d6677e0b 100644 --- a/src/react_native/core.cljs +++ b/src/react_native/core.cljs @@ -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 diff --git a/src/status_im/utils/money.cljs b/src/status_im/utils/money.cljs index eafcee2e5c..63d75f34f7 100644 --- a/src/status_im/utils/money.cljs +++ b/src/status_im/utils/money.cljs @@ -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))) diff --git a/src/status_im2/contexts/communities/discover/view.cljs b/src/status_im2/contexts/communities/discover/view.cljs index d1f197ae97..b51f2415fd 100644 --- a/src/status_im2/contexts/communities/discover/view.cljs +++ b/src/status_im2/contexts/communities/discover/view.cljs @@ -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]) diff --git a/src/status_im2/contexts/quo_preview/community/community_list_view.cljs b/src/status_im2/contexts/quo_preview/community/community_list_view.cljs deleted file mode 100644 index a840c986ea..0000000000 --- a/src/status_im2/contexts/quo_preview/community/community_list_view.cljs +++ /dev/null @@ -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}]]) diff --git a/src/status_im2/contexts/quo_preview/list_items/community_list.cljs b/src/status_im2/contexts/quo_preview/list_items/community_list.cljs new file mode 100644 index 0000000000..f29b7b8c10 --- /dev/null +++ b/src/status_im2/contexts/quo_preview/list_items/community_list.cljs @@ -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}]]) diff --git a/src/status_im2/contexts/quo_preview/main.cljs b/src/status_im2/contexts/quo_preview/main.cljs index 09a38cfc77..facd81d7c0 100644 --- a/src/status_im2/contexts/quo_preview/main.cljs +++ b/src/status_im2/contexts/quo_preview/main.cljs @@ -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} diff --git a/src/status_im2/contexts/quo_preview/preview.cljs b/src/status_im2/contexts/quo_preview/preview.cljs index 1ea5c99b1a..5bd40231a2 100644 --- a/src/status_im2/contexts/quo_preview/preview.cljs +++ b/src/status_im2/contexts/quo_preview/preview.cljs @@ -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)]))]) diff --git a/src/utils/money.cljs b/src/utils/money.cljs new file mode 100644 index 0000000000..a7d671cf80 --- /dev/null +++ b/src/utils/money.cljs @@ -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)))) diff --git a/src/utils/money_test.cljs b/src/utils/money_test.cljs new file mode 100644 index 0000000000..a5820f6bfb --- /dev/null +++ b/src/utils/money_test.cljs @@ -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"))