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:
Icaro Motta 2023-04-24 14:28:19 -03:00 committed by GitHub
parent faa29a2946
commit 1e4a49fafe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 92 additions and 38 deletions

View File

@ -141,6 +141,7 @@ globalThis.__STATUS_MOBILE_JS_IDENTITY_PROXY__ = new Proxy({}, {get() { return (
{:ClipPath #js {:render identity}
:Circle #js {:render identity}
:Defs #js {:render identity}
:G #js {:render identity}
:Path #js {:render identity}
:Rect #js {:render identity}
:SvgUri #js {:render identity}

View File

@ -1,29 +1,43 @@
(ns quo2.components.icon
(:require [clojure.string :as string]
[quo2.components.icons.icons :as icons]
[quo2.components.icons.svg :as icons.svg]
[quo2.foundations.colors :as colors]
[react-native.core :as rn]))
(defn- valid-color?
[color]
(or (keyword? color)
(and (string? color)
(not (string/blank? color)))))
(defn memo-icon-fn
([icon-name] (memo-icon-fn icon-name nil))
([icon-name
{:keys [color container-style size
accessibility-label no-color]
{:keys [color color-2 no-color
container-style size accessibility-label]
:or {accessibility-label :icon}}]
(let [size (or size 20)]
^{:key icon-name}
[rn/image
{:style
(merge {:width size
:height size}
(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
{:style
(merge {:width size
:height size}
(when (not no-color)
{:tint-color (if (and (string? color) (not (string/blank? color)))
color
(colors/theme-colors colors/neutral-100 colors/white))})
(when (not no-color)
{:tint-color (if (and (string? color) (not (string/blank? color)))
color
(colors/theme-colors colors/neutral-100 colors/white))})
container-style)
:accessibility-label accessibility-label
:source (icons/icon-source (str (name icon-name) size))}])))
container-style)
:accessibility-label accessibility-label
:source (icons/icon-source (str (name icon-name) size))}]))))
(def icon (memoize memo-icon-fn))

View File

@ -2,9 +2,9 @@
(:require [clojure.java.io :as io]
[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]
(fn [el]
(let [s (str "." path el ".png")
@ -15,7 +15,7 @@
(str size))]
[k `(js/require ~s)])))
(defn get-files
(defn- get-files
[path]
(->> (io/file path)
file-seq
@ -23,7 +23,7 @@
(map #(first (string/split (.getName %) #"@")))
distinct))
(defn get-icons
(defn- get-icons
[size]
(let [path (str icon-path size "x" size "/")]
(into {} (map (require-icon size path) (get-files path)))))

View File

@ -2,7 +2,7 @@
(:require-macros [quo2.components.icons.icons :as icons])
(:require [taoensso.timbre :as log]))
(def icons (icons/resolve-icons))
(def ^:private icons (icons/resolve-icons))
(defn icon-source
[icon]

View File

@ -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)))

View File

@ -28,7 +28,7 @@
[blur? override-theme]
(if blur?
(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
[customization-color override-theme]

View File

@ -48,10 +48,6 @@
{:text-transform :lowercase
: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
{:width 20
:height 20

View File

@ -39,9 +39,8 @@
:hit-slop {:top 3 :right 3 :bottom 3 :left 3}
:accessibility-label :button-clear-preview}
[icon/icon :i/clear
{:size 20
:container-style style/clear-button
:color (colors/theme-colors colors/neutral-50 colors/neutral-60)}]])
{:size 20
:color (colors/theme-colors colors/neutral-50 colors/neutral-60)}]])
(defn view
[{:keys [title body logo on-clear loading? loading-message container-style]}]

View File

@ -9,3 +9,4 @@
(def defs (reagent/adapt-react-class Svg/Defs))
(def circle (reagent/adapt-react-class Svg/Circle))
(def svgxml (reagent/adapt-react-class Svg/SvgXml))
(def g (reagent/adapt-react-class Svg/G))

View File

@ -1,16 +1,9 @@
(ns status-im.ui2.screens.chat.composer.images.style
(:require [quo2.foundations.colors :as colors]))
(ns status-im.ui2.screens.chat.composer.images.style)
(def remove-photo-container
{:width 14
:height 14
:border-radius 7
:background-color colors/neutral-50
:position :absolute
:top 5
:right 5
:justify-content :center
:align-items :center})
{:position :absolute
:top 5
:right 5})
(def small-image
{:width 56

View File

@ -20,7 +20,9 @@
:left 5
:top 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
[images]