diff --git a/src/quo/components/blur/view.cljs b/src/quo/components/blur/view.cljs index 6b7a082597..e6cac16c2e 100644 --- a/src/quo/components/blur/view.cljs +++ b/src/quo/components/blur/view.cljs @@ -10,11 +10,10 @@ ([{:keys [style]} child] (let [theme (quo.theme/use-theme)] [rn/view - {:style (assoc style - :pointer-events :box-none - :background-color - (or (:background-color style) - (colors/theme-colors colors/white colors/neutral-80 theme)))} + {:style [style + {:pointer-events :box-none + :background-color (or (:background-color style) + (colors/theme-colors colors/white colors/neutral-80 theme))}]} child]))) (def view (if platform/ios? blur/view view-android)) diff --git a/src/quo/components/buttons/button/style.cljs b/src/quo/components/buttons/button/style.cljs index 28db9c3e8d..cfbbe2e079 100644 --- a/src/quo/components/buttons/button/style.cljs +++ b/src/quo/components/buttons/button/style.cljs @@ -56,62 +56,63 @@ 24 8 12))}) -(defn style-container +(defn container-styles [{:keys [size disabled? border-radius background-color border-color icon-only? icon-top - icon-left icon-right]}] - (merge {:height size - :align-items :center - :justify-content :center - :flex-direction (if icon-top :column :row) - :padding-horizontal (when-not (or icon-only? icon-left icon-right) - (case size - 56 (if border-color 10 11) - 40 16 - 32 12 - 24 7 - 16)) - :padding-left (when-not (or icon-only? icon-left) - (case size - 56 nil - 40 16 - 32 12 - 24 8 - 16)) - :padding-right (when-not (or icon-only? icon-right) - (case size - 56 nil - 40 16 - 32 12 - 24 8 - 16)) - :padding-top (when-not (or icon-only? icon-left icon-right) - (case size - 56 0 - 40 (if border-color 8 9) - 32 (if border-color 4 5) - 24 0 - (if border-color 8 9))) - :padding-bottom (when-not (or icon-only? icon-left icon-right) - (case size - 56 0 - 40 9 - 32 5 - 24 0 - 9)) - :overflow :hidden - :background-color (if disabled? (colors/alpha background-color 0.3) background-color) - :border-radius (if border-radius - border-radius - (case size - 56 12 - 40 12 - 32 10 - 24 8 - 12)) - :border-color border-color - :border-width (when border-color 1)} - (when icon-only? - {:width size}) - (when border-color - {:border-color border-color - :border-width 1}))) + icon-left icon-right inner-style]}] + [{:height size + :align-items :center + :justify-content :center + :flex-direction (if icon-top :column :row) + :padding-horizontal (when-not (or icon-only? icon-left icon-right) + (case size + 56 (if border-color 10 11) + 40 16 + 32 12 + 24 7 + 16)) + :padding-left (when-not (or icon-only? icon-left) + (case size + 56 nil + 40 16 + 32 12 + 24 8 + 16)) + :padding-right (when-not (or icon-only? icon-right) + (case size + 56 nil + 40 16 + 32 12 + 24 8 + 16)) + :padding-top (when-not (or icon-only? icon-left icon-right) + (case size + 56 0 + 40 (if border-color 8 9) + 32 (if border-color 4 5) + 24 0 + (if border-color 8 9))) + :padding-bottom (when-not (or icon-only? icon-left icon-right) + (case size + 56 0 + 40 9 + 32 5 + 24 0 + 9)) + :overflow :hidden + :background-color (if disabled? (colors/alpha background-color 0.3) background-color) + :border-radius (if border-radius + border-radius + (case size + 56 12 + 40 12 + 32 10 + 24 8 + 12)) + :border-color border-color + :border-width (when border-color 1)} + (when icon-only? + {:width size}) + (when border-color + {:border-color border-color + :border-width 1}) + inner-style]) diff --git a/src/quo/components/buttons/button/view.cljs b/src/quo/components/buttons/button/view.cljs index 73cd893fe4..bc692d1355 100644 --- a/src/quo/components/buttons/button/view.cljs +++ b/src/quo/components/buttons/button/view.cljs @@ -62,12 +62,9 @@ :on-press on-press :allow-multiple-presses? allow-multiple-presses? :on-long-press on-long-press - :style (merge - (style/shape-style-container size border-radius) - container-style)} + :style [(style/shape-style-container size border-radius) container-style]} [rn/view - {:style (merge - (style/style-container {:size size + {:style (style/container-styles {:size size :disabled? disabled? :border-radius border-radius :background-color background-color @@ -75,8 +72,8 @@ :icon-only? icon-only? :icon-top icon-top :icon-left icon-left - :icon-right icon-right}) - inner-style)} + :icon-right icon-right + :inner-style inner-style})} (when overlay-customization-color [customization-colors/overlay {:customization-color overlay-customization-color diff --git a/src/quo/components/drawers/drawer_top/view.cljs b/src/quo/components/drawers/drawer_top/view.cljs index fce8012455..a53e11e0e8 100644 --- a/src/quo/components/drawers/drawer_top/view.cljs +++ b/src/quo/components/drawers/drawer_top/view.cljs @@ -201,7 +201,7 @@ button-disabled? account-avatar-emoji account-avatar-type customization-color icon-avatar context icon]}] (let [theme (quo.theme/use-theme)] - [rn/view {:style (merge style/container container-style)} + [rn/view {:style [style/container container-style]} (when (left-image-supported-types type) [rn/view {:style style/left-container} [left-image diff --git a/src/quo/components/gradient/gradient_cover/view.cljs b/src/quo/components/gradient/gradient_cover/view.cljs index 0a601c942e..ef271710bc 100644 --- a/src/quo/components/gradient/gradient_cover/view.cljs +++ b/src/quo/components/gradient/gradient_cover/view.cljs @@ -18,5 +18,5 @@ :colors [color-top color-bottom] :start {:x 0 :y 0} :end {:x 0 :y 1} - :style (merge (style/root-container opacity height) - container-style)}]))) + :style [(style/root-container opacity height) + container-style]}]))) diff --git a/src/quo/components/inputs/input/view.cljs b/src/quo/components/inputs/input/view.cljs index f9867e3c80..a58e478003 100644 --- a/src/quo/components/inputs/input/view.cljs +++ b/src/quo/components/inputs/input/view.cljs @@ -131,7 +131,7 @@ :char-limit char-limit :theme theme}]) [rn/view - {:style (merge (style/input-container colors-by-status small? disabled?) input-container-style)} + {:style [(style/input-container colors-by-status small? disabled?) input-container-style]} (when-let [{:keys [icon-name]} left-icon] [left-accessory {:variant-colors variant-colors diff --git a/src/quo/components/keycard/style.cljs b/src/quo/components/keycard/style.cljs index 3b8e6c41c7..0762602f3c 100644 --- a/src/quo/components/keycard/style.cljs +++ b/src/quo/components/keycard/style.cljs @@ -36,7 +36,7 @@ (colors/theme-colors colors/neutral-100 colors/white theme)) :align-self :center :height 280.48 - :transform [{:rotate "-30deg"} {:translateY -30}] + :transform [{:rotate "-30deg"} {:translate-y -30}] :opacity (when-not locked? 0.02) :z-index 1}) diff --git a/src/quo/components/messages/gap.cljs b/src/quo/components/messages/gap.cljs index 9229b680ec..bfd8a03044 100644 --- a/src/quo/components/messages/gap.cljs +++ b/src/quo/components/messages/gap.cljs @@ -48,7 +48,7 @@ :margin-left -4} (if (= type :top) {:top 0} - {:transform [{:rotateZ "180deg"}] + {:transform [{:rotate-z "180deg"}] :bottom 0}) style)}])) diff --git a/src/quo/components/settings/category/data_item/view.cljs b/src/quo/components/settings/category/data_item/view.cljs index e83f4966f0..d920d2735a 100644 --- a/src/quo/components/settings/category/data_item/view.cljs +++ b/src/quo/components/settings/category/data_item/view.cljs @@ -10,7 +10,7 @@ [{:keys [label data container-style blur?]}] (let [theme (quo.theme/use-theme) last-item (rn/use-memo #(last data) [data])] - [rn/view {:style (merge (style/container label) container-style)} + [rn/view {:style [(style/container label) container-style]} (when label [text/text {:weight :medium @@ -29,4 +29,3 @@ [data-item/view data-item-props] (when-not (= item last-item) [rn/view {:style (style/settings-separator blur? theme)}])])]])) - diff --git a/src/quo/components/settings/category/reorder/view.cljs b/src/quo/components/settings/category/reorder/view.cljs index 0e57ed90c1..48d160bd25 100644 --- a/src/quo/components/settings/category/reorder/view.cljs +++ b/src/quo/components/settings/category/reorder/view.cljs @@ -24,7 +24,7 @@ separator (rn/use-memo (fn [] [rn/view {:style (style/reorder-separator blur? theme)}]) [blur? theme])] - [rn/view {:style (merge (style/container label) container-style)} + [rn/view {:style [(style/container label) container-style]} [text/text {:weight :medium :size :paragraph-2 diff --git a/src/quo/components/settings/category/settings/view.cljs b/src/quo/components/settings/category/settings/view.cljs index 566b1147ee..f6059e44d5 100644 --- a/src/quo/components/settings/category/settings/view.cljs +++ b/src/quo/components/settings/category/settings/view.cljs @@ -11,7 +11,7 @@ (let [theme (quo.theme/use-theme) settings-items (remove nil? data) last-index (dec (count settings-items))] - [rn/view {:style (merge (style/container label) container-style)} + [rn/view {:style [(style/container label) container-style]} (when label [text/text {:weight :medium diff --git a/src/quo/components/share/qr_code/style.cljs b/src/quo/components/share/qr_code/style.cljs index 3fdee84e04..baef44043b 100644 --- a/src/quo/components/share/qr_code/style.cljs +++ b/src/quo/components/share/qr_code/style.cljs @@ -10,12 +10,7 @@ {:flex 1})) (def avatar-overlay - {:position :absolute - :top 0 - :right 0 - :left 0 - :bottom 0 - :justify-content :center + {:justify-content :center :align-items :center}) (defn qr-image diff --git a/src/quo/components/share/qr_code/view.cljs b/src/quo/components/share/qr_code/view.cljs index 4ff539cb6b..09b835f6a7 100644 --- a/src/quo/components/share/qr_code/view.cljs +++ b/src/quo/components/share/qr_code/view.cljs @@ -11,7 +11,7 @@ (defn- avatar-image [{avatar-type :avatar :as props}] - [rn/view {:style style/avatar-overlay} + [rn/view {:style [rn/stylesheet-absolute-fill style/avatar-overlay]} [rn/view {:style (case avatar-type :wallet-account style/avatar-container-rounded diff --git a/src/quo/components/tags/context_tag/view.cljs b/src/quo/components/tags/context_tag/view.cljs index c48977548b..0bd00a6cb3 100644 --- a/src/quo/components/tags/context_tag/view.cljs +++ b/src/quo/components/tags/context_tag/view.cljs @@ -97,7 +97,7 @@ theme (quo.theme/use-theme)} :as props}] (let [[image-error? set-image-error] (rn/use-state false)] - [rn/view {:style (merge {:align-items :flex-start} container-style)} + [rn/view {:style [{:align-items :flex-start} container-style]} [rn/view {:style (style/container {:theme theme :type type diff --git a/src/quo/components/wallet/network_bridge/view.cljs b/src/quo/components/wallet/network_bridge/view.cljs index 0a00655ac1..16fbefae50 100644 --- a/src/quo/components/wallet/network_bridge/view.cljs +++ b/src/quo/components/wallet/network_bridge/view.cljs @@ -14,9 +14,9 @@ (defn network-bridge-add [{:keys [network state theme container-style on-press]}] [rn/pressable - {:style (merge (style/container network state theme) - (style/add-container theme) - container-style) + {:style [(style/container network state theme) + (style/add-container theme) + container-style] :on-press on-press} [icon/icon :i/edit {:size 12 @@ -36,7 +36,7 @@ (if (= status :edit) [network-bridge-add (assoc args :theme theme)] [rn/pressable - {:style (merge (style/container network status theme) container-style) + {:style [(style/container network status theme) container-style] :accessible true :accessibility-label :container :on-press on-press diff --git a/src/react_native/core.cljs b/src/react_native/core.cljs index 694e2f2a67..6e5a7752c4 100644 --- a/src/react_native/core.cljs +++ b/src/react_native/core.cljs @@ -211,3 +211,5 @@ (def linking (.-Linking react-native)) (defn open-url [link] (.openURL ^js linking link)) + +(def stylesheet-absolute-fill ^js (.. react-native -StyleSheet -absoluteFill)) diff --git a/src/react_native/reanimated.cljs b/src/react_native/reanimated.cljs index 0d6967e5fb..63b0717343 100644 --- a/src/react_native/reanimated.cljs +++ b/src/react_native/reanimated.cljs @@ -21,9 +21,7 @@ ["react-native-redash" :refer (withPause)] [react-native.flat-list :as rn-flat-list] [react-native.platform :as platform] - [react-native.utils :as rn.utils] [reagent.core :as reagent] - [utils.transforms :as transforms] [utils.worklets.core :as worklets.core])) (def ^:const default-duration 300) @@ -36,13 +34,7 @@ ;; Animated Components (def create-animated-component (comp reagent/adapt-react-class (.-createAnimatedComponent reanimated))) -(def ^:private view* (reagent/adapt-react-class (.-View reanimated))) - -(defn view - [& argv] - (let [[reagent-props children] (rn.utils/get-props-and-children argv) - updated-props (update reagent-props :style transforms/styles-with-vectors)] - (into [view* updated-props] children))) +(def view (reagent/adapt-react-class (.-View reanimated))) (def scroll-view (reagent/adapt-react-class (.-ScrollView reanimated))) (def image (reagent/adapt-react-class (.-Image reanimated))) @@ -186,4 +178,3 @@ (with-timing value (clj->js {:duration duration :easing (default-easing)}))))) - diff --git a/src/react_native/utils_test.cljs b/src/react_native/utils_test.cljs new file mode 100644 index 0000000000..350c1f0d1f --- /dev/null +++ b/src/react_native/utils_test.cljs @@ -0,0 +1,54 @@ +(ns react-native.utils-test + (:require + [cljs.test :refer [deftest is testing]] + [utils.reagent :as sut])) + +(sut/set-convert-props-in-vectors!) + +(deftest convert-prop-value-test + ;; `test-fn` transforms the result to be easily compared during testing + (let [test-fn (comp js->clj sut/convert-prop-value)] + (testing "camelCase keys are kept as is" + (let [props {:foo nil + :bar nil}] + (is (= (update-keys props name) + (test-fn props))))) + + (testing "kebab-case keys are transformed" + (let [props {:foo nil + :bar nil}] + (is (= {"foo" nil + "bar" nil} + (test-fn props))))) + + (testing "kebab-case keys are transformed when passed inside a vector" + (let [props-in-vector [{:foo nil + :bar nil}]] + (is (= [{"foo" nil + "bar" nil}] + (test-fn props-in-vector))))) + + (testing "kebab-case keys are transformed recursively when the structure has vectors" + (let [props-with-vectors {:foo [{:foo-bar nil + :foo-baz nil}] + :bar [{:bar-baz nil} + {:bar-qux nil}]}] + (is (= {"foo" [{"fooBar" nil + "fooBaz" nil}] + "bar" [{"barBaz" nil} {"barQux" nil}]} + (test-fn props-with-vectors)))) + (testing "Complex example" + (let [complex-props {:foo [{:foo-bar nil :foo-baz nil}] + :bar [{:bar-baz nil} {:bar-qux nil}] + :qux {:foo-qux :bar-qux} + :foo-bar-qux {:foo [{:bar :qux} + {:bar-qux nil}] + :foo-bar :qux + :foo-bar-qux [:foo :bar :qux {:foo-bar :foo-bar}]}}] + (is (= {"foo" [{"fooBar" nil "fooBaz" nil}] + "bar" [{"barBaz" nil} {"barQux" nil}] + "qux" {"fooQux" "bar-qux"} + "fooBarQux" {"foo" [{"bar" "qux"} {"barQux" nil}] + "fooBar" "qux" + "fooBarQux" ["foo" "bar" "qux" {"fooBar" "foo-bar"}]}} + (test-fn complex-props)))))))) diff --git a/src/status_im/common/scan_qr_code/style.cljs b/src/status_im/common/scan_qr_code/style.cljs index 1db2406402..ebf773da7a 100644 --- a/src/status_im/common/scan_qr_code/style.cljs +++ b/src/status_im/common/scan_qr_code/style.cljs @@ -15,16 +15,7 @@ (def flex-spacer {:flex 1}) -(def absolute-fill - {:position :absolute - :top 0 - :bottom 0 - :left 0 - :right 0}) - -(def hole - (merge absolute-fill - {:z-index 2 :opacity 0.95})) +(def hole {:z-index 2 :opacity 0.95}) (defn root-container [padding-top] diff --git a/src/status_im/common/scan_qr_code/view.cljs b/src/status_im/common/scan_qr_code/view.cljs index 8993454abf..852ed3af93 100644 --- a/src/status_im/common/scan_qr_code/view.cljs +++ b/src/status_im/common/scan_qr_code/view.cljs @@ -174,10 +174,10 @@ :on-success-scan set-qr-code-succeeded :on-failed-scan set-rescan-timeout}))}]] [hole-view/hole-view - {:style style/hole - :holes [(assoc qr-view-finder :borderRadius 16)]} + {:style [rn/stylesheet-absolute-fill style/hole] + :holes [(assoc qr-view-finder :border-radius 16)]} [quo/blur - {:style style/absolute-fill + {:style rn/stylesheet-absolute-fill :blur-amount 10 :blur-type :transparent :overlay-color colors/neutral-80-opa-80-blur diff --git a/src/status_im/core.cljs b/src/status_im/core.cljs index 5e0456bbae..a3194935bd 100644 --- a/src/status_im/core.cljs +++ b/src/status_im/core.cljs @@ -28,6 +28,7 @@ [status-im.setup.interceptors :as interceptors] status-im.subs.root [utils.i18n :as i18n] + [utils.reagent] [status-im.setup.status-backend-client :as status-backend-client])) ;;;; re-frame RN setup @@ -35,6 +36,7 @@ (set! batching/fake-raf #(js/setTimeout % 0)) (def functional-compiler (reagent.core/create-compiler {:function-components true})) (reagent.core/set-default-compiler! functional-compiler) +(utils.reagent/set-convert-props-in-vectors!) (def adjust-resize 16) diff --git a/src/utils/reagent.cljs b/src/utils/reagent.cljs new file mode 100644 index 0000000000..3ec6dc1f3d --- /dev/null +++ b/src/utils/reagent.cljs @@ -0,0 +1,27 @@ +(ns utils.reagent + (:require [reagent.impl.template :as template] + [reagent.impl.util :as reagent.util] + [utils.transforms :as transforms])) + +(defn convert-prop-value + "Based on `reagent.impl.template/kv-conv`. + Takes the prop map of a reagent component and returns a React-valid property JS Object + by transforming kebab-case keys -> camelCase, maps -> JS Objects and vectors -> arrays. + + This version adds support to recursively transform properties inside vectors, to have a + more consistent developer experience in React Native." + [x] + (cond + (reagent.util/js-val? x) x + (reagent.util/named? x) (name x) + (map? x) (reduce-kv template/kv-conv #js {} x) + (vector? x) (transforms/map-array convert-prop-value x) + (coll? x) (clj->js x) + (ifn? x) (fn [& args] + (apply x args)) + :else (clj->js x))) + +(defn set-convert-props-in-vectors! + "We override the default reagent implementation with the one that supports vectors." + [] + (set! template/convert-prop-value convert-prop-value)) diff --git a/src/utils/transforms.cljs b/src/utils/transforms.cljs index be9be7e898..1c9c5c437d 100644 --- a/src/utils/transforms.cljs +++ b/src/utils/transforms.cljs @@ -3,9 +3,7 @@ (:require [camel-snake-kebab.core :as csk] [cljs-bean.core :as clj-bean] - [oops.core :as oops] - [reagent.impl.template :as reagent.template] - [reagent.impl.util :as reagent.util])) + [oops.core :as oops])) (defn js->clj [data] (cljs.core/js->clj data :keywordize-keys true)) @@ -67,27 +65,10 @@ (when-not (= json "undefined") (try (.parse js/JSON json) (catch js/Error _ (when (string? json) json))))) -(declare styles-with-vectors) - -(defn ^:private convert-keys-and-values - "Takes a JS Object a key and a value. - Transforms the key from a Clojure style prop to a JS style prop, using the reagent cache. - Performs a mutual recursion transformation on the value using `styles-with-vectors`. - - Based on `reagent.impl.template/kv-conv`." - [obj k v] - (doto obj - (oops/gobj-set (reagent.template/cached-prop-name k) (styles-with-vectors v)))) - -(defn styles-with-vectors - "Takes a Clojure style map or a Clojure vector of style maps and returns a JS Object - valid to use as React Native styles. - The transformation is done by performing mutual recursive calls with `convert-keys-and-values`. - - Based on `reagent.impl.template/convert-prop-value`." - [x] - (cond (reagent.util/js-val? x) x - (reagent.util/named? x) (name x) - (map? x) (reduce-kv convert-keys-and-values #js {} x) - (vector? x) (to-array (mapv styles-with-vectors x)) - :else (clj->js x))) +(defn map-array + "Performs an efficient `map` operation on `coll` but returns a JS array" + [f coll] + (let [js-array ^js (array)] + (doseq [e coll] + (.push js-array (f e))) + js-array))