From 8df314b2c204bfd68773de6a0049481e962d073c Mon Sep 17 00:00:00 2001 From: Icaro Motta Date: Thu, 20 Jun 2024 19:10:04 -0300 Subject: [PATCH] chore(malli)_: New map schema to reduce optional/maybe verbosity --- .../avatars/user_avatar/schema.cljs | 20 ++--- .../drawers/drawer_action/schema.cljs | 25 +++--- .../components/list_items/account/schema.cljs | 17 ++-- .../list_items/account_list_card/schema.cljs | 12 +-- src/quo/components/tabs/tabs/schema.cljs | 32 +++---- .../tags/collectible_tag/schema.cljs | 16 ++-- .../wallet/account_card/schema.cljs | 22 ++--- .../wallet/account_origin/schema.cljs | 10 +-- .../wallet/address_text/schema.cljs | 17 ++-- .../wallet/amount_input/schema.cljs | 16 ++-- .../wallet/required_tokens/schema.cljs | 18 ++-- .../wallet/transaction_summary/schema.cljs | 28 +++---- src/schema/common.cljs | 83 +++++++++++++++++++ src/schema/registry.cljs | 8 +- 14 files changed, 205 insertions(+), 119 deletions(-) diff --git a/src/quo/components/avatars/user_avatar/schema.cljs b/src/quo/components/avatars/user_avatar/schema.cljs index 3c113616ad..269e4ba89e 100644 --- a/src/quo/components/avatars/user_avatar/schema.cljs +++ b/src/quo/components/avatars/user_avatar/schema.cljs @@ -6,15 +6,13 @@ [:=> [:catn [:props - [:map - [:full-name {:optional true} [:maybe string?]] - [:size {:optional true} [:maybe (into [:enum] (keys style/sizes))]] - [:customization-color {:optional true} [:maybe :schema.common/customization-color]] - [:static? {:optional true} [:maybe boolean?]] - [:status-indicator? {:optional true} [:maybe boolean?]] - [:online? {:optional true} [:maybe boolean?]] - [:ring? {:optional true} [:maybe boolean?]] - [:profile-picture - {:optional true} - [:maybe :schema.quo/profile-picture-source]]]]] + [:schema.common/map {:optional true :maybe true} + [:full-name string?] + [:size (into [:enum] (keys style/sizes))] + [:customization-color :schema.common/customization-color] + [:static? boolean?] + [:status-indicator? boolean?] + [:online? boolean?] + [:ring? boolean?] + [:profile-picture :schema.quo/profile-picture-source]]]] :any]) diff --git a/src/quo/components/drawers/drawer_action/schema.cljs b/src/quo/components/drawers/drawer_action/schema.cljs index db5f9b7dd3..447cabb6df 100644 --- a/src/quo/components/drawers/drawer_action/schema.cljs +++ b/src/quo/components/drawers/drawer_action/schema.cljs @@ -3,17 +3,16 @@ (def ?schema [:=> [:cat - [:map {:closed true} - [:accessibility-label {:optional true} [:maybe :keyword]] - [:type {:optional true} [:maybe [:enum :main :danger]]] - [:action {:optional true} [:maybe [:enum :arrow :toggle :input]]] - [:icon {:optional true} [:maybe :keyword]] - [:description {:optional true} [:maybe :string]] - [:state {:optional true} [:maybe [:enum :selected]]] - [:title {:optional true} :string] - [:on-press {:optional true} [:maybe fn?]] - [:input-props {:optional true} [:maybe :map]] - [:customization-color {:optional true} - [:maybe :schema.common/customization-color]] - [:blur? {:optional true} [:maybe :boolean]]]] + [:schema.common/map {:closed true :optional true :maybe true} + [:accessibility-label :keyword] + [:type [:enum :main :danger]] + [:action [:enum :arrow :toggle :input]] + [:icon :keyword] + [:description :string] + [:state [:enum :selected]] + [:title {:no-maybe true} :string] + [:on-press fn?] + [:input-props :map] + [:customization-color :schema.common/customization-color] + [:blur? :boolean]]] :any]) diff --git a/src/quo/components/list_items/account/schema.cljs b/src/quo/components/list_items/account/schema.cljs index dd55d91679..e5c6d4d8c7 100644 --- a/src/quo/components/list_items/account/schema.cljs +++ b/src/quo/components/list_items/account/schema.cljs @@ -20,15 +20,14 @@ [:on-options-press {:optional true} [:maybe fn?]]]) (def ^:private ?base - [:map - [:type {:optional true} - [:enum :default :tag :action :balance-neutral :balance-negative :balance-positive]] - [:state {:optional true} [:enum :default :selected :active]] - [:blur? {:optional true} [:maybe :boolean]] - [:customization-color {:optional true} [:maybe :schema.common/customization-color]] - [:on-press {:optional true} [:maybe fn?]] - [:title-icon {:optional true} [:maybe :keyword]] - [:account-props + [:schema.common/map {:optional true} + [:type [:enum :default :tag :action :balance-neutral :balance-negative :balance-positive]] + [:state [:enum :default :selected :active]] + [:blur? [:maybe :boolean]] + [:customization-color [:maybe :schema.common/customization-color]] + [:on-press [:maybe fn?]] + [:title-icon [:maybe :keyword]] + [:account-props {:no-optional true} [:map [:name :string] [:address :string] diff --git a/src/quo/components/list_items/account_list_card/schema.cljs b/src/quo/components/list_items/account_list_card/schema.cljs index 9d170e3142..c0d45bd36a 100644 --- a/src/quo/components/list_items/account_list_card/schema.cljs +++ b/src/quo/components/list_items/account_list_card/schema.cljs @@ -1,11 +1,11 @@ (ns quo.components.list-items.account-list-card.schema) (def ^:private ?base - [:map - [:action {:optional true} [:enum :icon :none]] - [:blur? {:optional true} [:maybe :boolean]] - [:on-press {:optional true} [:maybe fn?]] - [:account-props + [:schema.common/map {:optional true} + [:action [:enum :icon :none]] + [:blur? [:maybe :boolean]] + [:on-press [:maybe fn?]] + [:account-props {:no-optional true} [:map {:closed true} [:type [:enum :default :watch-only]] [:name :string] @@ -13,7 +13,7 @@ [:emoji :string] [:size {:optional true} [:enum 80 :size-64 48 32 28 24 20 16]] [:customization-color {:optional true} [:maybe :schema.common/customization-color]]]] - [:networks {:optional true} [:* [:map [:network-name :keyword] [:short-name :string]]]]]) + [:networks [:* [:map [:network-name :keyword] [:short-name :string]]]]]) (def ^:private ?on-option-press [:map diff --git a/src/quo/components/tabs/tabs/schema.cljs b/src/quo/components/tabs/tabs/schema.cljs index e7d35323e6..a3f05ee8c3 100644 --- a/src/quo/components/tabs/tabs/schema.cljs +++ b/src/quo/components/tabs/tabs/schema.cljs @@ -13,20 +13,20 @@ [:=> [:catn [:props - [:map - [:default-active {:optional true} [:maybe [:or :int :keyword]]] - [:active-tab-id {:optional true} [:maybe [:or :int :keyword]]] - [:data ?data] - [:fade-end-percentage {:optional true} [:or :double :string]] - [:fade-end? {:optional true} [:maybe :boolean]] - [:blur? {:optional true} [:maybe :boolean]] - [:on-change {:optional true} [:maybe fn?]] - [:on-scroll {:optional true} [:maybe fn?]] - [:scroll-on-press? {:optional true} [:maybe :boolean]] - [:scrollable? {:optional true} [:maybe :boolean]] - [:style {:optional true} [:maybe :map]] - [:container-style {:optional true} [:maybe :map]] - [:size {:optional true} [:maybe [:or :keyword :int]]] - [:in-scroll-view? {:optional true} [:maybe :boolean]] - [:customization-color {:optional true} [:maybe :schema.common/customization-color]]]]] + [:schema.common/map {:optional true :maybe true} + [:default-active [:or :int :keyword]] + [:active-tab-id [:or :int :keyword]] + [:data {:no-maybe true} ?data] + [:fade-end-percentage {:no-maybe true} [:or :double :string]] + [:fade-end? :boolean] + [:blur? :boolean] + [:on-change fn?] + [:on-scroll fn?] + [:scroll-on-press? :boolean] + [:scrollable? :boolean] + [:style :map] + [:container-style :map] + [:size [:or :keyword :int]] + [:in-scroll-view? :boolean] + [:customization-color :schema.common/customization-color]]]] :any]) diff --git a/src/quo/components/tags/collectible_tag/schema.cljs b/src/quo/components/tags/collectible_tag/schema.cljs index a2292584fb..e97627ee29 100644 --- a/src/quo/components/tags/collectible_tag/schema.cljs +++ b/src/quo/components/tags/collectible_tag/schema.cljs @@ -4,11 +4,13 @@ [:=> [:catn [:props - [:map - [:options {:optional true} [:maybe [:enum :add :hold]]] - [:size {:optional true} [:maybe [:enum :size-24 :size-32]]] - [:blur? {:optional true} [:maybe :boolean]] - [:collectible-img-src :schema.common/image-source] - [:collectible-name :string] - [:collectible-id {:optional true} [:maybe :string]]]]] + [:schema.common/map {:optional true :maybe true} + [:options [:enum :add :hold]] + [:size [:enum :size-24 :size-32]] + [:blur? :boolean] + [:collectible-id :string] + [:collectible-img-src {:no-optional true :no-maybe true} + :schema.common/image-source] + [:collectible-name {:no-optional true :no-maybe true} + :string]]]] :any]) diff --git a/src/quo/components/wallet/account_card/schema.cljs b/src/quo/components/wallet/account_card/schema.cljs index 996665206d..45af7b467b 100644 --- a/src/quo/components/wallet/account_card/schema.cljs +++ b/src/quo/components/wallet/account_card/schema.cljs @@ -1,23 +1,23 @@ (ns quo.components.wallet.account-card.schema) (def ^:private ?base - [:map - [:type {:optional true} [:enum :default :watch-only :add-account :empty :missing-keypair]] - [:customization-color {:optional true} [:maybe :schema.common/customization-color]] - [:metrics? {:optional true} [:maybe :boolean]] - [:on-press {:optional true} [:maybe fn?]]]) + [:schema.common/map {:optional true :maybe true} + [:type {:no-maybe true} [:enum :default :watch-only :add-account :empty :missing-keypair]] + [:customization-color :schema.common/customization-color] + [:metrics? :boolean] + [:on-press fn?]]) (def ^:private ?amount [:map [:amount {:optional true} [:maybe :string]]]) (def ^:private ?card - [:map - [:balance {:optional true} [:maybe :string]] - [:loading? {:optional true} [:maybe :boolean]] - [:name {:optional true} [:maybe :string]] - [:percentage-value {:optional true} [:maybe :string]] - [:emoji {:optional true} [:maybe :string]]]) + [:schema.common/map {:optional true :maybe true} + [:balance :string] + [:loading? :boolean] + [:name :string] + [:percentage-value :string] + [:emoji :string]]) (def ?schema [:=> diff --git a/src/quo/components/wallet/account_origin/schema.cljs b/src/quo/components/wallet/account_origin/schema.cljs index 0949e6d8cd..57456b6cbf 100644 --- a/src/quo/components/wallet/account_origin/schema.cljs +++ b/src/quo/components/wallet/account_origin/schema.cljs @@ -10,11 +10,11 @@ [:keypair-name {:optional true} [:maybe :string]]]) (def ^:private ?default-keypair - [:map - [:profile-picture {:optional true} [:maybe :schema.common/image-source]] - [:derivation-path {:optional true} [:maybe :string]] - [:on-press {:optional true} [:maybe fn?]] - [:customization-color {:optional true} [:maybe :schema.common/customization-color]]]) + [:schema.common/map {:optional true :maybe true} + [:profile-picture :schema.common/image-source] + [:derivation-path :string] + [:on-press fn?] + [:customization-color :schema.common/customization-color]]) (def ^:private ?recovery-phrase [:map diff --git a/src/quo/components/wallet/address_text/schema.cljs b/src/quo/components/wallet/address_text/schema.cljs index 6f917403e2..2f5cf83e93 100644 --- a/src/quo/components/wallet/address_text/schema.cljs +++ b/src/quo/components/wallet/address_text/schema.cljs @@ -4,15 +4,14 @@ [:=> [:catn [:props - [:map - [:address {:optional true} [:maybe :string]] - [:blur? {:optional true} [:maybe :boolean]] - [:format {:optional true} [:enum :short :long]] - [:networks {:optional true} - [:maybe [:sequential [:map [:network-name :keyword] [:short-name :string]]]]] - [:full-address? {:optional true} [:maybe :boolean]] + [:schema.common/map {:optional true :maybe true} + [:address :string] + [:blur? :boolean] + [:format {:no-maybe true} [:enum :short :long]] + [:networks [:sequential [:map [:network-name :keyword] [:short-name :string]]]] + [:full-address? :boolean] ;; TODO: size and weight are text schemas and should be imported here ;; https://github.com/status-im/status-mobile/issues/19443 - [:size {:optional true} [:maybe :keyword]] - [:weight {:optional true} [:maybe :keyword]]]]] + [:size :keyword] + [:weight :keyword]]]] :any]) diff --git a/src/quo/components/wallet/amount_input/schema.cljs b/src/quo/components/wallet/amount_input/schema.cljs index 394cdc65da..49bee5ad53 100644 --- a/src/quo/components/wallet/amount_input/schema.cljs +++ b/src/quo/components/wallet/amount_input/schema.cljs @@ -4,12 +4,12 @@ [:=> [:catn [:props - [:map {:closed true} - [:status {:optional true} [:maybe [:enum :default :error]]] - [:on-inc-press {:optional true} [:maybe fn?]] - [:on-dec-press {:optional true} [:maybe fn?]] - [:container-style {:optional true} [:maybe :map]] - [:min-value {:optional true} [:maybe :int]] - [:max-value {:optional true} [:maybe :int]] - [:value [:maybe :int]]]]] + [:schema.common/map {:closed true :optional true :maybe true} + [:status [:enum :default :error]] + [:on-inc-press fn?] + [:on-dec-press fn?] + [:container-style :map] + [:min-value :int] + [:max-value :int] + [:value {:no-optional true} :int]]]] :any]) diff --git a/src/quo/components/wallet/required_tokens/schema.cljs b/src/quo/components/wallet/required_tokens/schema.cljs index 6ad329d869..244c7b9263 100644 --- a/src/quo/components/wallet/required_tokens/schema.cljs +++ b/src/quo/components/wallet/required_tokens/schema.cljs @@ -4,13 +4,13 @@ [:=> [:catn [:props - [:map {:closed true} - [:type [:enum :token :collectible]] - [:amount {:optional true} [:maybe [:or :string :int]]] - [:token {:optional true} [:maybe :string]] - [:token-img-src {:optional true} [:maybe :schema.common/image-source]] - [:collectible-img-src {:optional true} [:maybe :schema.common/image-source]] - [:collectible-name {:optional true} [:maybe :string]] - [:divider? {:optional true} [:maybe :boolean]] - [:container-style {:optional true} [:maybe :map]]]]] + [:schema.common/map {:closed true :optional true :maybe true} + [:type {:no-maybe true} [:enum :token :collectible]] + [:amount [:or :string :int]] + [:token :string] + [:token-img-src :schema.common/image-source] + [:collectible-img-src :schema.common/image-source] + [:collectible-name :string] + [:divider? :boolean] + [:container-style :map]]]] :any]) diff --git a/src/quo/components/wallet/transaction_summary/schema.cljs b/src/quo/components/wallet/transaction_summary/schema.cljs index 4cfe9e2173..9b2c946cf3 100644 --- a/src/quo/components/wallet/transaction_summary/schema.cljs +++ b/src/quo/components/wallet/transaction_summary/schema.cljs @@ -5,18 +5,18 @@ [:=> [:catn [:props - [:map - [:transaction {:optional true} [:maybe [:enum :send :swap :bridge]]] - [:first-tag {:optional true} [:maybe context-tag-schema/?schema]] - [:second-tag {:optional true} [:maybe context-tag-schema/?schema]] - [:third-tag {:optional true} [:maybe context-tag-schema/?schema]] - [:fourth-tag {:optional true} [:maybe context-tag-schema/?schema]] - [:fifth-tag {:optional true} [:maybe context-tag-schema/?schema]] - [:second-tag-prefix {:optional true} [:maybe :keyword]] - [:third-tag-prefix {:optional true} [:maybe :keyword]] - [:fourth-tag-prefix {:optional true} [:maybe :keyword]] - [:max-fees {:optional true} [:maybe :string]] - [:nonce {:optional true} [:maybe :int]] - [:input-data {:optional true} [:maybe :string]] - [:on-press {:optional true} [:maybe fn?]]]]] + [:schema.common/map {:optional true :maybe true} + [:transaction [:enum :send :swap :bridge]] + [:first-tag context-tag-schema/?schema] + [:second-tag context-tag-schema/?schema] + [:third-tag context-tag-schema/?schema] + [:fourth-tag context-tag-schema/?schema] + [:fifth-tag context-tag-schema/?schema] + [:second-tag-prefix :keyword] + [:third-tag-prefix :keyword] + [:fourth-tag-prefix :keyword] + [:max-fees :string] + [:nonce :int] + [:input-data :string] + [:on-press fn?]]]] :any]) diff --git a/src/schema/common.cljs b/src/schema/common.cljs index 7945b1fa25..276ec8323b 100644 --- a/src/schema/common.cljs +++ b/src/schema/common.cljs @@ -1,7 +1,89 @@ (ns schema.common (:require + [malli.core :as malli] + malli.util [schema.registry :as registry])) +(defn- optional-keys + "Makes map keys optional, non-recursively. + + Ignores keys with schema property :no-maybe true." + [?schema] + (let [mapper (fn [[_k properties :as entry]] + (if (:no-optional properties) + (update entry 1 dissoc :no-optional) + (update entry 1 assoc :optional true)))] + (malli.util/transform-entries ?schema #(map mapper %) nil))) + +(defn- maybe-keys + "Makes map keys nullable, non-recursively. + + Ignores keys with schema property :no-maybe true." + [?schema] + (let [mapper (fn [[_k properties ?key :as entry]] + (let [key-form (malli/form ?key)] + (if (or (:no-maybe properties) + (and (vector? key-form) + (= :maybe (first key-form)))) + (update entry 1 dissoc :no-maybe) + (assoc entry 2 [:maybe key-form]))))] + (malli.util/transform-entries ?schema #(map mapper %) nil))) + +(defn- ?map-optional-maybe + "This implementation should closely track how the base schema `:map` is + implemented by malli. This is a solution to sort of inherit a base schema and + reuse its implementation, but extend it to support new features. + + Usage: + + [:schema.common/map {:optional true :maybe true} + [:type [:enum :default :multiuser :group]] + [:customization-color :schema.common/customization-color] + [:blur? boolean?] + [:value {:no-optional true} pos-int?]] + + This is the same as if you manually entered the following: + + [:map + [:type {:optional true} [:maybe [:enum :default :multiuser :group]]] + [:customization-color {:optional true} [:maybe :schema.common/customization-color]] + [:blur? {:optional true} [:maybe :boolean]] + [:value [:maybe pos-int?]]] + + You can verify the final transformed `:schema.common/map` by + calling `(malli.core/form ?some-schema)`. + + Schema `:schema.common/map` may take two properties: + + - When `:optional` is non-nil, every key will have its `:optional` property + set to true. If any key uses the property `:no-optional` non-nil, the key is + ignored. + + - When `:maybe` is non-nil, every key will have its children wrapped by a + `:maybe` schema. If any key uses the property `:no-maybe` non-nil, the key is + ignored. + " + [] + ^{:type ::malli/into-schema} + (reify + malli/AST + (-from-ast [parent ast options] + (malli/-from-ast parent ast options)) + + malli/IntoSchema + (-type [_] + :schema.common/map) + (-type-properties [this] + (malli/-type-properties this)) + (-properties-schema [this options] + (malli/-properties-schema this options)) + (-children-schema [this options] + (malli/-children-schema this options)) + (-into-schema [_parent props children options] + (cond-> (malli/into-schema :map (dissoc props :optional :maybe) children options) + (:optional props) (optional-keys) + (:maybe props) (maybe-keys))))) + (def ^:private ?theme [:enum :light :dark]) @@ -44,4 +126,5 @@ (registry/register ::image-source ?image-source) (registry/register ::rpc-call ?rpc-call) (registry/register ::exception ?exception) + (registry/register ::map (?map-optional-maybe)) (registry/register ::hiccup ?hiccup)) diff --git a/src/schema/registry.cljs b/src/schema/registry.cljs index 6af72064f4..0e90287c60 100644 --- a/src/schema/registry.cljs +++ b/src/schema/registry.cljs @@ -17,7 +17,13 @@ We normalize `?schema` by always registering it as a proper instance of `malli.core/Schema` to avoid inconsistencies down the road." [type ?schema] - (swap! registry assoc type (malli/schema ?schema)) + (swap! registry assoc + type + ;; An into-schema instance should not be converted to schema. This will cause a + ;; :malli.core/invalid-schema. One such schema is `:schema.common/map`. + (if (malli/into-schema? ?schema) + ?schema + (malli/schema ?schema))) ?schema) (defn merge