[#17964] emoji screen performance (#18213)

* Fix `->` style in emoji-picker.data

* Make `temp-empty-symbol` component code safer to avoid app crashing

* fix `->` style in `emoji-picker.utils`

* Pass sheet-animating? ratom to bottom-sheet's content component

* Improve emoji picker performance
This commit is contained in:
Ulises Manuel 2023-12-21 15:44:33 -06:00 committed by GitHub
parent 4f9544d20c
commit 5fa9c0cab6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 110 additions and 100 deletions

View File

@ -46,7 +46,10 @@
:border-color :grey} :border-color :grey}
size)} size)}
[quo/text {:style {:color :grey}} [quo/text {:style {:color :grey}}
(string/capitalize (first (name token)))]]) (some-> token
name
first
string/capitalize)]])
(defn view-internal (defn view-internal
"Render a token image. "Render a token image.

View File

@ -2,8 +2,15 @@
(:require (:require
[quo.foundations.colors :as colors] [quo.foundations.colors :as colors]
[quo.theme :as theme] [quo.theme :as theme]
[react-native.platform :as platform]
[react-native.reanimated :as reanimated])) [react-native.reanimated :as reanimated]))
(defn container
[{:keys [top] :as _insets}]
(let [padding-top (if platform/ios? top (+ top 10))]
{:flex 1
:padding-top padding-top}))
(defn background (defn background
[opacity] [opacity]
(reanimated/apply-animations-to-style (reanimated/apply-animations-to-style

View File

@ -15,29 +15,26 @@
(def ^:const drag-threshold 200) (def ^:const drag-threshold 200)
(defn drag-gesture (defn drag-gesture
[translate-y opacity scroll-enabled curr-scroll close] [{:keys [translate-y opacity scroll-enabled curr-scroll close reset-open-sheet set-animating-true]}]
(-> (-> (gesture/gesture-pan)
(gesture/gesture-pan) (gesture/on-start (fn [e]
(gesture/on-start (fn [e] (set-animating-true)
(when (< (oops/oget e "velocityY") 0) (when (< (oops/oget e "velocityY") 0)
(reset! scroll-enabled true)))) (reset! scroll-enabled true))))
(gesture/on-update (fn [e] (gesture/on-update (fn [e]
(let [translation (oops/oget e "translationY") (let [translation (oops/oget e "translationY")
progress (Math/abs (/ translation drag-threshold))] progress (Math/abs (/ translation drag-threshold))]
(when (pos? translation) (when (pos? translation)
(reanimated/set-shared-value translate-y translation) (reanimated/set-shared-value translate-y translation)
(reanimated/set-shared-value opacity (- 1 (/ progress 5))))))) (reanimated/set-shared-value opacity (- 1 (/ progress 5)))))))
(gesture/on-end (fn [e] (gesture/on-end (fn [e]
(if (> (oops/oget e "translationY") drag-threshold) (if (> (oops/oget e "translationY") drag-threshold)
(close) (close)
(do (reset-open-sheet))))
(reanimated/animate translate-y 0 300) (gesture/on-finalize (fn [e]
(reanimated/animate opacity 1 300) (when (and (>= (oops/oget e "velocityY") 0)
(reset! scroll-enabled true))))) (<= @curr-scroll (if platform/ios? -1 0)))
(gesture/on-finalize (fn [e] (reset! scroll-enabled false))))))
(when (and (>= (oops/oget e "velocityY") 0)
(<= @curr-scroll (if platform/ios? -1 0)))
(reset! scroll-enabled false))))))
(defn on-scroll (defn on-scroll
[e curr-scroll] [e curr-scroll]
@ -46,40 +43,54 @@
(defn- f-view (defn- f-view
[_] [_]
(let [scroll-enabled (reagent/atom true) (let [scroll-enabled (reagent/atom true)
curr-scroll (reagent/atom 0)] curr-scroll (reagent/atom 0)
animating? (reagent/atom true)
set-animating-true #(reset! animating? true)
set-animating-false (fn [ms]
(js/setTimeout #(reset! animating? false) ms))]
(fn [{:keys [content skip-background? theme]}] (fn [{:keys [content skip-background? theme]}]
(let [insets (safe-area/get-insets) (let [insets (safe-area/get-insets)
{:keys [height]} (rn/get-window) {:keys [height]} (rn/get-window)
padding-top (:top insets)
padding-top (if platform/ios? padding-top (+ padding-top 10))
opacity (reanimated/use-shared-value 0) opacity (reanimated/use-shared-value 0)
translate-y (reanimated/use-shared-value height) translate-y (reanimated/use-shared-value height)
close (fn [] close (fn []
(set-animating-true)
(reanimated/animate translate-y height 300) (reanimated/animate translate-y height 300)
(reanimated/animate opacity 0 300) (reanimated/animate opacity 0 300)
(rf/dispatch [:navigate-back]))] (rf/dispatch [:navigate-back]))
reset-open-sheet (fn []
(reanimated/animate translate-y 0 300)
(reanimated/animate opacity 1 300)
(set-animating-false 300)
(reset! scroll-enabled true))]
(rn/use-effect (rn/use-effect
(fn [] (fn []
(reanimated/animate translate-y 0 300) (reanimated/animate translate-y 0 300)
(reanimated/animate opacity 1 300))) (reanimated/animate opacity 1 300)
(set-animating-false 300)))
(hooks/use-back-handler close) (hooks/use-back-handler close)
[rn/view [rn/view {:style (style/container insets)}
{:style {:flex 1
:padding-top padding-top}}
(when-not skip-background? (when-not skip-background?
[reanimated/view {:style (style/background opacity)}]) [reanimated/view {:style (style/background opacity)}])
[gesture/gesture-detector [gesture/gesture-detector
{:gesture (drag-gesture translate-y opacity scroll-enabled curr-scroll close)} {:gesture (drag-gesture {:translate-y translate-y
:opacity opacity
:scroll-enabled scroll-enabled
:curr-scroll curr-scroll
:close close
:reset-open-sheet reset-open-sheet
:set-animating-true set-animating-true})}
[reanimated/view {:style (style/main-view translate-y theme)} [reanimated/view {:style (style/main-view translate-y theme)}
[rn/view {:style style/handle-container} [rn/view {:style style/handle-container}
[rn/view {:style (style/handle theme)}]] [rn/view {:style (style/handle theme)}]]
[content [content
{:insets insets {:insets insets
:close close :close close
:scroll-enabled scroll-enabled :scroll-enabled scroll-enabled
:current-scroll curr-scroll :current-scroll curr-scroll
:on-scroll #(on-scroll % curr-scroll)}]]]])))) :on-scroll #(on-scroll % curr-scroll)
:sheet-animating? animating?}]]]]))))
(defn- internal-view (defn- internal-view
[params] [params]

View File

@ -85,7 +85,7 @@
(def ^:private categorized-and-partitioned (def ^:private categorized-and-partitioned
(->> emoji-data (->> emoji-data
(reduce (fn [acc {:keys [group] :as emoji}] (reduce (fn [acc {:keys [group] :as emoji}]
(update-in acc [(-> (emoji-group->category group) :index) :data] conj emoji)) (update-in acc [(-> group emoji-group->category :index) :data] conj emoji))
categories) categories)
(reduce (fn [acc {:keys [data] :as item}] (reduce (fn [acc {:keys [data] :as item}]
(conj acc (assoc item :data (partition-all constants/emojis-per-row data)))) (conj acc (assoc item :data (partition-all constants/emojis-per-row data))))

View File

@ -7,14 +7,14 @@
(defn search-emoji (defn search-emoji
[search-query] [search-query]
(let [cleaned-query (string/lower-case (string/trim search-query))] (let [cleaned-query (string/lower-case (string/trim search-query))]
(->> (filter (fn [{:keys [label tags emoticon]}] (->> emoji-data
(filter (fn [{:keys [label tags emoticon]}]
(or (string/includes? label cleaned-query) (or (string/includes? label cleaned-query)
(when emoticon (when emoticon
(if (vector? emoticon) (if (vector? emoticon)
(some #(string/includes? (string/lower-case %) cleaned-query) emoticon) (some #(string/includes? (string/lower-case %) cleaned-query) emoticon)
(string/includes? (string/lower-case emoticon) cleaned-query))) (string/includes? (string/lower-case emoticon) cleaned-query)))
(some #(string/includes? % cleaned-query) tags))) (some #(string/includes? % cleaned-query) tags))))
emoji-data)
(partition-all constants/emojis-per-row)))) (partition-all constants/emojis-per-row))))
(defn random-emoji (defn random-emoji

View File

@ -16,8 +16,7 @@
[status-im.contexts.emoji-picker.utils :as emoji-picker.utils] [status-im.contexts.emoji-picker.utils :as emoji-picker.utils]
[utils.debounce :as debounce] [utils.debounce :as debounce]
[utils.i18n :as i18n] [utils.i18n :as i18n]
[utils.re-frame :as rf] [utils.re-frame :as rf]))
[utils.transforms :as transforms]))
(defn- on-press-category (defn- on-press-category
[{:keys [id index active-category scroll-ref]}] [{:keys [id index active-category scroll-ref]}]
@ -30,16 +29,14 @@
(defn- handle-on-viewable-items-changed (defn- handle-on-viewable-items-changed
[{:keys [event active-category should-update-active-category?]}] [{:keys [event active-category should-update-active-category?]}]
(when should-update-active-category? (when should-update-active-category?
(let [viewable-item (-> (oops/oget event "viewableItems") (let [viewable-item (some-> event
transforms/js->clj (oops/oget "viewableItems")
first (aget 0)
:item) (oops/oget "item"))
header? (and (map? viewable-item) (:header? viewable-item)) header? (and (map? viewable-item) (:header? viewable-item))
section-key (if header? section-key (if header?
(:id viewable-item) (:id viewable-item)
(:id (emoji-picker.data/emoji-group->category (-> viewable-item (-> viewable-item first :group emoji-picker.data/emoji-group->category :id))]
first
:group))))]
(when (and (some? section-key) (not= @active-category section-key)) (when (and (some? section-key) (not= @active-category section-key))
(reset! active-category section-key))))) (reset! active-category section-key)))))
@ -77,11 +74,10 @@
(defn- emoji-row (defn- emoji-row
[row-data {:keys [on-select close]}] [row-data {:keys [on-select close]}]
(into [rn/view {:style style/emoji-row-container}] (into [rn/view {:style style/emoji-row-container}]
(map-indexed (map-indexed (fn [col-index {:keys [hexcode] :as emoji}]
(fn [col-index {:keys [hexcode] :as emoji}] ^{:key hexcode}
^{:key hexcode} [emoji-item emoji col-index on-select close]))
[emoji-item emoji col-index on-select close]) row-data))
row-data)))
(defn- render-item (defn- render-item
[item _ _ render-data] [item _ _ render-data]
@ -98,30 +94,30 @@
:container-style style/empty-results}]) :container-style style/empty-results}])
(defn- render-list (defn- render-list
[{:keys [theme filtered-data on-viewable-items-changed scroll-enabled on-scroll on-select [{:keys [theme filtered-data on-viewable-items-changed scroll-enabled on-scroll
set-scroll-ref close]}] on-select set-scroll-ref close sheet-animating?]}]
(let [data (if filtered-data filtered-data emoji-picker.data/flatten-data)] [gesture/flat-list
[gesture/flat-list {:ref set-scroll-ref
{:ref set-scroll-ref :scroll-enabled @scroll-enabled
:scroll-enabled @scroll-enabled :data (or filtered-data emoji-picker.data/flatten-data)
:data data :initial-num-to-render 14
:initial-num-to-render 20 :max-to-render-per-batch 10
:max-to-render-per-batch 20 :render-fn render-item
:render-fn render-item :get-item-layout get-item-layout
:get-item-layout get-item-layout :keyboard-dismiss-mode :on-drag
:keyboard-dismiss-mode :on-drag :keyboard-should-persist-taps :handled
:keyboard-should-persist-taps :handled :shows-vertical-scroll-indicator false
:shows-vertical-scroll-indicator false :on-scroll-to-index-failed identity
:on-scroll-to-index-failed identity :empty-component [empty-result]
:empty-component [empty-result] :on-scroll on-scroll
:on-scroll on-scroll :render-data {:close close
:render-data {:close close :theme theme
:theme theme :on-select on-select}
:on-select on-select} :content-container-style style/list-container
:content-container-style style/list-container :viewability-config {:item-visible-percent-threshold 100
:viewability-config {:item-visible-percent-threshold 100 :minimum-view-time 200}
:minimum-view-time 200} :on-viewable-items-changed on-viewable-items-changed
:on-viewable-items-changed on-viewable-items-changed}])) :window-size (if @sheet-animating? 1 10)}])
(defn- footer (defn- footer
[{:keys [theme active-category scroll-ref]}] [{:keys [theme active-category scroll-ref]}]
@ -152,12 +148,9 @@
(reset! search-text "")) (reset! search-text ""))
(defn f-view (defn f-view
[{:keys [render-emojis? search-text on-change-text clear-states active-category scroll-ref theme] [{:keys [search-text on-change-text clear-states active-category scroll-ref theme]
:as sheet-opts}] :as sheet-opts}]
(let [search-active? (pos? (count @search-text))] (let [search-active? (pos? (count @search-text))]
;; rendering emojis is heavy on the UI thread, we need to delay rendering until the navigation
;; animation completes. 250 is based on the default 300ms navigation duration.
(rn/use-effect #(js/setTimeout (fn [] (reset! render-emojis? true)) 250))
[rn/keyboard-avoiding-view [rn/keyboard-avoiding-view
{:style style/flex-spacer {:style style/flex-spacer
:keyboard-vertical-offset 8} :keyboard-vertical-offset 8}
@ -171,8 +164,7 @@
:on-change-text on-change-text :on-change-text on-change-text
:clearable? search-active? :clearable? search-active?
:on-clear clear-states}]] :on-clear clear-states}]]
(when @render-emojis? [render-list sheet-opts]
[render-list sheet-opts])
(when-not search-active? (when-not search-active?
[footer [footer
{:theme theme {:theme theme
@ -186,7 +178,6 @@
set-scroll-ref #(reset! scroll-ref %) set-scroll-ref #(reset! scroll-ref %)
search-text (reagent/atom "") search-text (reagent/atom "")
filtered-data (reagent/atom nil) filtered-data (reagent/atom nil)
render-emojis? (reagent/atom false)
active-category (reagent/atom constants/default-category) active-category (reagent/atom constants/default-category)
clear-states #(clear {:active-category active-category clear-states #(clear {:active-category active-category
:filtered-data filtered-data :filtered-data filtered-data
@ -210,17 +201,15 @@
:should-update-active-category? (nil? @filtered-data)}))] :should-update-active-category? (nil? @filtered-data)}))]
(fn [sheet-opts] (fn [sheet-opts]
[:f> f-view [:f> f-view
(merge sheet-opts (assoc sheet-opts
{:render-emojis? render-emojis? :search-text search-text
:search-text search-text :on-change-text on-change-text
:on-change-text on-change-text :clear-states clear-states
:clear-states clear-states :filtered-data @filtered-data
:filtered-data @filtered-data :set-scroll-ref set-scroll-ref
:set-scroll-ref set-scroll-ref :on-select on-select
:on-select on-select :on-viewable-items-changed on-viewable-items-changed
:on-viewable-items-changed on-viewable-items-changed :active-category active-category
:active-category active-category :scroll-ref scroll-ref)])))
:scroll-ref scroll-ref})])))
(def view (quo.theme/with-theme view-internal)) (def view (quo.theme/with-theme view-internal))