Integrate support for SVG icons and fix clear icon (#15691)
This commit solves the problem described in detail in issue #15606, but in essence, it fixes the clear icon by integrating rudimentary support for SVG icons. Fixes https://github.com/status-im/status-mobile/issues/15606 - Hopefully, if SVG icons prove to be a solid solution, we can easily and progressively migrate PNG icons to SVGs, but for the moment, it was aligned with @flexsurfer https://github.com/status-im/status-mobile/issues/15606#issuecomment-1514631270 that we'll only use SVG icons on demand. - Note that it's possible to import SVGs directly via js/require by installing the library react-native-svg-transformer, but this approach is only good when we don't want/need color customization, which is rarely the case with icons where we want to change the foreground and/or background colors. I opted for rendering the SVG icon as hiccup to support color customization. - Since icons are fully memoized, the app's performance is on the same ballpark as PNGs rendered with RN Image. - It's possible to trim down SVGs by using a tool such as https://github.com/svg/svgo, but this is obviously outside the scope of this PR.
This commit is contained in:
parent
faa29a2946
commit
1e4a49fafe
|
@ -141,6 +141,7 @@ globalThis.__STATUS_MOBILE_JS_IDENTITY_PROXY__ = new Proxy({}, {get() { return (
|
||||||
{:ClipPath #js {:render identity}
|
{:ClipPath #js {:render identity}
|
||||||
:Circle #js {:render identity}
|
:Circle #js {:render identity}
|
||||||
:Defs #js {:render identity}
|
:Defs #js {:render identity}
|
||||||
|
:G #js {:render identity}
|
||||||
:Path #js {:render identity}
|
:Path #js {:render identity}
|
||||||
:Rect #js {:render identity}
|
:Rect #js {:render identity}
|
||||||
:SvgUri #js {:render identity}
|
:SvgUri #js {:render identity}
|
||||||
|
|
|
@ -1,17 +1,31 @@
|
||||||
(ns quo2.components.icon
|
(ns quo2.components.icon
|
||||||
(:require [clojure.string :as string]
|
(:require [clojure.string :as string]
|
||||||
[quo2.components.icons.icons :as icons]
|
[quo2.components.icons.icons :as icons]
|
||||||
|
[quo2.components.icons.svg :as icons.svg]
|
||||||
[quo2.foundations.colors :as colors]
|
[quo2.foundations.colors :as colors]
|
||||||
[react-native.core :as rn]))
|
[react-native.core :as rn]))
|
||||||
|
|
||||||
|
(defn- valid-color?
|
||||||
|
[color]
|
||||||
|
(or (keyword? color)
|
||||||
|
(and (string? color)
|
||||||
|
(not (string/blank? color)))))
|
||||||
|
|
||||||
(defn memo-icon-fn
|
(defn memo-icon-fn
|
||||||
([icon-name] (memo-icon-fn icon-name nil))
|
([icon-name] (memo-icon-fn icon-name nil))
|
||||||
([icon-name
|
([icon-name
|
||||||
{:keys [color container-style size
|
{:keys [color color-2 no-color
|
||||||
accessibility-label no-color]
|
container-style size accessibility-label]
|
||||||
:or {accessibility-label :icon}}]
|
:or {accessibility-label :icon}}]
|
||||||
(let [size (or size 20)]
|
(let [size (or size 20)]
|
||||||
^{:key icon-name}
|
^{:key icon-name}
|
||||||
|
(if-let [svg-icon (icons.svg/get-icon icon-name size)]
|
||||||
|
[svg-icon
|
||||||
|
{:size size
|
||||||
|
:color (when (valid-color? color) color)
|
||||||
|
:color-2 (when (valid-color? color-2) color-2)
|
||||||
|
:accessibility-label accessibility-label
|
||||||
|
:style container-style}]
|
||||||
[rn/image
|
[rn/image
|
||||||
{:style
|
{:style
|
||||||
(merge {:width size
|
(merge {:width size
|
||||||
|
@ -24,6 +38,6 @@
|
||||||
|
|
||||||
container-style)
|
container-style)
|
||||||
:accessibility-label accessibility-label
|
:accessibility-label accessibility-label
|
||||||
:source (icons/icon-source (str (name icon-name) size))}])))
|
:source (icons/icon-source (str (name icon-name) size))}]))))
|
||||||
|
|
||||||
(def icon (memoize memo-icon-fn))
|
(def icon (memoize memo-icon-fn))
|
||||||
|
|
|
@ -2,9 +2,9 @@
|
||||||
(:require [clojure.java.io :as io]
|
(:require [clojure.java.io :as io]
|
||||||
[clojure.string :as string]))
|
[clojure.string :as string]))
|
||||||
|
|
||||||
(def icon-path "./resources/images/icons2/")
|
(def ^:private icon-path "./resources/images/icons2/")
|
||||||
|
|
||||||
(defn require-icon
|
(defn- require-icon
|
||||||
[size path]
|
[size path]
|
||||||
(fn [el]
|
(fn [el]
|
||||||
(let [s (str "." path el ".png")
|
(let [s (str "." path el ".png")
|
||||||
|
@ -15,7 +15,7 @@
|
||||||
(str size))]
|
(str size))]
|
||||||
[k `(js/require ~s)])))
|
[k `(js/require ~s)])))
|
||||||
|
|
||||||
(defn get-files
|
(defn- get-files
|
||||||
[path]
|
[path]
|
||||||
(->> (io/file path)
|
(->> (io/file path)
|
||||||
file-seq
|
file-seq
|
||||||
|
@ -23,7 +23,7 @@
|
||||||
(map #(first (string/split (.getName %) #"@")))
|
(map #(first (string/split (.getName %) #"@")))
|
||||||
distinct))
|
distinct))
|
||||||
|
|
||||||
(defn get-icons
|
(defn- get-icons
|
||||||
[size]
|
[size]
|
||||||
(let [path (str icon-path size "x" size "/")]
|
(let [path (str icon-path size "x" size "/")]
|
||||||
(into {} (map (require-icon size path) (get-files path)))))
|
(into {} (map (require-icon size path) (get-files path)))))
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
(:require-macros [quo2.components.icons.icons :as icons])
|
(:require-macros [quo2.components.icons.icons :as icons])
|
||||||
(:require [taoensso.timbre :as log]))
|
(:require [taoensso.timbre :as log]))
|
||||||
|
|
||||||
(def icons (icons/resolve-icons))
|
(def ^:private icons (icons/resolve-icons))
|
||||||
|
|
||||||
(defn icon-source
|
(defn icon-source
|
||||||
[icon]
|
[icon]
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
(ns quo2.components.icons.svg
|
||||||
|
"Declare icons in this namespace when they have two possible colors, because the
|
||||||
|
ReactNative `:tint-color` prop affects all non-transparent pixels of PNGs. If
|
||||||
|
the icon has only one color, prefer a PNG.
|
||||||
|
|
||||||
|
Keep all SVG components private and expose them by name in the `icons` var."
|
||||||
|
(:require [react-native.svg :as svg]
|
||||||
|
[quo2.foundations.colors :as colors]))
|
||||||
|
|
||||||
|
(defn- container
|
||||||
|
[{:keys [size accessibility-label style]
|
||||||
|
:or {size 20}}
|
||||||
|
& children]
|
||||||
|
(into [svg/svg
|
||||||
|
{:accessibility-label accessibility-label
|
||||||
|
:style style
|
||||||
|
:width size
|
||||||
|
:height size
|
||||||
|
:view-box (str "0 0 " size " " size)
|
||||||
|
:fill :none}]
|
||||||
|
children))
|
||||||
|
|
||||||
|
(defn- clear-20
|
||||||
|
[{:keys [color color-2] :as props}]
|
||||||
|
(let [color (or color colors/neutral-100)
|
||||||
|
color-2 (or color-2 colors/white)]
|
||||||
|
[container props
|
||||||
|
[svg/path
|
||||||
|
{:d
|
||||||
|
"M3 10C3 6.13401 6.13401 3 10 3C13.866 3 17 6.13401 17 10C17 13.866 13.866 17 10 17C6.13401 17 3 13.866 3 10Z"
|
||||||
|
:fill color}]
|
||||||
|
[svg/path
|
||||||
|
{:d
|
||||||
|
"M9.15142 9.99998L7.07566 12.0757L7.9242 12.9243L9.99994 10.8485L12.0757 12.9242L12.9242 12.0757L10.8485 9.99998L12.9242 7.92421L12.0757 7.07568L9.99994 9.15145L7.92421 7.07572L7.07568 7.92425L9.15142 9.99998Z"
|
||||||
|
:fill color-2}]]))
|
||||||
|
|
||||||
|
(def ^:private icons
|
||||||
|
{:i/clear-20 clear-20})
|
||||||
|
|
||||||
|
(defn- append-to-keyword
|
||||||
|
[k & xs]
|
||||||
|
(keyword (apply str
|
||||||
|
(subs (str k) 1)
|
||||||
|
xs)))
|
||||||
|
|
||||||
|
(defn get-icon
|
||||||
|
[icon-name size]
|
||||||
|
(get icons (append-to-keyword icon-name "-" size)))
|
|
@ -28,7 +28,7 @@
|
||||||
[blur? override-theme]
|
[blur? override-theme]
|
||||||
(if blur?
|
(if blur?
|
||||||
(colors/theme-colors colors/neutral-80-opa-30 colors/white-opa-10 override-theme)
|
(colors/theme-colors colors/neutral-80-opa-30 colors/white-opa-10 override-theme)
|
||||||
(colors/theme-colors colors/neutral-40 colors/neutral-50 override-theme)))
|
(colors/theme-colors colors/neutral-40 colors/neutral-60 override-theme)))
|
||||||
|
|
||||||
(defn cursor
|
(defn cursor
|
||||||
[customization-color override-theme]
|
[customization-color override-theme]
|
||||||
|
|
|
@ -48,10 +48,6 @@
|
||||||
{:text-transform :lowercase
|
{:text-transform :lowercase
|
||||||
:color (colors/theme-colors colors/neutral-50 colors/neutral-40)})
|
:color (colors/theme-colors colors/neutral-50 colors/neutral-40)})
|
||||||
|
|
||||||
(def clear-button
|
|
||||||
{:border-color colors/danger-50
|
|
||||||
:border-width 1})
|
|
||||||
|
|
||||||
(def clear-button-container
|
(def clear-button-container
|
||||||
{:width 20
|
{:width 20
|
||||||
:height 20
|
:height 20
|
||||||
|
|
|
@ -40,7 +40,6 @@
|
||||||
:accessibility-label :button-clear-preview}
|
:accessibility-label :button-clear-preview}
|
||||||
[icon/icon :i/clear
|
[icon/icon :i/clear
|
||||||
{:size 20
|
{:size 20
|
||||||
:container-style style/clear-button
|
|
||||||
:color (colors/theme-colors colors/neutral-50 colors/neutral-60)}]])
|
:color (colors/theme-colors colors/neutral-50 colors/neutral-60)}]])
|
||||||
|
|
||||||
(defn view
|
(defn view
|
||||||
|
|
|
@ -9,3 +9,4 @@
|
||||||
(def defs (reagent/adapt-react-class Svg/Defs))
|
(def defs (reagent/adapt-react-class Svg/Defs))
|
||||||
(def circle (reagent/adapt-react-class Svg/Circle))
|
(def circle (reagent/adapt-react-class Svg/Circle))
|
||||||
(def svgxml (reagent/adapt-react-class Svg/SvgXml))
|
(def svgxml (reagent/adapt-react-class Svg/SvgXml))
|
||||||
|
(def g (reagent/adapt-react-class Svg/G))
|
||||||
|
|
|
@ -1,16 +1,9 @@
|
||||||
(ns status-im.ui2.screens.chat.composer.images.style
|
(ns status-im.ui2.screens.chat.composer.images.style)
|
||||||
(:require [quo2.foundations.colors :as colors]))
|
|
||||||
|
|
||||||
(def remove-photo-container
|
(def remove-photo-container
|
||||||
{:width 14
|
{:position :absolute
|
||||||
:height 14
|
|
||||||
:border-radius 7
|
|
||||||
:background-color colors/neutral-50
|
|
||||||
:position :absolute
|
|
||||||
:top 5
|
:top 5
|
||||||
:right 5
|
:right 5})
|
||||||
:justify-content :center
|
|
||||||
:align-items :center})
|
|
||||||
|
|
||||||
(def small-image
|
(def small-image
|
||||||
{:width 56
|
{:width 56
|
||||||
|
|
|
@ -20,7 +20,9 @@
|
||||||
:left 5
|
:left 5
|
||||||
:top 10
|
:top 10
|
||||||
:bottom 10}}
|
:bottom 10}}
|
||||||
[quo/icon :i/close {:color colors/white :size 12}]]])
|
[quo/icon :i/clear
|
||||||
|
{:size 20
|
||||||
|
:color (colors/theme-colors colors/neutral-50 colors/neutral-60)}]]])
|
||||||
|
|
||||||
(defn images-list
|
(defn images-list
|
||||||
[images]
|
[images]
|
||||||
|
|
Loading…
Reference in New Issue