From 6e9a77edcd89883cc50b05fb3210ed71b230f55d Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Thu, 2 Jun 2022 22:31:27 +0200 Subject: [PATCH 1/5] :bug: Fix undo for drawing curves --- frontend/src/app/main/data/workspace/common.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/app/main/data/workspace/common.cljs b/frontend/src/app/main/data/workspace/common.cljs index f396d8547..54523a2e0 100644 --- a/frontend/src/app/main/data/workspace/common.cljs +++ b/frontend/src/app/main/data/workspace/common.cljs @@ -124,7 +124,7 @@ (let [edition (get-in state [:workspace-local :edition]) drawing (get state :workspace-drawing)] ;; Editors handle their own undo's - (when-not (or (some? edition) (and (not-empty drawing) (nil? (:object drawing)))) + (when (and (nil? edition) (nil? (:object drawing))) (let [undo (:workspace-undo state) items (:items undo) index (or (:index undo) (dec (count items)))] From 541168aee4a7065a2ac29351f859e65113bba3df Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Thu, 2 Jun 2022 22:35:59 +0200 Subject: [PATCH 2/5] :bug: Fix problem with some data and text input --- .../shapes/frame/dynamic_modifiers.cljs | 14 ++++++ .../main/ui/workspace/shapes/text/editor.cljs | 48 +++++++++++-------- .../src/app/main/ui/workspace/viewport.cljs | 8 ++-- 3 files changed, 46 insertions(+), 24 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/shapes/frame/dynamic_modifiers.cljs b/frontend/src/app/main/ui/workspace/shapes/frame/dynamic_modifiers.cljs index 70adcdfb6..ae25c9177 100644 --- a/frontend/src/app/main/ui/workspace/shapes/frame/dynamic_modifiers.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/frame/dynamic_modifiers.cljs @@ -82,6 +82,7 @@ frame? (= :frame type) group? (= :group type) + text? (= :text type) mask? (and group? masked-group?)] (cond @@ -103,6 +104,10 @@ (dom/query-all shape-defs ".svg-def") (dom/query-all shape-defs ".svg-mask-wrapper"))) + text? + [shape-node + (dom/query shape-node ".text-container")] + :else [shape-node]))) @@ -185,6 +190,15 @@ (dom/class? node "frame-children") (set-transform-att! node "transform" (gmt/inverse transform)) + (dom/class? node "text-container") + (let [modifiers (dissoc modifiers :displacement :rotation)] + (when (not (gsh/empty-modifiers? modifiers)) + (let [mtx (-> shape + (assoc :modifiers modifiers) + (gsh/transform-shape) + (gsh/transform-matrix {:no-flip true}))] + (override-transform-att! node "transform" mtx)))) + (or (= (dom/get-tag-name node) "mask") (= (dom/get-tag-name node) "filter")) (transform-region! node modifiers) diff --git a/frontend/src/app/main/ui/workspace/shapes/text/editor.cljs b/frontend/src/app/main/ui/workspace/shapes/text/editor.cljs index ff2fd0bfc..372cbda74 100644 --- a/frontend/src/app/main/ui/workspace/shapes/text/editor.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/text/editor.cljs @@ -7,9 +7,10 @@ (ns app.main.ui.workspace.shapes.text.editor (:require ["draft-js" :as draft] - [app.common.geom.matrix :as gmt] + [app.common.data.macros :as dm] [app.common.geom.point :as gpt] [app.common.geom.shapes :as gsh] + [app.common.geom.shapes.text :as gsht] [app.common.text :as txt] [app.main.data.workspace :as dw] [app.main.data.workspace.texts :as dwt] @@ -255,30 +256,37 @@ (-> (gpt/subtract pt box) (gpt/multiply zoom))))) -(mf/defc text-editor-viewport +(mf/defc text-editor-svg {::mf/wrap-props false} [props] (let [shape (obj/get props "shape") - viewport-ref (obj/get props "viewport-ref") - zoom (obj/get props "zoom") - position - (-> (gpt/point (-> shape :selrect :x) - (-> shape :selrect :y)) - (translate-point-from-viewport (mf/ref-val viewport-ref) zoom)) + clip-id + (dm/str "text-edition-clip" (:id shape)) - top-left-corner (gpt/point (/ (:width shape) 2) (/ (:height shape) 2)) + text-modifier-ref + (mf/use-memo (mf/deps (:id shape)) #(refs/workspace-text-modifier-by-id (:id shape))) - transform - (-> (gmt/matrix) - (gmt/scale (gpt/point zoom)) - (gmt/multiply (gsh/transform-matrix shape nil top-left-corner)))] + text-modifier + (mf/deref text-modifier-ref) - [:div {:style {:position "absolute" - :left (str (:x position) "px") - :top (str (:y position) "px") - :pointer-events "all" - :transform (str transform) - :transform-origin "left top"}} + bounding-box + (gsht/position-data-bounding-box text-modifier)] - [:& text-shape-edit-html {:shape shape :key (str (:id shape))}]])) + [:g.text-editor {:clip-path (dm/fmt "url(#%)" clip-id) + :transform (dm/str (gsh/transform-matrix shape))} + [:defs + [:clipPath {:id clip-id} + [:rect {:x (min (:x bounding-box) (:x shape)) + :y (min (:y bounding-box) (:y shape)) + :width (max (:width bounding-box) (:width shape)) + :height (max (:height bounding-box) (:height shape)) + :fill "red"}]]] + + [:foreignObject {:x (:x shape) :y (:y shape) :width "100%" :height "100%" + :externalResourcesRequired true} + [:div {:style {:position "absolute" + :left 0 + :top 0 + :pointer-events "all"}} + [:& text-shape-edit-html {:shape shape :key (str (:id shape))}]]]])) diff --git a/frontend/src/app/main/ui/workspace/viewport.cljs b/frontend/src/app/main/ui/workspace/viewport.cljs index bcd31fa34..8de6b65ed 100644 --- a/frontend/src/app/main/ui/workspace/viewport.cljs +++ b/frontend/src/app/main/ui/workspace/viewport.cljs @@ -187,10 +187,7 @@ [:div.viewport [:div.viewport-overlays {:ref overlays-ref} - (when show-text-editor? - [:& editor/text-editor-viewport {:shape editing-shape - :viewport-ref viewport-ref - :zoom zoom}]) + (when show-comments? [:& comments/comments-layer {:vbox vbox :vport vport @@ -275,6 +272,9 @@ :on-pointer-up on-pointer-up} [:g {:style {:pointer-events (if disable-events? "none" "auto")}} + (when show-text-editor? + [:& editor/text-editor-svg {:shape editing-shape}]) + (when show-outlines? [:& outline/shape-outlines {:objects base-objects From 14b1970a8a96c11d5e4736a78cbb8f72290d61b3 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Thu, 2 Jun 2022 22:37:33 +0200 Subject: [PATCH 3/5] :bug: Fix concurrent thumbnail modification --- .../main/data/workspace/notifications.cljs | 4 + .../main/data/workspace/state_helpers.cljs | 4 + .../app/main/data/workspace/thumbnails.cljs | 119 +++++++++--------- .../shapes/frame/thumbnail_render.cljs | 13 +- 4 files changed, 83 insertions(+), 57 deletions(-) diff --git a/frontend/src/app/main/data/workspace/notifications.cljs b/frontend/src/app/main/data/workspace/notifications.cljs index e13c80f73..c87ed1299 100644 --- a/frontend/src/app/main/data/workspace/notifications.cljs +++ b/frontend/src/app/main/data/workspace/notifications.cljs @@ -189,10 +189,14 @@ (s/def ::file-change-event (s/keys :req-un [::type ::profile-id ::file-id ::session-id ::revn ::changes])) + (defn handle-file-change [{:keys [file-id changes] :as msg}] (us/assert ::file-change-event msg) (ptk/reify ::handle-file-change + IDeref + (-deref [_] {:changes changes}) + ptk/WatchEvent (watch [_ _ _] (let [position-data-operation? diff --git a/frontend/src/app/main/data/workspace/state_helpers.cljs b/frontend/src/app/main/data/workspace/state_helpers.cljs index 9b4986922..e0f60d196 100644 --- a/frontend/src/app/main/data/workspace/state_helpers.cljs +++ b/frontend/src/app/main/data/workspace/state_helpers.cljs @@ -18,6 +18,10 @@ ([state page-id] (get-in state [:workspace-data :pages-index page-id]))) +(defn lookup-data-objects + [data page-id] + (dm/get-in data [:pages-index page-id :objects])) + (defn lookup-page-objects ([state] (lookup-page-objects state (:current-page-id state))) diff --git a/frontend/src/app/main/data/workspace/thumbnails.cljs b/frontend/src/app/main/data/workspace/thumbnails.cljs index 7766494a1..60e6a039c 100644 --- a/frontend/src/app/main/data/workspace/thumbnails.cljs +++ b/frontend/src/app/main/data/workspace/thumbnails.cljs @@ -10,6 +10,7 @@ [app.common.pages.helpers :as cph] [app.common.uuid :as uuid] [app.main.data.workspace.changes :as dch] + [app.main.data.workspace.state-helpers :as wsh] [app.main.refs :as refs] [app.main.repo :as rp] [app.main.store :as st] @@ -31,7 +32,9 @@ [object-id] (rx/create (fn [subs] - (let [node (dom/query (dm/fmt "canvas.thumbnail-canvas[data-object-id='%'" object-id))] + ;; We look in the DOM a canvas that 1) matches the id and 2) that it's not empty + ;; will be empty on first rendering before drawing the thumbnail and we don't want to store that + (let [node (dom/query (dm/fmt "canvas.thumbnail-canvas[data-object-id='%']:not([data-empty])" object-id))] (if (some? node) (-> node (.toBlob (fn [blob] @@ -43,6 +46,14 @@ (do (rx/push! subs nil) (rx/end! subs))))))) +(defn clear-thumbnail + [page-id frame-id] + (ptk/reify ::clear-thumbnail + ptk/UpdateEvent + (update [_ state] + (let [object-id (dm/str page-id frame-id)] + (assoc-in state [:workspace-file :thumbnails object-id] nil))))) + (defn update-thumbnail "Updates the thumbnail information for the given frame `id`" [page-id frame-id] @@ -71,50 +82,39 @@ (defn- extract-frame-changes "Process a changes set in a commit to extract the frames that are changing" - [[event [old-objects new-objects]]] + [[event [old-data new-data]]] (let [changes (-> event deref :changes) extract-ids - (fn [{type :type :as change}] + (fn [{:keys [page-id type] :as change}] (case type - :add-obj [(:id change)] - :mod-obj [(:id change)] - :del-obj [(:id change)] - :reg-objects (:shapes change) - :mov-objects (:shapes change) + :add-obj [[page-id (:id change)]] + :mod-obj [[page-id (:id change)]] + :del-obj [[page-id (:id change)]] + :mov-objects (->> (:shapes change) (map #(vector page-id %))) [])) get-frame-id - (fn [id] - (let [shape (or (get new-objects id) - (get old-objects id))] - (or (and (cph/frame-shape? shape) id) (:frame-id shape)))) + (fn [[page-id id]] + (let [old-objects (wsh/lookup-data-objects old-data page-id) + new-objects (wsh/lookup-data-objects new-data page-id) - ;; Extracts the frames and then removes nils and the root frame - xform (comp (mapcat extract-ids) - (map get-frame-id) - (remove nil?) - (filter #(not= uuid/zero %)) - (filter #(contains? new-objects %)))] + new-shape (get new-objects id) + old-shape (get old-objects id) - (into #{} xform changes))) + old-frame-id (if (cph/frame-shape? old-shape) id (:frame-id old-shape)) + new-frame-id (if (cph/frame-shape? new-shape) id (:frame-id new-shape))] -(defn thumbnail-change? - "Checks if a event is only updating thumbnails to ignore in the thumbnail generation process" - [event] - (let [changes (-> event deref :changes) + (cond-> #{} + (and old-frame-id (not= uuid/zero old-frame-id)) + (conj [page-id old-frame-id]) - is-thumbnail-op? - (fn [{type :type attr :attr}] - (and (= type :set) - (= attr :thumbnail))) - - is-thumbnail-change? - (fn [change] - (and (= (:type change) :mod-obj) - (->> change :operations (every? is-thumbnail-op?))))] - - (->> changes (every? is-thumbnail-change?)))) + (and new-frame-id (not= uuid/zero new-frame-id)) + (conj [page-id new-frame-id]))))] + (into #{} + (comp (mapcat extract-ids) + (mapcat get-frame-id)) + changes))) (defn watch-state-changes "Watch the state for changes inside frames. If a change is detected will force a rendering @@ -123,32 +123,39 @@ (ptk/reify ::watch-state-changes ptk/WatchEvent (watch [_ _ stream] - (let [stopper (->> stream - (rx/filter #(or (= :app.main.data.workspace/finalize-page (ptk/type %)) - (= ::watch-state-changes (ptk/type %))))) + (let [stopper + (->> stream + (rx/filter #(or (= :app.main.data.workspace/finalize-page (ptk/type %)) + (= ::watch-state-changes (ptk/type %))))) - objects-stream (->> (rx/concat - (rx/of nil) - (rx/from-atom refs/workspace-page-objects {:emit-current-value? true})) - ;; We need to keep the old-objects so we can check the frame for the - ;; deleted objects - (rx/buffer 2 1)) + workspace-data-str + (->> (rx/concat + (rx/of nil) + (rx/from-atom refs/workspace-data {:emit-current-value? true})) + ;; We need to keep the old-objects so we can check the frame for the + ;; deleted objects + (rx/buffer 2 1)) - frame-changes (->> stream - (rx/filter dch/commit-changes?) + change-str + (->> stream + (rx/filter #(or (dch/commit-changes? %) + (= (ptk/type %) :app.main.data.workspace.notifications/handle-file-change))) + (rx/observe-on :async)) - ;; Async so we wait for additional side-effects of commit-changes - (rx/observe-on :async) - (rx/filter (complement thumbnail-change?)) - (rx/with-latest-from objects-stream) - (rx/map extract-frame-changes) - (rx/share))] + frame-changes-str + (->> change-str + (rx/with-latest-from workspace-data-str) + (rx/flat-map extract-frame-changes) + (rx/share))] - (->> frame-changes - (rx/flat-map - (fn [ids] - (->> (rx/from ids) - (rx/map #(ptk/data-event ::force-render %))))) + (->> (rx/merge + (->> frame-changes-str + (rx/filter (fn [[page-id _]] (not= page-id (:current-page-id @st/state)))) + (rx/map (fn [[page-id frame-id]] (clear-thumbnail page-id frame-id)))) + + (->> frame-changes-str + (rx/filter (fn [[page-id _]] (= page-id (:current-page-id @st/state)))) + (rx/map (fn [[_ frame-id]] (ptk/data-event ::force-render frame-id))))) (rx/take-until stopper)))))) (defn duplicate-thumbnail diff --git a/frontend/src/app/main/ui/workspace/shapes/frame/thumbnail_render.cljs b/frontend/src/app/main/ui/workspace/shapes/frame/thumbnail_render.cljs index 1e763a462..c61ca1d84 100644 --- a/frontend/src/app/main/ui/workspace/shapes/frame/thumbnail_render.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/frame/thumbnail_render.cljs @@ -32,6 +32,7 @@ (.clearRect canvas-context 0 0 canvas-width canvas-height) (.drawImage canvas-context img-node 0 0 canvas-width canvas-height) + (.removeAttribute canvas-node "data-empty") true)) (catch :default err (.error js/console err) @@ -75,6 +76,8 @@ thumbnail-data-ref (mf/use-memo (mf/deps page-id id) #(refs/thumbnail-frame-data page-id id)) thumbnail-data (mf/deref thumbnail-data-ref) + prev-thumbnail-data (hooks/use-previous thumbnail-data) + render-frame? (mf/use-state (not thumbnail-data)) on-image-load @@ -141,6 +144,12 @@ (.observe observer node #js {:childList true :attributes true :attributeOldValue true :characterData true :subtree true}) (reset! observer-ref observer)))))] + (mf/use-effect + (mf/deps thumbnail-data) + (fn [] + (when (and (some? prev-thumbnail-data) (nil? thumbnail-data)) + (rx/push! updates-str :update)))) + (mf/use-effect (mf/deps @render-frame? thumbnail-data) (fn [] @@ -198,8 +207,10 @@ [:foreignObject {:x x :y y :width width :height height} [:canvas.thumbnail-canvas - {:ref frame-canvas-ref + {:key (dm/str "thumbnail-canvas-" (:id shape)) + :ref frame-canvas-ref :data-object-id (dm/str page-id (:id shape)) + :data-empty true :width fixed-width :height fixed-height ;; DEBUG From 424630a67f445e00b4b20a7239e9a848fc18a2bd Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Thu, 2 Jun 2022 22:53:50 +0200 Subject: [PATCH 4/5] :books: Update changelog --- CHANGES.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 64657c165..8561bd6a9 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -8,6 +8,14 @@ ### :arrow_up: Deps updates ### :heart: Community contributions by (Thank you!) +## 1.13.4-beta + +### :bug: Bugs fixed + +- Fix undo when drawing curves [Taiga #3523](https://tree.taiga.io/project/penpot/issue/3523) +- Fix issue with text edition and certain fonts (WorkSans, Raleway, ...) and foreign objects [Taiga #3521](https://tree.taiga.io/project/penpot/issue/3521) +- Fix thumbnail generation when concurrent edition [Taiga #3522](https://tree.taiga.io/project/penpot/issue/3522) + ## 1.13.3-beta ### :bug: Bugs fixed From 2b61b1768f287ac2acc875b47d8a75d16560d1a2 Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Fri, 3 Jun 2022 07:43:19 +0200 Subject: [PATCH 5/5] :bug: Fix exporter white list domains configuration --- exporter/src/app/config.cljs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exporter/src/app/config.cljs b/exporter/src/app/config.cljs index bdbccb122..47b835c60 100644 --- a/exporter/src/app/config.cljs +++ b/exporter/src/app/config.cljs @@ -26,7 +26,7 @@ :http-server-port 6061 :http-server-host "localhost" :redis-uri "redis://redis/0" - :exporter-domain-whitelist #{"localhost:3449"}}) + :domain-white-list #{"localhost:3449"}}) (s/def ::http-server-port ::us/integer) (s/def ::http-server-host ::us/string) @@ -45,7 +45,7 @@ ::http-server-host ::browser-pool-max ::browser-pool-min - ::domain-whitelist])) + ::domain-white-list])) (defn- read-env [prefix]