Support hooks & atom state management in previews (#20053)

Change the implementation of component previews to support receiving either
state/set-state from use-state or a Reagent atom, thus allowing us to gradually
change preview namespaces to use hooks instead of having to refactor all at once
in a gigantic PR.

All types of preview fields were tested, including multi-select.

Only the components counter.step and selectors.react previews were adapted
to use-state.
This commit is contained in:
Icaro Motta 2024-05-17 16:48:57 -03:00 committed by GitHub
parent 624593ec35
commit ea58e52dc1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 172 additions and 123 deletions

View File

@ -1,7 +1,7 @@
(ns status-im.contexts.preview.quo.counter.step
(:require
[quo.core :as quo]
[reagent.core :as reagent]
[react-native.core :as rn]
[status-im.contexts.preview.quo.preview :as preview]))
(def descriptor
@ -17,13 +17,13 @@
(defn view
[]
(let [state (reagent/atom {:value "5"
:type :neutral
:in-blur-view? false})]
(fn []
[preview/preview-container
{:state state
:descriptor descriptor
:blur? (:in-blur-view? @state)
:show-blur-background? (:in-blur-view? @state)}
[quo/step (dissoc @state :value) (:value @state)]])))
(let [[state set-state] (rn/use-state {:value "5"
:type :neutral
:in-blur-view? false})]
[preview/preview-container
{:state state
:set-state set-state
:descriptor descriptor
:blur? (:in-blur-view? state)
:show-blur-background? (:in-blur-view? state)}
[quo/step (dissoc state :value) (:value state)]]))

View File

@ -46,70 +46,94 @@
(str (humanize k) ":"))
(defn- customizer-boolean
[{:keys [label state] :as args}]
(let [theme (quo.theme/use-theme)
label (or label (key->boolean-label (:key args)))
field-value (reagent/cursor state [(:key args)])
active? @field-value]
[{:keys [label state set-state] :as args}]
(let [theme (quo.theme/use-theme)
label (or label (key->boolean-label (:key args)))
field-cursor (when-not (fn? set-state)
(reagent/cursor state [(:key args)]))
field-value (if (fn? set-state)
(get state (:key args))
@field-cursor)
set-field-value (fn [value]
(if (fn? set-state)
(set-state (assoc state (:key args) value))
(reset! field-cursor value)))]
[rn/view {:style style/field-row}
[label-view state label theme]
[rn/view {:style (style/boolean-container)}
[rn/pressable
{:style (style/boolean-button {:active? active? :left? true} theme)
:on-press #(reset! field-value true)}
[rn/text {:style (style/field-text active? theme)}
{:style (style/boolean-button {:active? field-value :left? true} theme)
:on-press #(set-field-value true)}
[rn/text {:style (style/field-text field-value theme)}
"True"]]
[rn/pressable
{:style (style/boolean-button {:active? (not active?) :left? false} theme)
:on-press #(reset! field-value false)}
[rn/text {:style (style/field-text (not active?) theme)}
{:style (style/boolean-button {:active? (not field-value) :left? false} theme)
:on-press #(set-field-value false)}
[rn/text {:style (style/field-text (not field-value) theme)}
"False"]]]]))
(defn- customizer-text
[{:keys [label state limit suffix] :as args} theme]
(let [label (or label (key->text-label (:key args)))
field-value (reagent/cursor state [(:key args)])]
[{:keys [label state set-state limit suffix] :as args} theme]
(let [label (or label (key->text-label (:key args)))
field-cursor (when-not (fn? set-state)
(reagent/cursor state [(:key args)]))
field-value (if (fn? set-state)
(get state (:key args))
@field-cursor)
set-field-value (fn [value]
(if (fn? set-state)
(set-state (assoc state (:key args) value))
(reset! field-cursor value)))]
[rn/view {:style style/field-row}
[label-view state label theme]
[rn/view {:style style/field-column}
[rn/text-input
(merge
{:value @field-value
{:value field-value
:show-cancel false
:style (style/field-container false theme)
:keyboard-appearance theme
:on-change-text (fn [text]
(reset! field-value (if (and suffix
(> (count text) (count @field-value)))
(str (string/replace text suffix "") suffix)
text))
(reagent/flush))}
(set-field-value (if (and suffix
(> (count text) (count field-value)))
(str (string/replace text suffix "") suffix)
text))
(when-not (fn? set-state)
(reagent/flush)))}
(when limit
{:max-length limit}))]]]))
(defn- customizer-number
[{:keys [label state default] :as args} theme]
(let [label (or label (key->text-label (:key args)))
field-value (reagent/cursor state [(:key args)])]
[{:keys [label state set-state default] :as args} theme]
(let [label (or label (key->text-label (:key args)))
field-cursor (when-not (fn? set-state)
(reagent/cursor state [(:key args)]))
field-value (if (fn? set-state)
(get state (:key args))
@field-cursor)
set-field-value (fn [value]
(if (fn? set-state)
(set-state (assoc state (:key args) value))
(reset! field-cursor value)))]
[rn/view {:style style/field-row}
[label-view state label theme]
[rn/view {:style style/field-column}
[rn/text-input
(merge
{:value (str @field-value)
:show-cancel false
:style (style/field-container false theme)
:keyboard-appearance theme
:on-change-text (fn [text]
(reset! field-value (utils.number/parse-int text default))
(reagent/flush))})]]]))
{:value (str field-value)
:show-cancel false
:style (style/field-container false theme)
:keyboard-appearance theme
:on-change-text (fn [text]
(set-field-value (utils.number/parse-int text default))
(when-not (fn? set-state)
(reagent/flush)))}]]]))
(defn- find-selected-option
[id v]
(first (filter #(= (:key %) id) v)))
(defn- customizer-select-modal
[{:keys [open options field-value]}]
[{:keys [open options field-value set-field-value]}]
(let [theme (quo.theme/use-theme)]
[rn/modal
{:visible @open
@ -125,17 +149,17 @@
:let [v (or v (humanize k))]]
^{:key k}
[rn/pressable
{:style (style/select-option (= @field-value k) theme)
{:style (style/select-option (= field-value k) theme)
:on-press (fn []
(reset! open false)
(reset! field-value k))}
[rn/text {:style (style/field-text (= @field-value k) theme)}
(set-field-value k))}
[rn/text {:style (style/field-text (= field-value k) theme)}
v]]))]
[rn/view {:style (style/footer theme)}
[rn/pressable
{:style (style/select-button theme)
:on-press (fn []
(reset! field-value nil)
(set-field-value nil)
(reset! open false))}
[rn/text {:style (style/field-text false theme)}
"Clear"]]
@ -163,22 +187,31 @@
(defn- customizer-select
[]
(let [open (reagent/atom nil)]
(fn [{:keys [label state options] :as args}]
(fn [{:keys [label state set-state options] :as args}]
(let [theme (quo.theme/use-theme)
label (or label (key->text-label (:key args)))
field-value (reagent/cursor state [(:key args)])
selected-option (find-selected-option @field-value options)]
field-cursor (when-not (fn? set-state)
(reagent/cursor state [(:key args)]))
field-value (if (fn? set-state)
(get state (:key args))
@field-cursor)
set-field-value (fn [value]
(if (fn? set-state)
(set-state (assoc state (:key args) value))
(reset! field-cursor value)))
selected-option (find-selected-option field-value options)]
[rn/view {:style style/field-row}
[label-view state label theme]
[rn/view {:style style/field-column}
[customizer-select-modal
{:open open
:options options
:field-value field-value}]
{:open open
:options options
:field-value field-value
:set-field-value set-field-value}]
[customizer-select-button {:open open :selected-option selected-option} theme]]]))))
(defn- customizer-multi-select-modal
[{:keys [open-atom options selected-keys-atom]}]
[{:keys [open-atom options field-value set-field-value]}]
(let [theme (quo.theme/use-theme)]
[rn/modal
{:visible @open-atom
@ -193,13 +226,13 @@
(for [{k :key v :value} options
:let [v (or v (humanize k))]]
^{:key k}
(let [checked? (boolean (some #(= k %) @selected-keys-atom))
(let [checked? (boolean (some #(= k %) field-value))
remove-key (fn [v] (filterv #(not= % k) v))
on-press (fn []
(swap! selected-keys-atom
(if checked? remove-key conj)
k))]
(set-field-value
(if checked?
(remove-key field-value)
(conj field-value k))))]
[rn/pressable
{:style (style/multi-select-option theme)
:on-press on-press}
@ -212,7 +245,7 @@
[rn/pressable
{:style (style/select-button theme)
:on-press (fn []
(reset! selected-keys-atom nil)
(set-field-value nil)
(reset! open-atom false))}
[rn/text {:style (style/field-text false theme)}
"Clear"]]
@ -246,41 +279,51 @@
(defn- customizer-multi-select
[]
(let [open (reagent/atom nil)]
(fn [{:keys [label state options] :as args}]
(fn [{:keys [label state set-state options] :as args}]
(let [theme (quo.theme/use-theme)
label (or label (key->text-label (:key args)))
selected-keys (reagent/cursor state [(:key args)])
selected-options (filter-by-keys options @selected-keys)]
field-cursor (when-not (fn? set-state)
(reagent/cursor state [(:key args)]))
field-value (if (fn? set-state)
(get state (:key args))
@field-cursor)
set-field-value (fn [value]
(if (fn? set-state)
(set-state (assoc state (:key args) value))
(reset! field-cursor value)))
selected-options (filter-by-keys options field-value)]
[rn/view {:style style/field-row}
[label-view state label theme]
[rn/view {:style style/field-column}
[customizer-multi-select-modal
{:open-atom open
:selected-keys-atom selected-keys
:options options}]
{:open-atom open
:field-value field-value
:set-field-value set-field-value
:options options}]
[customizer-multi-select-button {:open open :selected-options selected-options} theme]]]))))
(defn customizer
[state descriptors theme]
[state set-state descriptors theme]
[rn/view
{:style {:flex-shrink 1
:padding-horizontal 20}}
(doall
(for [desc descriptors
:let [desc-path (:path desc)
new-state (if desc-path
(reagent/cursor state desc-path)
state)
descriptor (assoc desc :state new-state)]]
descriptor (if (fn? set-state)
(assoc desc :state state :set-state set-state)
(let [new-state (if desc-path
(reagent/cursor state desc-path)
state)]
(assoc desc :state new-state)))]]
^{:key (:key desc)}
[:<>
(case (:type desc)
:boolean [customizer-boolean descriptor]
:text [customizer-text descriptor theme]
:number [customizer-number descriptor theme]
:select [customizer-select descriptor]
:multi-select [customizer-multi-select descriptor]
nil)]))])
(case (:type desc)
:boolean [customizer-boolean descriptor]
:text [customizer-text descriptor theme]
:number [customizer-number descriptor theme]
:select [customizer-select descriptor]
:multi-select [customizer-multi-select descriptor]
nil)))])
(defn customization-color-option
([]
@ -325,7 +368,7 @@
children)])
(defn- f-preview-container
[{:keys [title state descriptor blur? blur-dark-only?
[{:keys [title state set-state descriptor blur? blur-dark-only?
component-container-style
blur-container-style blur-view-props blur-height show-blur-background? full-screen?]
:or {blur-height 200}}
@ -352,7 +395,7 @@
:on-press rn/dismiss-keyboard!}
(when descriptor
[rn/view {:style style/customizer-container}
[customizer state descriptor theme]])
[customizer state set-state descriptor theme]])
(if blur?
[rn/view {:style (merge style/component-container component-container-style)}
(into [blur-view
@ -371,8 +414,11 @@
(into [rn/view {:style (merge style/component-container component-container-style)}]
children))]
(when state
(let [decr-state (if descriptor (select-keys @state (mapv :key (flatten descriptor))) @state)
state-str (with-out-str (cljs.pprint/pprint decr-state))]
(let [actual-state (if (fn? set-state) state @state)
decr-state (if descriptor
(select-keys actual-state (mapv :key (flatten descriptor)))
actual-state)
state-str (with-out-str (cljs.pprint/pprint decr-state))]
[rn/view {:style {:margin 50}}
[quo/text {:style {:margin-bottom 10}} "State map (click on map to copy)"]
[rn/pressable

View File

@ -4,17 +4,16 @@
[quo.core :as quo]
[quo.foundations.colors :as colors]
[react-native.core :as rn]
[reagent.core :as reagent]
[status-im.constants :as constants]
[status-im.contexts.preview.quo.preview :as preview]))
(defn- gen-quantity
(defn gen-quantity
[max-count _]
(rand-int max-count))
(def ^:private memo-gen-quantity (memoize gen-quantity))
(def memo-gen-quantity (memoize gen-quantity))
(def ^:private descriptor
(def descriptor
[{:key :hide-new-reaction-button?
:type :boolean}
{:label "Reactions"
@ -37,40 +36,44 @@
(defn preview-react
[]
(let [state (reagent/atom {:hide-new-reaction-button? true
:max-count 1000
:reaction-ids [1 2 3]
:use-case :default})
pressed-reactions (reagent/atom #{1})]
(let [[state set-state]
(rn/use-state {:hide-new-reaction-button? true
:max-count 1000
:reaction-ids [1 2 3]
:use-case :default})
(fn []
(let [reactions (mapv (fn [reaction-id]
{:emoji-reaction-id reaction-id
:emoji-id reaction-id
:emoji (get constants/reactions reaction-id)
:quantity (memo-gen-quantity (:max-count @state) reaction-id)
:own (contains? @pressed-reactions reaction-id)})
(:reaction-ids @state))]
[preview/preview-container
{:state state
:descriptor descriptor}
[rn/view
{:padding-bottom 150
:padding-vertical 60
:padding-horizontal 20
:border-radius 16
:background-color (when (= :pinned (:use-case @state))
(colors/custom-color :blue 50 10))
:align-items :flex-start}
[quo/react
{:reactions reactions
:hide-new-reaction-button? (:hide-new-reaction-button? @state)
:use-case (:use-case @state)
:on-press (fn [reaction]
(let [reaction-id (:emoji-id reaction)
change-pressed (partial swap! pressed-reactions)]
(if (contains? @pressed-reactions reaction-id)
(change-pressed disj reaction-id)
(change-pressed conj reaction-id))))
:on-long-press identity
:on-press-new identity}]]]))))
[pressed-reactions set-pressed-reactions] (rn/use-state #{1})
reactions (mapv (fn [reaction-id]
{:emoji-reaction-id reaction-id
:emoji-id reaction-id
:emoji (get constants/reactions reaction-id)
:quantity (memo-gen-quantity (:max-count state) reaction-id)
:own (contains? pressed-reactions reaction-id)})
(:reaction-ids state))
on-press (fn [reaction]
(let [reaction-id (:emoji-id reaction)]
(if (contains? pressed-reactions reaction-id)
(set-pressed-reactions (disj pressed-reactions reaction-id))
(set-pressed-reactions (conj pressed-reactions
reaction-id)))))]
[preview/preview-container
{:state state
:set-state set-state
:descriptor descriptor}
[rn/view
{:padding-bottom 150
:padding-vertical 60
:padding-horizontal 20
:border-radius 16
:background-color (when (= :pinned (:use-case state))
(colors/custom-color :blue 50 10))
:align-items :flex-start}
[quo/react
{:reactions reactions
:hide-new-reaction-button? (:hide-new-reaction-button? state)
:use-case (:use-case state)
:on-press on-press
:on-long-press identity
:on-press-new identity}]]]))