From e5778ee30070fe4fe059333a1e78f90989d72db2 Mon Sep 17 00:00:00 2001 From: Jamie Caprani Date: Fri, 23 Jun 2023 13:11:50 +0100 Subject: [PATCH] feat: add new theming mechanism (#16191) * chore: set react-dom to same version as react --- package.json | 2 +- src/quo2/components/buttons/button.cljs | 10 ++--- .../components/dividers/divider_label.cljs | 15 ++++--- src/quo2/components/tabs/tab/style.cljs | 9 ++-- src/quo2/components/tabs/tab/view.cljs | 18 ++++---- src/quo2/components/tabs/tabs.cljs | 6 --- src/quo2/theme.cljs | 42 ++++++++++++++++--- src/react_native/core.cljs | 4 ++ src/status_im/bottom_sheet/sheets.cljs | 11 +++-- .../contexts/activity_center/view.cljs | 1 - .../contexts/onboarding/profiles/view.cljs | 3 +- .../shell/components/shell_screen/view.cljs | 30 ++++++------- .../contexts/syncing/setup_syncing/view.cljs | 21 +++++----- src/status_im2/navigation/options.cljs | 4 +- src/status_im2/navigation/screens.cljs | 36 +++++++++++----- src/status_im2/navigation/view.cljs | 31 ++++++++------ yarn.lock | 22 +++------- 17 files changed, 157 insertions(+), 108 deletions(-) diff --git a/package.json b/package.json index a78c92776a..80e76aff56 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "node-libs-react-native": "^1.2.1", "qrcode": "^1.4.1", "react": "18.0.0", - "react-dom": "^16.4.2", + "react-dom": "18.0.0", "react-native": "0.69.10", "react-native-background-timer": "^2.1.1", "react-native-blob-util": "^0.13.18", diff --git a/src/quo2/components/buttons/button.cljs b/src/quo2/components/buttons/button.cljs index 532874fd47..90adb98cc7 100644 --- a/src/quo2/components/buttons/button.cljs +++ b/src/quo2/components/buttons/button.cljs @@ -204,7 +204,7 @@ (when disabled {:opacity 0.3}))) -(defn button +(defn- button-internal "with label [button opts \"label\"] opts @@ -223,7 +223,7 @@ (let [pressed-in (reagent/atom false)] (fn [{:keys [on-press disabled type size before after above icon-secondary-no-color - width customization-color override-theme override-background-color pressed + width customization-color theme override-background-color pressed on-long-press accessibility-label icon icon-no-color style inner-style test-ID] :or {type :primary size 40 @@ -231,9 +231,7 @@ children] (let [{:keys [icon-color icon-secondary-color background-color label-color border-color]} (get-in (themes customization-color) - [(or - override-theme - (theme/get-theme)) type]) + [theme type]) state (cond disabled :disabled (or @pressed-in pressed) :pressed :else :default) @@ -309,3 +307,5 @@ :no-color icon-secondary-no-color :color icon-secondary-color :size icon-size}]])]]])))) + +(def button (theme/with-theme button-internal)) diff --git a/src/quo2/components/dividers/divider_label.cljs b/src/quo2/components/dividers/divider_label.cljs index c8582f4972..694711136a 100644 --- a/src/quo2/components/dividers/divider_label.cljs +++ b/src/quo2/components/dividers/divider_label.cljs @@ -1,5 +1,6 @@ (ns quo2.components.dividers.divider-label - (:require [quo2.components.icon :as icons] + (:require [quo2.theme :as theme] + [quo2.components.icon :as icons] [quo2.components.markdown.text :as markdown.text] [quo2.foundations.colors :as colors] [react-native.core :as rn])) @@ -8,7 +9,7 @@ (def chevron-icon-container-height 20) -(defn divider-label +(defn themed-view "label -> string chevron-position -> :left, :right chevron-icon -> keyword @@ -16,7 +17,8 @@ padding-bottom -> number counter-value -> number increase-padding-top? -> boolean - blur? -> boolean" + blur? -> boolean + theme -> theme value passed from with-theme HOC" [{:keys [label chevron-position chevron-icon @@ -25,8 +27,9 @@ padding-bottom blur? container-style - on-press]}] - (let [dark? (colors/dark?) + on-press + theme]}] + (let [dark? (= :dark theme) border-and-counter-bg-color (if dark? (if blur? colors/white-opa-5 colors/neutral-70) colors/neutral-10) @@ -83,3 +86,5 @@ :weight :medium :style {:color counter-text-color}} counter-value]])]])) + +(def divider-label (theme/with-theme themed-view)) diff --git a/src/quo2/components/tabs/tab/style.cljs b/src/quo2/components/tabs/tab/style.cljs index 38f4a92b95..83fd8d9cc8 100644 --- a/src/quo2/components/tabs/tab/style.cljs +++ b/src/quo2/components/tabs/tab/style.cljs @@ -1,6 +1,5 @@ (ns quo2.components.tabs.tab.style - (:require [quo2.foundations.colors :as colors] - [quo2.theme :as theme])) + (:require [quo2.foundations.colors :as colors])) (def tab-background-opacity 0.3) @@ -99,10 +98,10 @@ :label {:style {:color colors/white}}}}}) (defn by-theme - [{:keys [override-theme disabled active blur?]}] + [{:keys [disabled active blur? theme]}] (let [state (cond disabled :disabled active :active - :else :default) - theme (or override-theme (theme/get-theme))] + :else :default)] + (get-in (if blur? themes-for-blur-background themes) [theme state]))) diff --git a/src/quo2/components/tabs/tab/view.cljs b/src/quo2/components/tabs/tab/view.cljs index 7d3467132d..298e3b60de 100644 --- a/src/quo2/components/tabs/tab/view.cljs +++ b/src/quo2/components/tabs/tab/view.cljs @@ -1,8 +1,9 @@ (ns quo2.components.tabs.tab.view (:require [quo2.components.icon :as icons] [quo2.components.markdown.text :as text] - [quo2.components.tabs.tab.style :as style] [quo2.components.notifications.notification-dot :as notification-dot] + [quo2.components.tabs.tab.style :as style] + [quo2.theme :as theme] [react-native.core :as rn] [react-native.svg :as svg])) @@ -46,7 +47,7 @@ (vector? children) children)]) -(defn view +(defn- themed-view [{:keys [accessibility-label active before @@ -56,7 +57,7 @@ disabled id on-press - override-theme + theme segmented? size notification-dot?] @@ -65,11 +66,10 @@ (let [show-notification-dot? (and notification-dot? (= size 32)) {:keys [icon-color background-color - label]} - (style/by-theme {:override-theme override-theme - :blur? blur? - :disabled disabled - :active active})] + label]} (style/by-theme {:theme theme + :blur? blur? + :disabled disabled + :active active})] [rn/touchable-without-feedback (merge {:disabled disabled :accessibility-label accessibility-label} @@ -101,3 +101,5 @@ :height size :disabled disabled :background-color background-color}])]])) + +(def view (theme/with-theme themed-view)) diff --git a/src/quo2/components/tabs/tabs.cljs b/src/quo2/components/tabs/tabs.cljs index 8824927a5b..7816bf3b0e 100644 --- a/src/quo2/components/tabs/tabs.cljs +++ b/src/quo2/components/tabs/tabs.cljs @@ -65,7 +65,6 @@ flat-list-ref number-of-items on-change - override-theme scroll-on-press? size style]} @@ -80,7 +79,6 @@ :notification-dot? notification-dot? :accessibility-label accessibility-label :size size - :override-theme override-theme :blur? blur? :active (= id @active-tab-id) :on-press (fn [id] @@ -101,7 +99,6 @@ - `blur?` Boolean passed down to `quo2.components.tabs.tab/tab`. - `data` Vector of tab items. - `on-change` Callback called after a tab is selected. - - `override-theme` Passed down to `quo2.components.tabs.tab/tab`. - `size` 32/24 - `style` Style map passed to View wrapping tabs or to the FlatList when tabs are scrollable. @@ -132,7 +129,6 @@ style size blur? - override-theme in-scroll-view?] :or {fade-end-percentage fade-end-percentage fade-end? false @@ -183,7 +179,6 @@ :flat-list-ref flat-list-ref :number-of-items (count data) :on-change on-change - :override-theme override-theme :scroll-on-press? scroll-on-press? :size size :style style})})]]] @@ -195,7 +190,6 @@ :blur? blur? :number-of-items (count data) :on-change on-change - :override-theme override-theme :size size :style style} item diff --git a/src/quo2/theme.cljs b/src/quo2/theme.cljs index 7a14581cad..546aace9cb 100644 --- a/src/quo2/theme.cljs +++ b/src/quo2/theme.cljs @@ -1,19 +1,23 @@ (ns quo2.theme - (:require [reagent.core :as reagent])) + (:require [react-native.core :as rn] + ["react" :as react] + [reagent.core :as reagent] + utils.transforms)) -(def theme (reagent/atom :light)) +(defonce ^:private theme-context (react/createContext :light)) +(defonce ^:private theme-state (reagent/atom :light)) (defn dark? [] - (= :dark @theme)) + (= :dark @theme-state)) (defn get-theme [] - @theme) + @theme-state) (defn set-theme [value] - (reset! theme value)) + (reset! theme-state value)) (defn theme-value "Returns a value based on the current/override-theme theme." @@ -22,3 +26,31 @@ ([light-value dark-value override-theme] (let [theme (or override-theme (get-theme))] (if (= theme :light) light-value dark-value)))) + +(defn provider + "Wrap `children` in a React Provider using `quo2.theme/theme-context` as the + context. + + `options`: Clojure map. Currently we only use the `:theme` key. In the future + we may support other settings. + " + [options & children] + (into [:> (.-Provider theme-context) {:value options}] + children)) + +(defn use-theme + "A hook that returns the current theme context." + [] + (utils.transforms/js->clj (rn/use-context theme-context))) + +(defn ^:private f-with-theme + [component props & args] + (let [theme (-> (use-theme) :theme keyword)] + (into [component (assoc props :theme theme)] args))) + +(defn with-theme + "Create a functional component that assoc `:theme` into the first arg of + `component`. The theme value is taken from the nearest `quo2.theme/provider`." + [component] + (fn [& args] + (into [:f> f-with-theme component] args))) diff --git a/src/react_native/core.cljs b/src/react_native/core.cljs index 5ea42c5c85..1bf0825d7d 100644 --- a/src/react_native/core.cljs +++ b/src/react_native/core.cljs @@ -95,6 +95,10 @@ [ref] (oops/oget ref "current")) +(def create-context react/createContext) + +(def use-context react/useContext) + (defn use-effect ([effect-fn] (use-effect effect-fn [])) diff --git a/src/status_im/bottom_sheet/sheets.cljs b/src/status_im/bottom_sheet/sheets.cljs index 7077261bb0..4511b849b3 100644 --- a/src/status_im/bottom_sheet/sheets.cljs +++ b/src/status_im/bottom_sheet/sheets.cljs @@ -1,5 +1,6 @@ (ns status-im.bottom-sheet.sheets (:require [utils.re-frame :as rf] + [quo2.theme :as theme] [status-im.ui.screens.about-app.views :as about-app] [status-im.ui.screens.keycard.views :as keycard] [status-im.ui.screens.mobile-network-settings.view :as mobile-network-settings] @@ -28,7 +29,8 @@ (merge keycard/more-sheet) (= view :learn-more) - (merge about-app/learn-more))] + (merge about-app/learn-more)) + page-theme (:theme options)] [:f> (fn [] @@ -36,6 +38,7 @@ (rn/hw-back-add-listener dismiss-bottom-sheet-callback) (fn [] (rn/hw-back-remove-listener dismiss-bottom-sheet-callback)))) - [bottom-sheet/bottom-sheet opts - (when content - [content (when options options)])])])) + [theme/provider {:theme (or page-theme (theme/get-theme))} + [bottom-sheet/bottom-sheet opts + (when content + [content (when options options)])]])])) diff --git a/src/status_im2/contexts/activity_center/view.cljs b/src/status_im2/contexts/activity_center/view.cljs index 7ecef45a40..765fe971fa 100644 --- a/src/status_im2/contexts/activity_center/view.cljs +++ b/src/status_im2/contexts/activity_center/view.cljs @@ -83,7 +83,6 @@ {:size 32 :scrollable? true :blur? true - :override-theme :dark :style style/tabs :fade-end-percentage 0.79 :scroll-on-press? true diff --git a/src/status_im2/contexts/onboarding/profiles/view.cljs b/src/status_im2/contexts/onboarding/profiles/view.cljs index 886651d2c3..34899dfbcf 100644 --- a/src/status_im2/contexts/onboarding/profiles/view.cljs +++ b/src/status_im2/contexts/onboarding/profiles/view.cljs @@ -120,8 +120,7 @@ :size 32 :icon true :on-press show-new-account-options - :accessibility-label :show-new-account-options - :override-theme :dark} + :accessibility-label :show-new-account-options} :main-icons/add]] [rn/flat-list {:data (sort-by :timestamp > profiles-data) diff --git a/src/status_im2/contexts/shell/components/shell_screen/view.cljs b/src/status_im2/contexts/shell/components/shell_screen/view.cljs index f8d1081350..64c01d22d0 100644 --- a/src/status_im2/contexts/shell/components/shell_screen/view.cljs +++ b/src/status_im2/contexts/shell/components/shell_screen/view.cljs @@ -2,6 +2,7 @@ (:require [utils.i18n :as i18n] [quo2.core :as quo] [quo2.foundations.colors :as colors] + [quo2.theme :as theme] [utils.re-frame :as rf] [react-native.core :as rn] [react-native.blur :as blur] @@ -98,17 +99,18 @@ width (rf/sub [:dimensions/window-width]) top (safe-area/get-top) shell-margin (/ (- width (* 2 shell.constants/switcher-card-size)) 3)] - [rn/view - {:style {:top 0 - :left 0 - :right 0 - :bottom -1 - :position :absolute - :background-color colors/neutral-100}} - [jump-to-list switcher-cards shell-margin] - [top-nav-blur-overlay top] - [common.home/top-nav - {:type :shell - :avatar {:customization-color customization-color} - :style {:margin-top top - :z-index 2}}]])) + [theme/provider {:theme :dark} + [rn/view + {:style {:top 0 + :left 0 + :right 0 + :bottom -1 + :position :absolute + :background-color colors/neutral-100}} + [jump-to-list switcher-cards shell-margin] + [top-nav-blur-overlay top] + [common.home/top-nav + {:type :shell + :avatar {:customization-color customization-color} + :style {:margin-top top + :z-index 2}}]]])) diff --git a/src/status_im2/contexts/syncing/setup_syncing/view.cljs b/src/status_im2/contexts/syncing/setup_syncing/view.cljs index 86c611ac69..95d5c8ec25 100644 --- a/src/status_im2/contexts/syncing/setup_syncing/view.cljs +++ b/src/status_im2/contexts/syncing/setup_syncing/view.cljs @@ -118,17 +118,16 @@ :default-shown? true :editable false}] [quo/button - {:on-press (fn [] - (clipboard/set-string @code) - (rf/dispatch [:toasts/upsert - {:icon :correct - :icon-color colors/success-50 - :text (i18n/label - :t/sharing-copied-to-clipboard)}])) - :override-theme :dark - :type :grey - :style {:margin-top 12} - :before :i/copy} + {:on-press (fn [] + (clipboard/set-string @code) + (rf/dispatch [:toasts/upsert + {:icon :correct + :icon-color colors/success-50 + :text (i18n/label + :t/sharing-copied-to-clipboard)}])) + :type :grey + :style {:margin-top 12} + :before :i/copy} (i18n/label :t/copy-qr)]])]] [rn/view {:style style/sync-code} [quo/divider-label diff --git a/src/status_im2/navigation/options.cljs b/src/status_im2/navigation/options.cljs index 69e6ec5e30..70931833c0 100644 --- a/src/status_im2/navigation/options.cljs +++ b/src/status_im2/navigation/options.cljs @@ -73,6 +73,7 @@ (def transparent-screen-options (merge {:modalPresentationStyle :overCurrentContext + :theme :dark :layout {:componentBackgroundColor :transparent :orientation ["portrait"] :backgroundColor :transparent}} @@ -96,7 +97,8 @@ (def dark-screen (merge (statusbar true) - {:layout {:componentBackgroundColor colors/neutral-95 + {:theme :dark + :layout {:componentBackgroundColor colors/neutral-95 :orientation ["portrait"] :backgroundColor colors/neutral-95}})) diff --git a/src/status_im2/navigation/screens.cljs b/src/status_im2/navigation/screens.cljs index 28b48a2f9a..418058e575 100644 --- a/src/status_im2/navigation/screens.cljs +++ b/src/status_im2/navigation/screens.cljs @@ -81,7 +81,8 @@ :component add-new-contact/new-contact} {:name :how-to-pair - :options {:sheet? true} + :options {:theme :dark + :sheet? true} :component how-to-pair/instructions} {:name :discover-communities @@ -100,31 +101,38 @@ ;; Onboarding {:name :intro + :options {:theme :dark} :component intro/view} {:name :profiles - :options {:layout options/onboarding-layout} + :options {:theme :dark + :layout options/onboarding-layout} :component profiles/views} {:name :new-to-status - :options {:layout options/onboarding-layout} + :options {:theme :dark + :layout options/onboarding-layout} :component new-to-status/new-to-status} {:name :create-profile - :options {:layout options/onboarding-layout} + :options {:theme :dark + :layout options/onboarding-layout} :component create-profile/create-profile} {:name :create-profile-password - :options {:insets {:top false} + :options {:theme :dark + :insets {:top false} :layout options/onboarding-layout} :component create-password/create-password} {:name :enable-biometrics - :options {:layout options/onboarding-layout} + :options {:theme :dark + :layout options/onboarding-layout} :component enable-biometrics/enable-biometrics} {:name :generating-keys - :options {:layout options/onboarding-layout + :options {:theme :dark + :layout options/onboarding-layout :popGesture false :hardwareBackButton {:dismissModalOnPress false :popStackOnPress false}} @@ -135,7 +143,8 @@ :component enter-seed-phrase/enter-seed-phrase} {:name :enable-notifications - :options {:layout options/onboarding-layout + :options {:theme :dark + :layout options/onboarding-layout :popGesture false :hardwareBackButton {:dismissModalOnPress false :popStackOnPress false}} @@ -143,11 +152,13 @@ {:name :identifiers :component identifiers/view - :options {:layout options/onboarding-layout + :options {:theme :dark + :layout options/onboarding-layout :popGesture false :hardwareBackButton {:dismissModalOnPress false :popStackOnPress false}}} {:name :scan-sync-code-page + :options {:theme :dark} :component scan-sync-code-page/view} {:name :sign-in @@ -155,15 +166,18 @@ :component sign-in/view} {:name :syncing-progress - :options {:layout options/onboarding-layout + :options {:theme :dark + :layout options/onboarding-layout :popGesture false} :component syncing-devices/view} {:name :syncing-results + :options {:theme :dark} :component syncing-results/view} {:name :welcome - :options {:layout options/onboarding-layout} + :options {:theme :dark + :layout options/onboarding-layout} :component welcome/view}] (when config/quo-preview-enabled? diff --git a/src/status_im2/navigation/view.cljs b/src/status_im2/navigation/view.cljs index c39bb31dc3..95cc514c7e 100644 --- a/src/status_im2/navigation/view.cljs +++ b/src/status_im2/navigation/view.cljs @@ -14,7 +14,8 @@ [status-im2.navigation.screens :as screens] [status-im2.setup.hot-reload :as reloader] [utils.re-frame :as rf] - [status-im2.common.bottom-sheet-screen.view :as bottom-sheet-screen])) + [status-im2.common.bottom-sheet-screen.view :as bottom-sheet-screen] + [quo2.theme :as theme])) (defn get-screens [] @@ -53,15 +54,17 @@ [key] (reagent.core/reactify-component (fn [] - (let [{:keys [component options]} - (get (if js/goog.DEBUG (get-screens) screens) (keyword key)) ;; needed for hot reload - {:keys [insets sheet?]} options - background-color (or (get-in options [:layout :backgroundColor]) - (when sheet? :transparent))] + (let [{:keys [component options]} (get (if js/goog.DEBUG + (get-screens) + screens) + (keyword key)) + {:keys [insets sheet? theme]} options + user-theme (theme/get-theme) + background-color (or (get-in options [:layout :backgroundColor]) + (when sheet? :transparent))] ^{:key (str "root" key @reloader/cnt)} - [:<> - [rn/view - {:style (wrapped-screen-style insets background-color)} + [theme/provider {:theme (or theme user-theme)} + [rn/view {:style (wrapped-screen-style insets background-color)} [inactive] (if sheet? [:f> bottom-sheet-screen/f-view component] @@ -72,11 +75,13 @@ (def bottom-sheet (reagent/reactify-component (fn [] - (let [{:keys [sheets hide?]} (rf/sub [:bottom-sheet]) - sheet (last sheets) - insets (safe-area/get-insets)] + (let [{:keys [sheets hide? theme]} (rf/sub [:bottom-sheet]) + sheet (last sheets) + insets (safe-area/get-insets) + user-theme (theme/get-theme)] ^{:key (str "sheet" @reloader/cnt)} - [:<> + (js/console.log "HEHREHRE") + [theme/provider {:theme (or theme user-theme)} [inactive] [rn/keyboard-avoiding-view {:style {:position :relative :flex 1} diff --git a/yarn.lock b/yarn.lock index a384d3dbb0..76fa6338c3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8768,7 +8768,7 @@ prompts@^2.4.0: kleur "^3.0.3" sisteransi "^1.0.5" -prop-types@15.x.x, prop-types@^15.6.2, prop-types@^15.7.2: +prop-types@15.x.x, prop-types@^15.7.2: version "15.7.2" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ== @@ -8931,15 +8931,13 @@ react-devtools-core@4.24.0: shell-quote "^1.6.1" ws "^7" -react-dom@^16.4.2: - version "16.13.1" - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.13.1.tgz#c1bd37331a0486c078ee54c4740720993b2e0e7f" - integrity sha512-81PIMmVLnCNLO/fFOQxdQkvEq/+Hfpv24XNJfpyZhTRfO0QcmQIF/PgCa1zCOj2w1hrn12MFLyaJ/G0+Mxtfag== +react-dom@18.0.0: + version "18.0.0" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.0.0.tgz#26b88534f8f1dbb80853e1eabe752f24100d8023" + integrity sha512-XqX7uzmFo0pUceWFCt7Gff6IyIMzFUn7QMZrbrQfGxtaxXZIcGQzoNpRLE3fQLnS4XzLLPMZX2T9TRcSrasicw== dependencies: loose-envify "^1.1.0" - object-assign "^4.1.1" - prop-types "^15.6.2" - scheduler "^0.19.1" + scheduler "^0.21.0" react-is@^16.12.0, react-is@^16.7.0, react-is@^16.8.1: version "16.13.1" @@ -9762,14 +9760,6 @@ saxes@^5.0.1: dependencies: xmlchars "^2.2.0" -scheduler@^0.19.1: - version "0.19.1" - resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.19.1.tgz#4f3e2ed2c1a7d65681f4c854fa8c5a1ccb40f196" - integrity sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA== - dependencies: - loose-envify "^1.1.0" - object-assign "^4.1.1" - scheduler@^0.21.0: version "0.21.0" resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.21.0.tgz#6fd2532ff5a6d877b6edb12f00d8ab7e8f308820"