From 581c50b5fffe0722745bbbffb456242d13a5529b Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Fri, 22 Apr 2022 22:14:37 +0200 Subject: [PATCH 1/5] :zap: Improved copy objects performance --- common/src/app/common/pages/helpers.cljc | 17 ++++++++++ frontend/src/app/main/data/workspace.cljs | 38 ++++++----------------- 2 files changed, 26 insertions(+), 29 deletions(-) diff --git a/common/src/app/common/pages/helpers.cljc b/common/src/app/common/pages/helpers.cljc index 42a4c76ac..63f693195 100644 --- a/common/src/app/common/pages/helpers.cljc +++ b/common/src/app/common/pages/helpers.cljc @@ -502,3 +502,20 @@ (reduce process-shape (transient {})) (persistent!)) persistent!))) + +(defn selected-subtree + "Given a set of shapes, returns an objects subtree with the parents + of the selected items up to the root. Useful to calculate a partial z-index" + [objects selected] + + (let [selected+parents + (into selected + (mapcat #(get-parent-ids objects %)) + selected) + + remove-children + (fn [shape] + (update shape :shapes #(filterv selected+parents %)))] + + (-> (select-keys objects selected+parents) + (d/update-vals remove-children)))) diff --git a/frontend/src/app/main/data/workspace.cljs b/frontend/src/app/main/data/workspace.cljs index d9c38e714..62cf0adba 100644 --- a/frontend/src/app/main/data/workspace.cljs +++ b/frontend/src/app/main/data/workspace.cljs @@ -49,7 +49,6 @@ [app.main.data.workspace.zoom :as dwz] [app.main.repo :as rp] [app.main.streams :as ms] - [app.main.worker :as uw] [app.util.dom :as dom] [app.util.globals :as ug] [app.util.http :as http] @@ -1193,28 +1192,14 @@ (defn copy-selected [] - (letfn [;; Sort objects so they have the same relative ordering - ;; when pasted later. - (sort-selected-async [state data] - (let [selected (wsh/lookup-selected state) - objects (wsh/lookup-page-objects state) - page-id (:current-page-id state)] - (->> (uw/ask! {:cmd :selection/query-z-index - :page-id page-id - :objects objects - :ids selected}) - (rx/map (fn [z-indexes] - (assoc data :selected - (->> (d/zip selected z-indexes) - (sort-by second) - (map first) - (into (d/ordered-set))))))))) - - ;; We cannot call to a remote procedure in Safari (for the copy) so we need - ;; to calculate it here instead of on the worker - (sort-selected-sync [state data] + (letfn [(sort-selected [state data] (let [selected (wsh/lookup-selected state) objects (wsh/lookup-page-objects state) + + ;; Narrow the objects map so it contains only relevant data for + ;; selected and its parents + objects (cph/selected-subtree objects selected) + z-index (cp/calculate-z-index objects) z-values (->> selected (map #(vector % @@ -1289,18 +1274,13 @@ :file-id (:current-file-id state) :selected selected :objects {} - :images #{}} + :images #{}}] + - sort-results - (fn [obs] - ;; Safari doesn't allow asynchronous sorting on the copy - (if (cfg/check-browser? :safari) - (rx/map (partial sort-selected-sync state) obs) - (rx/mapcat (partial sort-selected-async state) obs)))] (->> (rx/from (seq (vals pdata))) (rx/merge-map (partial prepare-object objects selected)) (rx/reduce collect-data initial) - (sort-results) + (rx/map (partial sort-selected state)) (rx/map t/encode-str) (rx/map wapi/write-to-clipboard) (rx/catch on-copy-error) From 6ad591eb23459d3914040f62a2586ac6a265eb97 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Fri, 22 Apr 2022 22:14:51 +0200 Subject: [PATCH 2/5] :bug: Fix problem with export texts and fonts --- frontend/src/app/main/render.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/app/main/render.cljs b/frontend/src/app/main/render.cljs index b170a2c08..8e3e77da5 100644 --- a/frontend/src/app/main/render.cljs +++ b/frontend/src/app/main/render.cljs @@ -402,7 +402,7 @@ :style {:-webkit-print-color-adjust :exact} :fill "none"} - (let [fonts (ff/frame->fonts object-id objects)] + (let [fonts (ff/frame->fonts object objects)] [:& ff/fontfaces-style {:fonts fonts}]) (case (:type object) From 2b9badfd4ebf58485cf8c6246f7c7f2a138ffc05 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Fri, 22 Apr 2022 22:15:17 +0200 Subject: [PATCH 3/5] :zap: Debounce update position-data event --- .../src/app/main/data/workspace/texts.cljs | 43 +++++++++++++++++++ .../workspace/shapes/text/viewport_texts.cljs | 10 +---- 2 files changed, 45 insertions(+), 8 deletions(-) diff --git a/frontend/src/app/main/data/workspace/texts.cljs b/frontend/src/app/main/data/workspace/texts.cljs index 930b7fc99..b6cf52a64 100644 --- a/frontend/src/app/main/data/workspace/texts.cljs +++ b/frontend/src/app/main/data/workspace/texts.cljs @@ -13,6 +13,7 @@ [app.common.math :as mth] [app.common.pages.helpers :as cph] [app.common.text :as txt] + [app.common.uuid :as uuid] [app.main.data.workspace.changes :as dch] [app.main.data.workspace.common :as dwc] [app.main.data.workspace.selection :as dws] @@ -379,3 +380,45 @@ ptk/UpdateEvent (update [_ state] (d/dissoc-in state [:workspace-text-modifier id])))) + +(defn commit-position-data + [] + (ptk/reify ::commit-position-data + ptk/WatchEvent + (watch [_ state _] + (let [position-data (::update-position-data state)] + (rx/concat + (rx/of (dch/update-shapes + (keys position-data) + (fn [shape] + (-> shape + (assoc :position-data (get position-data (:id shape)))) + ) + {:save-undo? false :reg-objects? false})) + (rx/of (fn [state] + (dissoc state ::update-position-data-debounce ::update-position-data)))))))) + +(defn update-position-data + [id position-data] + + (let [start (uuid/next)] + (ptk/reify ::update-position-data + ptk/UpdateEvent + (update [_ state] + (if (nil? (::update-position-data-debounce state)) + (assoc state ::update-position-data-debounce start) + (assoc-in state [::update-position-data id] position-data))) + + ptk/WatchEvent + (watch [_ state stream] + (if (= (::update-position-data-debounce state) start) + (let [stopper (->> stream (rx/filter (ptk/type? :app.main.data.workspace/finalize)))] + (rx/merge + (->> stream + (rx/filter (ptk/type? ::update-position-data)) + (rx/debounce 50) + (rx/take 1) + (rx/map #(commit-position-data)) + (rx/take-until stopper)) + (rx/of (update-position-data id position-data)))) + (rx/empty)))))) diff --git a/frontend/src/app/main/ui/workspace/shapes/text/viewport_texts.cljs b/frontend/src/app/main/ui/workspace/shapes/text/viewport_texts.cljs index 17e1aaae6..f225161f1 100644 --- a/frontend/src/app/main/ui/workspace/shapes/text/viewport_texts.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/text/viewport_texts.cljs @@ -12,7 +12,6 @@ [app.common.math :as mth] [app.common.pages.helpers :as cph] [app.common.text :as txt] - [app.main.data.workspace.changes :as dch] [app.main.data.workspace.texts :as dwt] [app.main.fonts :as fonts] [app.main.refs :as refs] @@ -41,7 +40,7 @@ (assoc :content (attrs/merge content editor-content))))) (defn- update-text-shape - [{:keys [grow-type id]} node] + [{:keys [grow-type id] :as shape} node] ;; Check if we need to update the size because it's auto-width or auto-height (when (contains? #{:auto-height :auto-width} grow-type) (let [{:keys [width height]} @@ -54,12 +53,7 @@ ;; Update the position-data of every text fragment (let [position-data (utp/calc-position-data node)] - (st/emit! (dch/update-shapes - [id] - (fn [shape] - (-> shape - (assoc :position-data position-data))) - {:save-undo? false})))) + (st/emit! (dwt/update-position-data id position-data)))) (defn- update-text-modifier [{:keys [grow-type id]} node] From 01ca538c72e691519a42d178694ecbbb644c4a39 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Fri, 22 Apr 2022 22:16:03 +0200 Subject: [PATCH 4/5] :zap: Debounce update indices event --- .../src/app/main/data/workspace/changes.cljs | 52 ++++++++++++++++--- 1 file changed, 46 insertions(+), 6 deletions(-) diff --git a/frontend/src/app/main/data/workspace/changes.cljs b/frontend/src/app/main/data/workspace/changes.cljs index 2738d6d4f..6d8557f5f 100644 --- a/frontend/src/app/main/data/workspace/changes.cljs +++ b/frontend/src/app/main/data/workspace/changes.cljs @@ -6,11 +6,13 @@ (ns app.main.data.workspace.changes (:require + [app.common.data :as d] [app.common.logging :as log] [app.common.pages :as cp] [app.common.pages.changes-builder :as pcb] [app.common.spec :as us] [app.common.spec.change :as spec.change] + [app.common.uuid :as uuid] [app.main.data.workspace.state-helpers :as wsh] [app.main.data.workspace.undo :as dwu] [app.main.store :as st] @@ -59,14 +61,52 @@ (let [changes (cond-> changes reg-objects? (pcb/resize-parents ids))] (rx/of (commit-changes changes))))))))) +(defn send-update-indices + [] + (ptk/reify ::send-update-indices + ptk/WatchEvent + (watch [_ _ _] + (->> (rx/of + (fn [state] + (-> state + (dissoc ::update-indices-debounce) + (dissoc ::update-changes)))) + (rx/observe-on :async))) + + ptk/EffectEvent + (effect [_ state _] + (doseq [[page-id changes] (::update-changes state)] + (uw/ask! {:cmd :update-page-indices + :page-id page-id + :changes changes}))))) + +;; Update indices will debounce operations so we don't have to update +;; the index several times (which is an expensive operation) (defn update-indices [page-id changes] - (ptk/reify ::update-indices - ptk/EffectEvent - (effect [_ _ _] - (uw/ask! {:cmd :update-page-indices - :page-id page-id - :changes changes})))) + + (let [start (uuid/next)] + (ptk/reify ::update-indices + ptk/UpdateEvent + (update [_ state] + (if (nil? (::update-indices-debounce state)) + (assoc state ::update-indices-debounce start) + (update-in state [::update-changes page-id] (fnil d/concat-vec []) changes))) + + ptk/WatchEvent + (watch [_ state stream] + (if (= (::update-indices-debounce state) start) + (let [stopper (->> stream (rx/filter (ptk/type? :app.main.data.workspace/finalize)))] + (rx/merge + (->> stream + (rx/filter (ptk/type? ::update-indices)) + (rx/debounce 50) + (rx/take 1) + (rx/map #(send-update-indices)) + (rx/take-until stopper)) + (rx/of (update-indices page-id changes)))) + (rx/empty)))))) + (defn commit-changes [{:keys [redo-changes undo-changes From 41948ff86b38f79ee460ad456522c8cc419d0761 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Mon, 25 Apr 2022 11:41:05 +0200 Subject: [PATCH 5/5] :bug: Changes after review --- frontend/src/app/main/data/workspace/texts.cljs | 3 +-- .../src/app/main/ui/workspace/shapes/text/viewport_texts.cljs | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/frontend/src/app/main/data/workspace/texts.cljs b/frontend/src/app/main/data/workspace/texts.cljs index b6cf52a64..4b3cf805a 100644 --- a/frontend/src/app/main/data/workspace/texts.cljs +++ b/frontend/src/app/main/data/workspace/texts.cljs @@ -392,8 +392,7 @@ (keys position-data) (fn [shape] (-> shape - (assoc :position-data (get position-data (:id shape)))) - ) + (assoc :position-data (get position-data (:id shape))))) {:save-undo? false :reg-objects? false})) (rx/of (fn [state] (dissoc state ::update-position-data-debounce ::update-position-data)))))))) diff --git a/frontend/src/app/main/ui/workspace/shapes/text/viewport_texts.cljs b/frontend/src/app/main/ui/workspace/shapes/text/viewport_texts.cljs index f225161f1..7e593d5a6 100644 --- a/frontend/src/app/main/ui/workspace/shapes/text/viewport_texts.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/text/viewport_texts.cljs @@ -40,7 +40,7 @@ (assoc :content (attrs/merge content editor-content))))) (defn- update-text-shape - [{:keys [grow-type id] :as shape} node] + [{:keys [grow-type id]} node] ;; Check if we need to update the size because it's auto-width or auto-height (when (contains? #{:auto-height :auto-width} grow-type) (let [{:keys [width height]}