From 07939d11dc4cb296ea82d5d5270643c51fd27df5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Moya?= Date: Thu, 29 Feb 2024 14:06:39 +0100 Subject: [PATCH 1/8] :tada: Improve sync algorithm when swapped copies --- common/src/app/common/files/helpers.cljc | 3 +- common/src/app/common/types/component.cljc | 29 ++++- common/src/app/common/types/file.cljc | 99 ++++++++++++++-- .../app/main/data/workspace/libraries.cljs | 43 +++++-- .../data/workspace/libraries_helpers.cljs | 112 ++++++++++++------ 5 files changed, 227 insertions(+), 59 deletions(-) diff --git a/common/src/app/common/files/helpers.cljc b/common/src/app/common/files/helpers.cljc index 296d4da83..676ba2c46 100644 --- a/common/src/app/common/files/helpers.cljc +++ b/common/src/app/common/files/helpers.cljc @@ -361,7 +361,8 @@ (defn set-touched-group [touched group] - (conj (or touched #{}) group)) + (when group + (conj (or touched #{}) group))) (defn touched-group? [shape group] diff --git a/common/src/app/common/types/component.cljc b/common/src/app/common/types/component.cljc index ec9a679a9..7c6570f0c 100644 --- a/common/src/app/common/types/component.cljc +++ b/common/src/app/common/types/component.cljc @@ -4,7 +4,11 @@ ;; ;; Copyright (c) KALEIDOS INC -(ns app.common.types.component) +(ns app.common.types.component + (:require + [app.common.data :as d] + [app.common.uuid :as uuid] + [cuerdas.core :as str])) ;; Attributes that may be synced in components, and the group they belong to. ;; When one attribute is modified in a shape inside a component, the corresponding @@ -170,6 +174,29 @@ (and (= shape-id (:main-instance-id component)) (= page-id (:main-instance-page component)))) +(defn build-swap-slot-group + "Convert a swap-slot into a :touched group" + [swap-slot] + (when swap-slot + (keyword (str "swap-slot-" swap-slot)))) + +(defn get-swap-slot + "If the shape has a :touched group in the form :swap-slot-, get the id." + [shape] + (let [group (->> (:touched shape) + (map name) + (d/seek #(str/starts-with? % "swap-slot-")))] + (when group + (uuid/uuid (subs group 10))))) + +(defn match-swap-slot? + [shape-inst shape-main] + (let [slot-inst (get-swap-slot shape-inst) + slot-main (get-swap-slot shape-main)] + (when (some? slot-inst) + (or (= slot-inst slot-main) + (= slot-inst (:id shape-main)))))) + (defn get-component-root [component] (if (true? (:main-instance-id component)) diff --git a/common/src/app/common/types/file.cljc b/common/src/app/common/types/file.cljc index 42a0a7789..9f16496ab 100644 --- a/common/src/app/common/types/file.cljc +++ b/common/src/app/common/types/file.cljc @@ -177,12 +177,36 @@ shape-id))) (dm/get-in component [:objects shape-id])))) +(defn get-component-shape-context + "Retrieve one shape in the component by id. Return the shape and its + context (the file and the container)." + [file component shape-id] + (let [components-v2 (dm/get-in file [:data :options :components-v2])] + (if (and components-v2 (not (:deleted component))) + (let [component-page (get-component-page (:data file) component)] + (when component-page + (let [child (cfh/get-child (:objects component-page) + (:main-instance-id component) + shape-id)] + (when child + [child file (ctn/make-container component-page :page)])))) + [(dm/get-in component [:objects shape-id]) + file + (ctn/make-container component :component)]))) + (defn get-ref-shape "Retrieve the shape in the component that is referenced by the instance shape." [file-data component shape] (when (:shape-ref shape) (get-component-shape file-data component (:shape-ref shape)))) +(defn get-ref-shape-context + "Retrieve the shape in the component that is referenced by the instance shape. + Return the shape and its context (the file and the container)." + [file component shape] + (when (:shape-ref shape) + (get-component-shape-context file component (:shape-ref shape)))) + (defn get-shape-in-copy "Given a shape in the main component and the root of the copy component returns the equivalent shape inside the root copy that matches the main-shape" @@ -196,11 +220,33 @@ [file page libraries shape & {:keys [include-deleted?] :or {include-deleted? false}}] (let [find-ref-shape-in-head (fn [head-shape] - (let [head-file (find-component-file file libraries (:component-file head-shape)) - head-component (when (some? head-file) - (ctkl/get-component (:data head-file) (:component-id head-shape) include-deleted?))] - (when (some? head-component) - (get-ref-shape (:data head-file) head-component shape))))] + (let [component-file (find-component-file file libraries (:component-file head-shape)) + component (when (some? component-file) + (ctkl/get-component (:data component-file) (:component-id head-shape) include-deleted?))] + (when (some? component) + (get-ref-shape (:data component-file) component shape))))] + + (some find-ref-shape-in-head (ctn/get-parent-heads (:objects page) shape)))) + +(defn find-ref-shape-context + "Locate the nearest component in the local file or libraries, and retrieve the shape + referenced by the instance shape. Return the shape and its context (the file and + the container)." + ; TODO: It should be nice to avoid this duplicity without adding overhead in the simple case. + ; Perhaps adding the context as metadata of the shape? + [file page libraries shape & {:keys [include-deleted?] :or {include-deleted? false}}] + (let [find-ref-shape-in-head + (fn [head-shape] + ;; (js/console.log "head-shape" (clj->js head-shape)) + ;; (js/console.log " component-file" (str (:component-file head-shape))) + ;; (js/console.log " component-id" (str (:component-id head-shape))) + (let [component-file (find-component-file file libraries (:component-file head-shape)) + component (when (some? component-file) + (ctkl/get-component (:data component-file) (:component-id head-shape) include-deleted?))] + ;; (js/console.log "component-file" (clj->js component-file)) + ;; (js/console.log "component" (clj->js component)) + (when (some? component) + (get-ref-shape-context component-file component shape))))] (some find-ref-shape-in-head (ctn/get-parent-heads (:objects page) shape)))) @@ -210,12 +256,14 @@ [file page libraries shape & {:keys [include-deleted?] :or {include-deleted? false}}] (let [find-ref-component-in-head (fn [head-shape] - (let [head-file (find-component-file file libraries (:component-file head-shape)) - head-component (when (some? head-file) - (ctkl/get-component (:data head-file) (:component-id head-shape) include-deleted?))] - (when (some? head-component) - (when (get-ref-shape (:data head-file) head-component shape) - head-component))))] + (let [component-file (find-component-file file libraries (:component-file head-shape)) + component (when (some? component-file) + (ctkl/get-component (:data component-file) + (:component-id head-shape) + include-deleted?))] + (when (some? component) + (when (get-ref-shape (:data component-file) component shape) + component))))] (some find-ref-component-in-head (ctn/get-parent-copy-heads (:objects page) shape)))) @@ -251,6 +299,35 @@ (let [ref-component (find-ref-component file page libraries shape :include-deleted? true)] (true? (= (:id component) (:id ref-component))))) +(defn find-swap-slot + [shape page file libraries] + (dm/assert! "expected shape is head" (ctk/instance-head? shape)) + ;; (js/console.log "find-swap-slot" (clj->js shape)) + (if-let [swap-slot (ctk/get-swap-slot shape)] + ;; (do (js/console.log "uno" (str swap-slot)) swap-slot) + swap-slot + (let [[ref-shape ref-file ref-container] (find-ref-shape-context file + page + libraries + shape + :include-deleted? true)] + ;; (js/console.log "ref-shape" (clj->js ref-shape)) + (when ref-shape + ;; (js/console.log "ref-shape" (clj->js ref-shape)) + (if-let [swap-slot (ctk/get-swap-slot ref-shape)] + ;; (do (js/console.log "dos" (str swap-slot)) swap-slot) + swap-slot + (if (ctk/main-instance? ref-shape) + (:id shape) + (find-swap-slot ref-shape ref-container ref-file libraries))))))) + +(defn match-swap-slot? + [shape-inst shape-main page-inst page-main file libraries] + (let [slot-inst (find-swap-slot shape-inst page-inst file libraries) + slot-main (find-swap-slot shape-main page-main file libraries)] + (or (= slot-inst slot-main) + (= slot-inst (:id shape-main))))) + (defn get-component-shapes "Retrieve all shapes of the component" [file-data component] diff --git a/frontend/src/app/main/data/workspace/libraries.cljs b/frontend/src/app/main/data/workspace/libraries.cljs index c24d1d469..0d64b733d 100644 --- a/frontend/src/app/main/data/workspace/libraries.cljs +++ b/frontend/src/app/main/data/workspace/libraries.cljs @@ -52,7 +52,7 @@ [potok.v2.core :as ptk])) ;; Change this to :info :debug or :trace to debug this module, or :warn to reset to default -(log/set-level! :warn) +(log/set-level! :trace) (defn- log-changes [changes file] @@ -870,16 +870,12 @@ 0))))) (defn- add-component-for-swap - [shape file-id id-new-component index target-cell keep-props-values {:keys [undo-group]}] + [shape file page libraries id-new-component index target-cell keep-props-values {:keys [undo-group]}] (dm/assert! (uuid? id-new-component)) - (dm/assert! (uuid? file-id)) (ptk/reify ::add-component-for-swap ptk/WatchEvent - (watch [it state _] - (let [page (wsh/lookup-page state) - libraries (wsh/get-libraries state) - - objects (:objects page) + (watch [it _ _] + (let [objects (:objects page) position (gpt/point (:x shape) (:y shape)) changes (-> (pcb/empty-changes it (:id page)) (pcb/set-undo-group undo-group) @@ -889,7 +885,7 @@ [new-shape changes] (dwlh/generate-instantiate-component changes objects - file-id + (:id file) id-new-component position page @@ -898,6 +894,16 @@ (:parent-id shape) (:frame-id shape)) + new-shape (cond-> new-shape + (nil? (ctk/get-swap-slot new-shape)) + (update :touched cfh/set-touched-group (-> (ctf/find-swap-slot shape + page + {:id (:id file) + :data file} + libraries) + (ctk/build-swap-slot-group)))) + + ;; _ (js/console.log "new-shape" (str (:id new-shape)) (clj->js new-shape)) changes (-> changes ;; Restore the properties @@ -905,7 +911,11 @@ ;; We need to set the same index as the original shape (pcb/change-parent (:parent-id shape) [new-shape] index {:component-swap true - :ignore-touched true}))] + :ignore-touched true}) + (dwlh/change-touched new-shape + shape + (ctn/make-container page :page) + {}))] ;; First delete so we don't break the grid layout cells (rx/of (dch/commit-changes changes) @@ -921,7 +931,10 @@ (watch [_ state _] ;; First delete shapes so we have space in the layout otherwise we can have problems ;; in the grid creating new rows/columns to make space - (let [objects (wsh/lookup-page-objects state) + (let [file (wsh/get-file state file-id) + libraries (wsh/get-libraries state) + page (wsh/lookup-page state) + objects (wsh/lookup-page-objects state) parent (get objects (:parent-id shape)) ;; If the target parent is a grid layout we need to pass the target cell @@ -941,7 +954,7 @@ (dwsh/delete-shapes nil (d/ordered-set (:id shape)) {:component-swap true :undo-id undo-id :undo-group undo-group}) - (add-component-for-swap shape file-id id-new-component index target-cell keep-props-values + (add-component-for-swap shape file page libraries id-new-component index target-cell keep-props-values {:undo-group undo-group}) (ptk/data-event :layout/update [(:parent-id shape)]) (dwu/commit-undo-transaction undo-id)))))) @@ -958,8 +971,12 @@ {::ev/name "component-swap"}) ptk/WatchEvent - (watch [_ _ _] + (watch [_ state _] (let [undo-id (js/Symbol)] + (log/info :msg "COMPONENT-SWAP" + :file (dwlh/pretty-file file-id state) + :id-new-component id-new-component + :undo-id undo-id) (rx/concat (rx/of (dwu/start-undo-transaction undo-id)) (rx/map #(component-swap % file-id id-new-component) (rx/from shapes)) diff --git a/frontend/src/app/main/data/workspace/libraries_helpers.cljs b/frontend/src/app/main/data/workspace/libraries_helpers.cljs index 32152b2ba..92b026456 100644 --- a/frontend/src/app/main/data/workspace/libraries_helpers.cljs +++ b/frontend/src/app/main/data/workspace/libraries_helpers.cljs @@ -30,7 +30,7 @@ [clojure.set :as set])) ;; Change this to :info :debug or :trace to debug this module, or :warn to reset to default -(log/set-level! :warn) +(log/set-level! :trace) (declare generate-sync-container) (declare generate-sync-shape) @@ -594,7 +594,7 @@ "Generate changes to synchronize one shape that is the root of a component instance, and all its children, from the given component." [changes libraries container shape-id reset? components-v2] - (log/debug :msg "Sync shape direct" :shape (str shape-id) :reset? reset?) + (log/debug :msg "Sync shape direct" :shape-inst (str shape-id) :reset? reset?) (let [shape-inst (ctn/get-shape container shape-id) library (dm/get-in libraries [(:component-file shape-inst) :data]) component (ctkl/get-component library (:component-id shape-inst) true)] @@ -656,7 +656,7 @@ (defn- generate-sync-shape-direct-recursive [changes container shape-inst component library shape-main root-inst root-main reset? initial-root? redirect-shaperef components-v2] (log/debug :msg "Sync shape direct recursive" - :shape (str (:name shape-inst)) + :shape-inst (str (:name shape-inst) " " (pretty-uuid (:id shape-inst))) :component (:name component)) (if (nil? shape-main) @@ -713,6 +713,8 @@ (map #(redirect-shaperef %) children-inst) children-inst) only-inst (fn [changes child-inst] + (log/trace :msg "Only inst" + :child-inst (str (:name child-inst) " " (pretty-uuid (:id child-inst)))) (if-not (and omit-touched? (contains? (:touched shape-inst) :shapes-group)) @@ -723,6 +725,8 @@ changes)) only-main (fn [changes child-main] + (log/trace :msg "Only main" + :child-main (str (:name child-main) " " (pretty-uuid (:id child-main)))) (if-not (and omit-touched? (contains? (:touched shape-inst) :shapes-group)) @@ -739,6 +743,9 @@ changes)) both (fn [changes child-inst child-main] + (log/trace :msg "Both" + :child-inst (str (:name child-inst) " " (pretty-uuid (:id child-inst))) + :child-main (str (:name child-main) " " (pretty-uuid (:id child-main)))) (generate-sync-shape-direct-recursive changes container child-inst @@ -753,6 +760,9 @@ components-v2)) moved (fn [changes child-inst child-main] + (log/trace :msg "Move" + :child-inst (str (:name child-inst) " " (pretty-uuid (:id child-inst))) + :child-main (str (:name child-main) " " (pretty-uuid (:id child-main)))) (move-shape changes child-inst @@ -768,7 +778,8 @@ only-main both moved - false)))) + false + reset?)))) (defn- generate-rename-component @@ -939,6 +950,7 @@ only-main both moved + true true) ;; The inverse sync may be made on a component that is inside a @@ -957,12 +969,15 @@ ;; ---- Operation generation helpers ---- (defn- compare-children - [changes children-inst children-main only-inst-cb only-main-cb both-cb moved-cb inverse?] + [changes children-inst children-main only-inst-cb only-main-cb both-cb moved-cb inverse? reset?] + (log/trace :msg "Compare children") (loop [children-inst (seq (or children-inst [])) children-main (seq (or children-main [])) changes changes] (let [child-inst (first children-inst) child-main (first children-main)] + (log/trace :main (str (:name child-main) " " (pretty-uuid (:id child-main))) + :inst (str (:name child-inst) " " (pretty-uuid (:id child-inst)))) (cond (and (nil? child-inst) (nil? child-main)) changes @@ -979,31 +994,58 @@ (next children-main) (both-cb changes child-inst child-main)) - (let [child-inst' (d/seek #(ctk/is-main-of? child-main %) children-inst) - child-main' (d/seek #(ctk/is-main-of? % child-inst) children-main)] - (cond - (nil? child-inst') - (recur children-inst - (next children-main) - (only-main-cb changes child-main)) - - (nil? child-main') + (if (and (ctk/match-swap-slot? child-main child-inst) (not reset?)) + (do + (log/trace :msg "Match slot" + :shape-inst (str (:name child-inst) " " (pretty-uuid (:id child-inst))) + :shape-main (str (:name child-main) " " (pretty-uuid (:id child-main)))) (recur (next children-inst) - children-main - (only-inst-cb changes child-inst)) + (next children-main) + changes)) - :else - (if inverse? - (recur (next children-inst) - (remove #(= (:id %) (:id child-main')) children-main) - (-> changes - (both-cb child-inst child-main') - (moved-cb child-inst child-main'))) - (recur (remove #(= (:id %) (:id child-inst')) children-inst) - (next children-main) - (-> changes - (both-cb child-inst' child-main) - (moved-cb child-inst' child-main))))))))))) + (let [child-inst' (d/seek #(ctk/is-main-of? child-main %) children-inst) + child-main' (d/seek #(ctk/is-main-of? % child-inst) children-main)] + (cond + (nil? child-inst') + (let [matching-inst (d/seek #(ctk/match-swap-slot? % child-main) children-inst)] + (if (and (some? matching-inst) (not reset?)) + (do + (log/trace :msg "Match slot inst" + :shape-inst (str (:name child-inst') " " (pretty-uuid (:id child-inst'))) + :shape-main (str (:name child-main) " " (pretty-uuid (:id child-main)))) + (recur (remove #(= (:id %) (:id matching-inst)) children-inst) + (next children-main) + changes)) + (recur children-inst + (next children-main) + (only-main-cb changes child-main)))) + + (nil? child-main') + (let [matching-main (d/seek #(ctk/match-swap-slot? child-inst %) children-main)] + (if (and (some? matching-main) (not reset?)) + (do + (log/trace :msg "Match slot main" + :shape-inst (str (:name child-inst) " " (pretty-uuid (:id child-inst))) + :shape-main (str (:name child-main') " " (pretty-uuid (:id child-main')))) + (recur (next children-inst) + (remove #(= (:id %) (:id matching-main)) children-inst) + changes)) + (recur (next children-inst) + children-main + (only-inst-cb changes child-inst)))) + + :else + (if inverse? + (recur (next children-inst) + (remove #(= (:id %) (:id child-main')) children-main) + (-> changes + (both-cb child-inst child-main') + (moved-cb child-inst child-main'))) + (recur (remove #(= (:id %) (:id child-inst')) children-inst) + (next children-main) + (-> changes + (both-cb child-inst' child-main) + (moved-cb child-inst' child-main)))))))))))) (defn- add-shape-to-instance [changes component-shape index component-page container root-instance root-main omit-touched? set-remote-synced?] @@ -1033,7 +1075,8 @@ (assoc :remote-synced true) :always - (assoc :shape-ref (:id original-shape))))) + (-> (assoc :shape-ref (:id original-shape)) + (dissoc :touched))))) ; New shape, by definition, is synced to the main shape update-original-shape (fn [original-shape _new-shape] original-shape) @@ -1270,11 +1313,10 @@ changes changes'))) -(defn- change-touched +(defn change-touched [changes dest-shape origin-shape container {:keys [reset-touched? copy-touched?] :as options}] - (if (or (nil? (:shape-ref dest-shape)) - (not (or reset-touched? copy-touched?))) + (if (nil? (:shape-ref dest-shape)) changes (do (log/info :msg (str "CHANGE-TOUCHED " @@ -1287,12 +1329,16 @@ (let [new-touched (cond reset-touched? nil + copy-touched? (if (:remote-synced origin-shape) nil (set/union (:touched dest-shape) - (:touched origin-shape))))] + (:touched origin-shape))) + + :else + (:touched dest-shape))] (-> changes (update :redo-changes conj (make-change From 4dfbfcf2ac2ca4e1e4863ae3cdde24e528e94a91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Moya?= Date: Mon, 4 Mar 2024 17:13:25 +0100 Subject: [PATCH 2/8] :recycle: Avoid duplicating helpers --- common/src/app/common/types/file.cljc | 88 +++++++++------------------ 1 file changed, 29 insertions(+), 59 deletions(-) diff --git a/common/src/app/common/types/file.cljc b/common/src/app/common/types/file.cljc index 9f16496ab..814c583dc 100644 --- a/common/src/app/common/types/file.cljc +++ b/common/src/app/common/types/file.cljc @@ -166,46 +166,33 @@ (ctk/get-component-root component)))) (defn get-component-shape - "Retrieve one shape in the component by id." - [file-data component shape-id] + "Retrieve one shape in the component by id. If with-context? is true, add the + file and container where the shape resides in its metadata." + [file-data component shape-id & {:keys [with-context?] :or {with-context? false}}] (let [components-v2 (dm/get-in file-data [:options :components-v2])] (if (and components-v2 (not (:deleted component))) (let [component-page (get-component-page file-data component)] - (when component-page - (cfh/get-child (:objects component-page) - (:main-instance-id component) - shape-id))) - (dm/get-in component [:objects shape-id])))) - -(defn get-component-shape-context - "Retrieve one shape in the component by id. Return the shape and its - context (the file and the container)." - [file component shape-id] - (let [components-v2 (dm/get-in file [:data :options :components-v2])] - (if (and components-v2 (not (:deleted component))) - (let [component-page (get-component-page (:data file) component)] (when component-page (let [child (cfh/get-child (:objects component-page) (:main-instance-id component) shape-id)] - (when child - [child file (ctn/make-container component-page :page)])))) - [(dm/get-in component [:objects shape-id]) - file - (ctn/make-container component :component)]))) + (cond-> child + (and child with-context?) + (with-meta {:file {:id (:id file-data) + :data file-data} + :container (ctn/make-container component-page :page)}))))) + + (cond-> (dm/get-in component [:objects shape-id]) + with-context? + (with-meta {:file {:id (:id file-data) + :data file-data} + :container (ctn/make-container component :component)}))))) (defn get-ref-shape "Retrieve the shape in the component that is referenced by the instance shape." - [file-data component shape] + [file-data component shape & {:keys [with-context?] :or {with-context? false}}] (when (:shape-ref shape) - (get-component-shape file-data component (:shape-ref shape)))) - -(defn get-ref-shape-context - "Retrieve the shape in the component that is referenced by the instance shape. - Return the shape and its context (the file and the container)." - [file component shape] - (when (:shape-ref shape) - (get-component-shape-context file component (:shape-ref shape)))) + (get-component-shape file-data component (:shape-ref shape) :with-context? with-context?))) (defn get-shape-in-copy "Given a shape in the main component and the root of the copy component returns the equivalent @@ -217,36 +204,14 @@ (defn find-ref-shape "Locate the nearest component in the local file or libraries, and retrieve the shape referenced by the instance shape." - [file page libraries shape & {:keys [include-deleted?] :or {include-deleted? false}}] + [file page libraries shape & {:keys [include-deleted? with-context?] :or {include-deleted? false with-context? false}}] (let [find-ref-shape-in-head (fn [head-shape] (let [component-file (find-component-file file libraries (:component-file head-shape)) component (when (some? component-file) (ctkl/get-component (:data component-file) (:component-id head-shape) include-deleted?))] (when (some? component) - (get-ref-shape (:data component-file) component shape))))] - - (some find-ref-shape-in-head (ctn/get-parent-heads (:objects page) shape)))) - -(defn find-ref-shape-context - "Locate the nearest component in the local file or libraries, and retrieve the shape - referenced by the instance shape. Return the shape and its context (the file and - the container)." - ; TODO: It should be nice to avoid this duplicity without adding overhead in the simple case. - ; Perhaps adding the context as metadata of the shape? - [file page libraries shape & {:keys [include-deleted?] :or {include-deleted? false}}] - (let [find-ref-shape-in-head - (fn [head-shape] - ;; (js/console.log "head-shape" (clj->js head-shape)) - ;; (js/console.log " component-file" (str (:component-file head-shape))) - ;; (js/console.log " component-id" (str (:component-id head-shape))) - (let [component-file (find-component-file file libraries (:component-file head-shape)) - component (when (some? component-file) - (ctkl/get-component (:data component-file) (:component-id head-shape) include-deleted?))] - ;; (js/console.log "component-file" (clj->js component-file)) - ;; (js/console.log "component" (clj->js component)) - (when (some? component) - (get-ref-shape-context component-file component shape))))] + (get-ref-shape (:data component-file) component shape :with-context? with-context?))))] (some find-ref-shape-in-head (ctn/get-parent-heads (:objects page) shape)))) @@ -306,14 +271,19 @@ (if-let [swap-slot (ctk/get-swap-slot shape)] ;; (do (js/console.log "uno" (str swap-slot)) swap-slot) swap-slot - (let [[ref-shape ref-file ref-container] (find-ref-shape-context file - page - libraries - shape - :include-deleted? true)] + (let [ref-shape (find-ref-shape file + page + libraries + shape + :include-deleted? true + :with-context? true) + shape-meta (meta ref-shape) + ref-file (:file shape-meta) + ref-container (:container shape-meta)] ;; (js/console.log "ref-shape" (clj->js ref-shape)) + ;; (js/console.log "ref-file" (clj->js ref-file)) + ;; (js/console.log "ref-container" (clj->js ref-container)) (when ref-shape - ;; (js/console.log "ref-shape" (clj->js ref-shape)) (if-let [swap-slot (ctk/get-swap-slot ref-shape)] ;; (do (js/console.log "dos" (str swap-slot)) swap-slot) swap-slot From 895fb3b480a51d2281fd59f4ac75268f529dd775 Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Tue, 5 Mar 2024 11:12:39 +0100 Subject: [PATCH 3/8] :tada: Remove shapes-group --- common/src/app/common/files/changes.cljc | 7 ++----- common/src/app/common/types/shape_tree.cljc | 6 ++---- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/common/src/app/common/files/changes.cljc b/common/src/app/common/files/changes.cljc index 2f1858505..c200749d8 100644 --- a/common/src/app/common/files/changes.cljc +++ b/common/src/app/common/files/changes.cljc @@ -507,8 +507,7 @@ (and (:shape-ref parent) (#{:group :frame} (:type parent)) (not ignore-touched)) - (-> (update :touched cfh/set-touched-group :shapes-group) - (dissoc :remote-synced))))) + (dissoc :remote-synced)))) (remove-from-old-parent [old-objects objects shape-id] (let [prev-parent-id (dm/get-in old-objects [shape-id :parent-id])] @@ -525,9 +524,7 @@ (-> objects (d/update-in-when [pid :shapes] d/without-obj sid) (d/update-in-when [pid :shapes] d/vec-without-nils) - (cond-> component? (d/update-when pid #(-> % - (update :touched cfh/set-touched-group :shapes-group) - (dissoc :remote-synced))))))))) + (cond-> component? (d/update-when pid #(dissoc % :remote-synced)))))))) (update-parent-id [objects id] (-> objects (d/update-when id assoc :parent-id parent-id))) diff --git a/common/src/app/common/types/shape_tree.cljc b/common/src/app/common/types/shape_tree.cljc index 92e8880be..646c26641 100644 --- a/common/src/app/common/types/shape_tree.cljc +++ b/common/src/app/common/types/shape_tree.cljc @@ -40,8 +40,7 @@ (update :shapes update-parent-shapes) (update :shapes d/vec-without-nils) (cond-> (and (ctk/in-component-copy? parent) (not ignore-touched)) - (-> (update :touched cfh/set-touched-group :shapes-group) - (dissoc :remote-synced))))) + (dissoc :remote-synced)))) update-objects (fn [objects parent-id] @@ -85,8 +84,7 @@ (let [parent (update parent :shapes d/without-obj shape-id)] (cond-> parent (and (:shape-ref parent) (not ignore-touched)) - (-> (update :touched cfh/set-touched-group :shapes-group) - (dissoc :remote-synced))))) + (dissoc :remote-synced)))) (delete-from-objects [objects] (if-let [target (get objects shape-id)] From c214d8b044d8691f79bd6392daa2660d952c8b23 Mon Sep 17 00:00:00 2001 From: Pablo Alba Date: Mon, 4 Mar 2024 17:44:34 +0100 Subject: [PATCH 4/8] :tada: Simplify and fix compare children --- common/src/app/common/types/component.cljc | 10 +- common/src/app/common/types/file.cljc | 10 +- .../data/workspace/libraries_helpers.cljs | 102 +++++++++--------- 3 files changed, 64 insertions(+), 58 deletions(-) diff --git a/common/src/app/common/types/component.cljc b/common/src/app/common/types/component.cljc index 7c6570f0c..70b030eaf 100644 --- a/common/src/app/common/types/component.cljc +++ b/common/src/app/common/types/component.cljc @@ -190,12 +190,12 @@ (uuid/uuid (subs group 10))))) (defn match-swap-slot? - [shape-inst shape-main] - (let [slot-inst (get-swap-slot shape-inst) - slot-main (get-swap-slot shape-main)] + [shape-main shape-inst] + (let [slot-main (get-swap-slot shape-main) + slot-inst (get-swap-slot shape-inst)] (when (some? slot-inst) - (or (= slot-inst slot-main) - (= slot-inst (:id shape-main)))))) + (or (= slot-main slot-inst) + (= (:id shape-main) slot-inst))))) (defn get-component-root [component] diff --git a/common/src/app/common/types/file.cljc b/common/src/app/common/types/file.cljc index 814c583dc..98e8fbae0 100644 --- a/common/src/app/common/types/file.cljc +++ b/common/src/app/common/types/file.cljc @@ -292,11 +292,11 @@ (find-swap-slot ref-shape ref-container ref-file libraries))))))) (defn match-swap-slot? - [shape-inst shape-main page-inst page-main file libraries] - (let [slot-inst (find-swap-slot shape-inst page-inst file libraries) - slot-main (find-swap-slot shape-main page-main file libraries)] - (or (= slot-inst slot-main) - (= slot-inst (:id shape-main))))) + [shape-main shape-inst page-inst page-main file libraries] + (let [slot-main (find-swap-slot shape-main page-main file libraries) + slot-inst (find-swap-slot shape-inst page-inst file libraries)] + (or (= slot-main slot-inst) + (= (:id shape-main) slot-inst)))) (defn get-component-shapes "Retrieve all shapes of the component" diff --git a/frontend/src/app/main/data/workspace/libraries_helpers.cljs b/frontend/src/app/main/data/workspace/libraries_helpers.cljs index 92b026456..adf8e4037 100644 --- a/frontend/src/app/main/data/workspace/libraries_helpers.cljs +++ b/frontend/src/app/main/data/workspace/libraries_helpers.cljs @@ -759,6 +759,13 @@ redirect-shaperef components-v2)) + swapped (fn [changes child-inst child-main] + (log/trace :msg "Match slot" + :child-inst (str (:name child-inst) " " (pretty-uuid (:id child-inst))) + :child-main (str (:name child-main) " " (pretty-uuid (:id child-main)))) + ;; For now we don't make any sync here. + changes) + moved (fn [changes child-inst child-main] (log/trace :msg "Move" :child-inst (str (:name child-inst) " " (pretty-uuid (:id child-inst))) @@ -777,6 +784,7 @@ only-inst only-main both + swapped moved false reset?)))) @@ -933,6 +941,13 @@ redirect-shaperef components-v2)) + swapped (fn [changes child-inst child-main] + (log/trace :msg "Match slot" + :child-inst (str (:name child-inst) " " (pretty-uuid (:id child-inst))) + :child-main (str (:name child-main) " " (pretty-uuid (:id child-main)))) + ;; For now we don't make any sync here. + changes) + moved (fn [changes child-inst child-main] (move-shape changes @@ -949,6 +964,7 @@ only-inst only-main both + swapped moved true true) @@ -969,7 +985,7 @@ ;; ---- Operation generation helpers ---- (defn- compare-children - [changes children-inst children-main only-inst-cb only-main-cb both-cb moved-cb inverse? reset?] + [changes children-inst children-main only-inst-cb only-main-cb both-cb swapped-cb moved-cb inverse? reset?] (log/trace :msg "Compare children") (loop [children-inst (seq (or children-inst [])) children-main (seq (or children-main [])) @@ -989,63 +1005,53 @@ (reduce only-inst-cb changes children-inst) :else - (if (ctk/is-main-of? child-main child-inst) + (if (or (ctk/is-main-of? child-main child-inst) + (ctk/match-swap-slot? child-main child-inst)) (recur (next children-inst) (next children-main) - (both-cb changes child-inst child-main)) + (if (or (ctk/is-main-of? child-main child-inst) reset?) + (both-cb changes child-inst child-main) + (swapped-cb changes child-inst child-main))) - (if (and (ctk/match-swap-slot? child-main child-inst) (not reset?)) - (do - (log/trace :msg "Match slot" - :shape-inst (str (:name child-inst) " " (pretty-uuid (:id child-inst))) - :shape-main (str (:name child-main) " " (pretty-uuid (:id child-main)))) - (recur (next children-inst) + (let [child-inst' (d/seek #(or (ctk/is-main-of? child-main %) + (ctk/match-swap-slot? child-main %)) + children-inst) + child-main' (d/seek #(or (ctk/is-main-of? % child-inst) + (ctk/match-swap-slot? % child-inst)) + children-main)] + (cond + (nil? child-inst') + (recur children-inst (next children-main) - changes)) + (only-main-cb changes child-main)) - (let [child-inst' (d/seek #(ctk/is-main-of? child-main %) children-inst) - child-main' (d/seek #(ctk/is-main-of? % child-inst) children-main)] - (cond - (nil? child-inst') - (let [matching-inst (d/seek #(ctk/match-swap-slot? % child-main) children-inst)] - (if (and (some? matching-inst) (not reset?)) - (do - (log/trace :msg "Match slot inst" - :shape-inst (str (:name child-inst') " " (pretty-uuid (:id child-inst'))) - :shape-main (str (:name child-main) " " (pretty-uuid (:id child-main)))) - (recur (remove #(= (:id %) (:id matching-inst)) children-inst) - (next children-main) - changes)) - (recur children-inst - (next children-main) - (only-main-cb changes child-main)))) + (nil? child-main') + (recur (next children-inst) + children-main + (only-inst-cb changes child-inst)) - (nil? child-main') - (let [matching-main (d/seek #(ctk/match-swap-slot? child-inst %) children-main)] - (if (and (some? matching-main) (not reset?)) - (do - (log/trace :msg "Match slot main" - :shape-inst (str (:name child-inst) " " (pretty-uuid (:id child-inst))) - :shape-main (str (:name child-main') " " (pretty-uuid (:id child-main')))) - (recur (next children-inst) - (remove #(= (:id %) (:id matching-main)) children-inst) - changes)) - (recur (next children-inst) - children-main - (only-inst-cb changes child-inst)))) - - :else - (if inverse? + :else + (if inverse? + (let [is-main? (ctk/is-main-of? child-inst child-main')] (recur (next children-inst) (remove #(= (:id %) (:id child-main')) children-main) - (-> changes - (both-cb child-inst child-main') - (moved-cb child-inst child-main'))) + (cond-> changes + is-main? + (both-cb child-inst child-main') + (not is-main?) + (swapped-cb child-inst child-main') + :always + (moved-cb child-inst child-main')))) + (let [is-main? (ctk/is-main-of? child-inst' child-main)] (recur (remove #(= (:id %) (:id child-inst')) children-inst) (next children-main) - (-> changes - (both-cb child-inst' child-main) - (moved-cb child-inst' child-main)))))))))))) + (cond-> changes + is-main? + (both-cb child-inst' child-main) + (not is-main?) + (swapped-cb child-inst' child-main) + :always + (moved-cb child-inst' child-main)))))))))))) (defn- add-shape-to-instance [changes component-shape index component-page container root-instance root-main omit-touched? set-remote-synced?] From bad0fb912ba6e5f5c8b5e487bd068f117d3f963d Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Thu, 7 Mar 2024 11:04:35 +0100 Subject: [PATCH 5/8] :tada Add undo-group for layout updates --- frontend/src/app/main/data/workspace.cljs | 10 ++--- .../src/app/main/data/workspace/changes.cljs | 2 +- .../src/app/main/data/workspace/groups.cljs | 6 +-- .../app/main/data/workspace/libraries.cljs | 11 +++--- .../data/workspace/libraries_helpers.cljs | 23 ++++++------ .../app/main/data/workspace/modifiers.cljs | 3 +- .../app/main/data/workspace/path/edition.cljs | 2 +- .../app/main/data/workspace/selection.cljs | 2 +- .../app/main/data/workspace/shape_layout.cljs | 37 ++++++++++--------- .../src/app/main/data/workspace/shapes.cljs | 17 +++++---- .../app/main/data/workspace/svg_upload.cljs | 2 +- .../src/app/main/data/workspace/texts.cljs | 2 +- .../app/main/data/workspace/transforms.cljs | 2 +- 13 files changed, 61 insertions(+), 58 deletions(-) diff --git a/frontend/src/app/main/data/workspace.cljs b/frontend/src/app/main/data/workspace.cljs index e981a1420..41fdb90ba 100644 --- a/frontend/src/app/main/data/workspace.cljs +++ b/frontend/src/app/main/data/workspace.cljs @@ -777,7 +777,7 @@ (rx/of (dwu/start-undo-transaction undo-id) (dch/commit-changes changes) - (ptk/data-event :layout/update selected-ids) + (ptk/data-event :layout/update {:ids selected-ids}) (dwu/commit-undo-transaction undo-id)))))) ;; --- Change Shape Order (D&D Ordering) @@ -987,7 +987,7 @@ (rx/of (dwu/start-undo-transaction undo-id) (dch/commit-changes changes) (dwco/expand-collapse parent-id) - (ptk/data-event :layout/update (concat all-parents ids)) + (ptk/data-event :layout/update {:ids (concat all-parents ids)}) (dwu/commit-undo-transaction undo-id)))))) (defn relocate-selected-shapes @@ -1105,7 +1105,7 @@ (when (can-align? selected objects) (rx/of (dwu/start-undo-transaction undo-id) (dwt/position-shapes moved) - (ptk/data-event :layout/update selected) + (ptk/data-event :layout/update {:ids selected}) (dwu/commit-undo-transaction undo-id))))))) (defn can-distribute? [selected] @@ -1132,7 +1132,7 @@ (when (can-distribute? selected) (rx/of (dwu/start-undo-transaction undo-id) (dwt/position-shapes moved) - (ptk/data-event :layout/update selected) + (ptk/data-event :layout/update {:ids selected}) (dwu/commit-undo-transaction undo-id))))))) ;; --- Shape Proportions @@ -2016,7 +2016,7 @@ (rx/of (dwu/start-undo-transaction undo-id) (dch/commit-changes changes) (dws/select-shapes selected) - (ptk/data-event :layout/update [frame-id]) + (ptk/data-event :layout/update {:ids [frame-id]}) (dwu/commit-undo-transaction undo-id))))))) (defn as-content [text] diff --git a/frontend/src/app/main/data/workspace/changes.cljs b/frontend/src/app/main/data/workspace/changes.cljs index 061b3c550..b2d595086 100644 --- a/frontend/src/app/main/data/workspace/changes.cljs +++ b/frontend/src/app/main/data/workspace/changes.cljs @@ -101,7 +101,7 @@ ;; Update layouts for properties marked (if (d/not-empty? update-layout-ids) - (rx/of (ptk/data-event :layout/update update-layout-ids)) + (rx/of (ptk/data-event :layout/update {:ids update-layout-ids})) (rx/empty)))))))) (defn send-update-indices diff --git a/frontend/src/app/main/data/workspace/groups.cljs b/frontend/src/app/main/data/workspace/groups.cljs index a89d845cb..c3a403269 100644 --- a/frontend/src/app/main/data/workspace/groups.cljs +++ b/frontend/src/app/main/data/workspace/groups.cljs @@ -194,7 +194,7 @@ (prepare-create-group it objects page-id shapes "Group" false)] (rx/of (dch/commit-changes changes) (dws/select-shapes (d/ordered-set (:id group))) - (ptk/data-event :layout/update parents)))))))) + (ptk/data-event :layout/update {:ids parents})))))))) (def ungroup-selected (ptk/reify ::ungroup-selected @@ -244,7 +244,7 @@ (when-not (empty? selected) (rx/of (dwu/start-undo-transaction undo-id) (dch/commit-changes changes) - (ptk/data-event :layout/update parents) + (ptk/data-event :layout/update {:ids parents}) (dwu/commit-undo-transaction undo-id) (dws/select-shapes child-ids))))))) @@ -289,7 +289,7 @@ (rx/of (dwu/start-undo-transaction undo-id) (dch/commit-changes changes) (dws/select-shapes (d/ordered-set (:id group))) - (ptk/data-event :layout/update [(:id group)]) + (ptk/data-event :layout/update {:ids [(:id group)]}) (dwu/commit-undo-transaction undo-id)))))))) (def unmask-group diff --git a/frontend/src/app/main/data/workspace/libraries.cljs b/frontend/src/app/main/data/workspace/libraries.cljs index 0d64b733d..e174df3e7 100644 --- a/frontend/src/app/main/data/workspace/libraries.cljs +++ b/frontend/src/app/main/data/workspace/libraries.cljs @@ -52,7 +52,7 @@ [potok.v2.core :as ptk])) ;; Change this to :info :debug or :trace to debug this module, or :warn to reset to default -(log/set-level! :trace) +(log/set-level! :warn) (defn- log-changes [changes file] @@ -334,7 +334,7 @@ (when-not (empty? (:redo-changes changes)) (rx/of (dch/commit-changes changes) (dws/select-shapes (d/ordered-set (:id root))) - (ptk/data-event :layout/update parents))))))))) + (ptk/data-event :layout/update {:ids parents}))))))))) (defn add-component "Add a new component to current file library, from the currently selected shapes. @@ -566,7 +566,7 @@ undo-id (js/Symbol)] (rx/of (dwu/start-undo-transaction undo-id) (dch/commit-changes changes) - (ptk/data-event :layout/update [(:id new-shape)]) + (ptk/data-event :layout/update {:ids [(:id new-shape)]}) (dws/select-shapes (d/ordered-set (:id new-shape))) (when start-move? (dwtr/start-move initial-point #{(:id new-shape)})) @@ -948,7 +948,6 @@ undo-id (js/Symbol) undo-group (uuid/next)] - (rx/of (dwu/start-undo-transaction undo-id) (dwsh/delete-shapes nil (d/ordered-set (:id shape)) {:component-swap true @@ -956,7 +955,7 @@ :undo-group undo-group}) (add-component-for-swap shape file page libraries id-new-component index target-cell keep-props-values {:undo-group undo-group}) - (ptk/data-event :layout/update [(:parent-id shape)]) + (ptk/data-event :layout/update {:ids [(:parent-id shape)] :undo-group undo-group}) (dwu/commit-undo-transaction undo-id)))))) (defn component-multi-swap @@ -1082,7 +1081,7 @@ :file-id file-id)))) (when-not (empty? updated-frames) (rx/merge - (rx/of (ptk/data-event :layout/update (map :id updated-frames))) + (rx/of (ptk/data-event :layout/update {:ids (map :id updated-frames) :undo-group undo-group})) (->> (rx/from updated-frames) (rx/mapcat (fn [shape] diff --git a/frontend/src/app/main/data/workspace/libraries_helpers.cljs b/frontend/src/app/main/data/workspace/libraries_helpers.cljs index adf8e4037..8864214a6 100644 --- a/frontend/src/app/main/data/workspace/libraries_helpers.cljs +++ b/frontend/src/app/main/data/workspace/libraries_helpers.cljs @@ -30,7 +30,7 @@ [clojure.set :as set])) ;; Change this to :info :debug or :trace to debug this module, or :warn to reset to default -(log/set-level! :trace) +(log/set-level! :warn) (declare generate-sync-container) (declare generate-sync-shape) @@ -320,16 +320,17 @@ (loop [containers (ctf/object-containers-seq file) changes (pcb/empty-changes it)] (if-let [container (first containers)] - (recur (next containers) - (pcb/concat-changes - changes - (generate-sync-container it - asset-type - asset-id - library-id - state - container - components-v2))) + (do + (recur (next containers) + (pcb/concat-changes + changes + (generate-sync-container it + asset-type + asset-id + library-id + state + container + components-v2)))) changes)))) (defn generate-sync-library diff --git a/frontend/src/app/main/data/workspace/modifiers.cljs b/frontend/src/app/main/data/workspace/modifiers.cljs index d27b980f6..6821ed0e8 100644 --- a/frontend/src/app/main/data/workspace/modifiers.cljs +++ b/frontend/src/app/main/data/workspace/modifiers.cljs @@ -454,7 +454,7 @@ ([] (apply-modifiers nil)) - ([{:keys [modifiers undo-transation? stack-undo? ignore-constraints ignore-snap-pixel] + ([{:keys [modifiers undo-transation? stack-undo? ignore-constraints ignore-snap-pixel undo-group] :or {undo-transation? true stack-undo? false ignore-constraints false ignore-snap-pixel false}}] (ptk/reify ::apply-modifiers ptk/WatchEvent @@ -504,6 +504,7 @@ {:reg-objects? true :stack-undo? stack-undo? :ignore-tree ignore-tree + :undo-group undo-group ;; Attributes that can change in the transform. This way we don't have to check ;; all the attributes :attrs [:selrect diff --git a/frontend/src/app/main/data/workspace/path/edition.cljs b/frontend/src/app/main/data/workspace/path/edition.cljs index a10b24b03..51d688707 100644 --- a/frontend/src/app/main/data/workspace/path/edition.cljs +++ b/frontend/src/app/main/data/workspace/path/edition.cljs @@ -332,7 +332,7 @@ ptk/WatchEvent (watch [_ _ _] - (rx/of (ptk/data-event :layout/update [id]))))) + (rx/of (ptk/data-event :layout/update {:ids [id]}))))) (defn split-segments [{:keys [from-p to-p t]}] diff --git a/frontend/src/app/main/data/workspace/selection.cljs b/frontend/src/app/main/data/workspace/selection.cljs index 89904f836..15d3d5ab4 100644 --- a/frontend/src/app/main/data/workspace/selection.cljs +++ b/frontend/src/app/main/data/workspace/selection.cljs @@ -751,7 +751,7 @@ (dwu/start-undo-transaction undo-id) (dch/commit-changes changes) (select-shapes new-selected) - (ptk/data-event :layout/update frames) + (ptk/data-event :layout/update {:ids frames}) (memorize-duplicated id-original id-duplicated) (dwu/commit-undo-transaction undo-id)))))))))) diff --git a/frontend/src/app/main/data/workspace/shape_layout.cljs b/frontend/src/app/main/data/workspace/shape_layout.cljs index 19e17a5a2..37e40cf91 100644 --- a/frontend/src/app/main/data/workspace/shape_layout.cljs +++ b/frontend/src/app/main/data/workspace/shape_layout.cljs @@ -94,7 +94,7 @@ ;; Never call this directly but through the data-event `:layout/update` ;; Otherwise a lot of cycle dependencies could be generated (defn- update-layout-positions - [ids] + [{:keys [ids undo-group]}] (ptk/reify ::update-layout-positions ptk/WatchEvent (watch [_ state _] @@ -103,7 +103,8 @@ (if (d/not-empty? ids) (let [modif-tree (dwm/create-modif-tree ids (ctm/reflow-modifiers))] (rx/of (dwm/apply-modifiers {:modifiers modif-tree - :stack-undo? true}))) + :stack-undo? true + :undo-group undo-group}))) (rx/empty)))))) (defn initialize @@ -139,7 +140,7 @@ (rx/of (dwu/start-undo-transaction undo-id) (dch/update-shapes [id] layout-initializer {:with-objects? true}) (dch/update-shapes (dm/get-prop parent :shapes) #(dissoc % :constraints-h :constraints-v)) - (ptk/data-event :layout/update [id]) + (ptk/data-event :layout/update {:ids [id]}) (dwu/commit-undo-transaction undo-id)))))) (defn create-layout-from-selection @@ -180,7 +181,7 @@ (dch/update-shapes [new-shape-id] #(assoc % :layout-item-h-sizing :auto :layout-item-v-sizing :auto)) (dch/update-shapes selected #(assoc % :layout-item-h-sizing :fix :layout-item-v-sizing :fix)) (dwsh/delete-shapes page-id selected) - (ptk/data-event :layout/update [new-shape-id]) + (ptk/data-event :layout/update {:ids [new-shape-id]}) (dwu/commit-undo-transaction undo-id))) ;; Create Layout from selection @@ -191,7 +192,7 @@ (dch/update-shapes [new-shape-id] #(assoc % :layout-item-h-sizing :auto :layout-item-v-sizing :auto)) (dch/update-shapes selected #(assoc % :layout-item-h-sizing :fix :layout-item-v-sizing :fix)))) - (rx/of (ptk/data-event :layout/update [new-shape-id]) + (rx/of (ptk/data-event :layout/update {:ids [new-shape-id]}) (dwu/commit-undo-transaction undo-id))))))) (defn remove-layout @@ -203,7 +204,7 @@ (rx/of (dwu/start-undo-transaction undo-id) (dch/update-shapes ids #(apply dissoc % layout-keys)) - (ptk/data-event :layout/update ids) + (ptk/data-event :layout/update {:ids ids}) (dwu/commit-undo-transaction undo-id)))))) (defn create-layout @@ -256,7 +257,7 @@ (let [undo-id (js/Symbol)] (rx/of (dwu/start-undo-transaction undo-id) (dch/update-shapes ids (d/patch-object changes)) - (ptk/data-event :layout/update ids) + (ptk/data-event :layout/update {:ids ids}) (dwu/commit-undo-transaction undo-id)))))) (defn add-layout-track @@ -275,7 +276,7 @@ (case type :row (ctl/add-grid-row shape value index) :column (ctl/add-grid-column shape value index)))) - (ptk/data-event :layout/update ids) + (ptk/data-event :layout/update {:ids ids}) (dwu/commit-undo-transaction undo-id))))))) (defn remove-layout-track @@ -309,7 +310,7 @@ :row (ctl/remove-grid-row shape index objects) :column (ctl/remove-grid-column shape index objects))) {:with-objects? true}) - (ptk/data-event :layout/update ids) + (ptk/data-event :layout/update {:ids ids}) (dwu/commit-undo-transaction undo-id))))))) (defn duplicate-layout-track @@ -363,7 +364,7 @@ undo-id (js/Symbol)] (rx/of (dwu/start-undo-transaction undo-id) (dch/commit-changes changes) - (ptk/data-event :layout/update ids) + (ptk/data-event :layout/update {:ids ids}) (dwu/commit-undo-transaction undo-id)))))) (defn reorder-layout-track @@ -381,7 +382,7 @@ (case type :row (ctl/reorder-grid-row shape from-index to-index move-content?) :column (ctl/reorder-grid-column shape from-index to-index move-content?)))) - (ptk/data-event :layout/update ids) + (ptk/data-event :layout/update {:ids ids}) (dwu/commit-undo-transaction undo-id)))))) (defn hover-layout-track @@ -426,7 +427,7 @@ (fn [shape] (-> shape (update-in [property index] merge props)))) - (ptk/data-event :layout/update ids) + (ptk/data-event :layout/update {:ids ids}) (dwu/commit-undo-transaction undo-id)))))) (defn fix-child-sizing @@ -523,7 +524,7 @@ (cond-> (ctl/grid-layout? parent) (ctl/assign-cells objects)))) {:with-objects? true}) - (ptk/data-event :layout/update ids) + (ptk/data-event :layout/update {:ids ids}) (dwu/commit-undo-transaction undo-id)))))) (defn update-grid-cells @@ -546,7 +547,7 @@ [:layout-grid-cells cell-id] d/patch-object props)) shape)))) - (ptk/data-event :layout/update [layout-id]) + (ptk/data-event :layout/update {:ids [layout-id]}) (dwu/commit-undo-transaction undo-id)))))) (defn change-cells-mode @@ -612,7 +613,7 @@ (d/update-in-when [:layout-grid-cells (:id target-cell)] assoc :position :area))))) {:with-objects? true}) (dwge/clean-selection layout-id) - (ptk/data-event :layout/update [layout-id]) + (ptk/data-event :layout/update {:ids [layout-id]}) (dwu/commit-undo-transaction undo-id)))))) (defn merge-cells @@ -644,7 +645,7 @@ (ctl/assign-cells objects)))) {:with-objects? true}) (dwge/clean-selection layout-id) - (ptk/data-event :layout/update [layout-id]) + (ptk/data-event :layout/update {:ids [layout-id]}) (dwu/commit-undo-transaction undo-id)))))) (defn update-grid-cell-position @@ -669,7 +670,7 @@ (:row-span new-data) (:column-span new-data)) (ctl/assign-cells objects)))) {:with-objects? true}) - (ptk/data-event :layout/update [layout-id]) + (ptk/data-event :layout/update {:ids [layout-id]}) (dwu/commit-undo-transaction undo-id)))))) @@ -724,5 +725,5 @@ (rx/of (dwu/start-undo-transaction undo-id) (dch/commit-changes changes) - (ptk/data-event :layout/update [layout-id]) + (ptk/data-event :layout/update {:ids [layout-id]}) (dwu/commit-undo-transaction undo-id)))))) diff --git a/frontend/src/app/main/data/workspace/shapes.cljs b/frontend/src/app/main/data/workspace/shapes.cljs index 72cbd6f7e..7febfef6f 100644 --- a/frontend/src/app/main/data/workspace/shapes.cljs +++ b/frontend/src/app/main/data/workspace/shapes.cljs @@ -57,7 +57,7 @@ (rx/of (dwu/start-undo-transaction undo-id) (dch/commit-changes changes) (when-not no-update-layout? - (ptk/data-event :layout/update [(:parent-id shape)])) + (ptk/data-event :layout/update {:ids [(:parent-id shape)]})) (when-not no-select? (dws/select-shapes (d/ordered-set (:id shape)))) (dwu/commit-undo-transaction undo-id)) @@ -141,10 +141,11 @@ (rx/concat (rx/of (dwu/start-undo-transaction undo-id) - (update-shape-flags ids-to-hide {:hidden true})) + (update-shape-flags ids-to-hide {:hidden true :undo-group (:undo-group options)})) (real-delete-shapes file page objects ids-to-delete it {:components-v2 components-v2 :ignore-touched (:component-swap options) - :undo-group (:undo-group options)}) + :undo-group (:undo-group options) + :undo-id undo-id}) (rx/of (dwu/commit-undo-transaction undo-id)))))))) (defn- real-delete-shapes-changes @@ -291,11 +292,11 @@ (defn- real-delete-shapes [file page objects ids it options] (let [[changes all-parents] (real-delete-shapes-changes file page objects ids it options) - undo-id (js/Symbol)] + undo-id (or (:undo-id options) (js/Symbol))] (rx/of (dwu/start-undo-transaction undo-id) (dc/detach-comment-thread ids) (dch/commit-changes changes) - (ptk/data-event :layout/update all-parents) + (ptk/data-event :layout/update {:ids all-parents :undo-group (:undo-group options)}) (dwu/commit-undo-transaction undo-id)))) (defn create-and-add-shape @@ -377,7 +378,7 @@ (dwu/start-undo-transaction undo-id) (dch/commit-changes changes) (dws/select-shapes (d/ordered-set (:id frame-shape))) - (ptk/data-event :layout/update [(:id frame-shape)]) + (ptk/data-event :layout/update {:ids [(:id frame-shape)]}) (dwu/commit-undo-transaction undo-id)))))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -385,7 +386,7 @@ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defn update-shape-flags - [ids {:keys [blocked hidden transforming] :as flags}] + [ids {:keys [blocked hidden transforming undo-group] :as flags}] (dm/assert! "expected valid coll of uuids" (every? uuid? ids)) @@ -409,7 +410,7 @@ ids (if (boolean? blocked) (into ids (->> ids (mapcat #(cfh/get-children-ids objects %)))) ids)] - (rx/of (dch/update-shapes ids update-fn {:attrs #{:blocked :hidden :transforming}})))))) + (rx/of (dch/update-shapes ids update-fn {:attrs #{:blocked :hidden :transforming} :undo-group undo-group})))))) (defn toggle-visibility-selected [] diff --git a/frontend/src/app/main/data/workspace/svg_upload.cljs b/frontend/src/app/main/data/workspace/svg_upload.cljs index d0a262312..fa159cf04 100644 --- a/frontend/src/app/main/data/workspace/svg_upload.cljs +++ b/frontend/src/app/main/data/workspace/svg_upload.cljs @@ -102,7 +102,7 @@ (rx/of (dwu/start-undo-transaction undo-id) (dch/commit-changes changes) (dws/select-shapes (d/ordered-set (:id new-shape))) - (ptk/data-event :layout/update [(:id new-shape)]) + (ptk/data-event :layout/update {:ids [(:id new-shape)]}) (dwu/commit-undo-transaction undo-id))) (catch :default cause diff --git a/frontend/src/app/main/data/workspace/texts.cljs b/frontend/src/app/main/data/workspace/texts.cljs index c3e98739b..e2b7e1895 100644 --- a/frontend/src/app/main/data/workspace/texts.cljs +++ b/frontend/src/app/main/data/workspace/texts.cljs @@ -382,7 +382,7 @@ :stack-undo? true :ignore-remote? true :ignore-touched true}) - (ptk/data-event :layout/update ids) + (ptk/data-event :layout/update {:ids ids}) (dwu/commit-undo-transaction undo-id)))))))) (defn resize-text diff --git a/frontend/src/app/main/data/workspace/transforms.cljs b/frontend/src/app/main/data/workspace/transforms.cljs index be9635715..fc8ee350d 100644 --- a/frontend/src/app/main/data/workspace/transforms.cljs +++ b/frontend/src/app/main/data/workspace/transforms.cljs @@ -719,7 +719,7 @@ (rx/of (dwu/start-undo-transaction undo-id) (dch/commit-changes changes) - (ptk/data-event :layout/update selected) + (ptk/data-event :layout/update {:ids selected}) (dwu/commit-undo-transaction undo-id)))))) (defn nudge-selected-shapes From 2c740df767883a50f1276cbc0a5f019d0c066880 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Moya?= Date: Thu, 7 Mar 2024 13:09:43 +0100 Subject: [PATCH 6/8] :bug: Do recursive swap-slot finding --- common/src/app/common/types/file.cljc | 20 +++++------ .../app/main/data/workspace/libraries.cljs | 6 ++-- .../data/workspace/libraries_helpers.cljs | 35 ++++++++++++++----- .../main/data/workspace/state_helpers.cljs | 5 +++ 4 files changed, 45 insertions(+), 21 deletions(-) diff --git a/common/src/app/common/types/file.cljc b/common/src/app/common/types/file.cljc index 98e8fbae0..5d8b8cd1c 100644 --- a/common/src/app/common/types/file.cljc +++ b/common/src/app/common/types/file.cljc @@ -204,7 +204,7 @@ (defn find-ref-shape "Locate the nearest component in the local file or libraries, and retrieve the shape referenced by the instance shape." - [file page libraries shape & {:keys [include-deleted? with-context?] :or {include-deleted? false with-context? false}}] + [file container libraries shape & {:keys [include-deleted? with-context?] :or {include-deleted? false with-context? false}}] (let [find-ref-shape-in-head (fn [head-shape] (let [component-file (find-component-file file libraries (:component-file head-shape)) @@ -213,7 +213,7 @@ (when (some? component) (get-ref-shape (:data component-file) component shape :with-context? with-context?))))] - (some find-ref-shape-in-head (ctn/get-parent-heads (:objects page) shape)))) + (some find-ref-shape-in-head (ctn/get-parent-heads (:objects container) shape)))) (defn find-ref-component "Locate the nearest component in the local file or libraries that is referenced by the @@ -265,14 +265,13 @@ (true? (= (:id component) (:id ref-component))))) (defn find-swap-slot - [shape page file libraries] - (dm/assert! "expected shape is head" (ctk/instance-head? shape)) + [shape container file libraries] ;; (js/console.log "find-swap-slot" (clj->js shape)) (if-let [swap-slot (ctk/get-swap-slot shape)] ;; (do (js/console.log "uno" (str swap-slot)) swap-slot) swap-slot (let [ref-shape (find-ref-shape file - page + container libraries shape :include-deleted? true @@ -292,11 +291,12 @@ (find-swap-slot ref-shape ref-container ref-file libraries))))))) (defn match-swap-slot? - [shape-main shape-inst page-inst page-main file libraries] - (let [slot-main (find-swap-slot shape-main page-main file libraries) - slot-inst (find-swap-slot shape-inst page-inst file libraries)] - (or (= slot-main slot-inst) - (= (:id shape-main) slot-inst)))) + [shape-main shape-inst container-inst container-main file libraries] + (let [slot-main (find-swap-slot shape-main container-main file libraries) + slot-inst (find-swap-slot shape-inst container-inst file libraries)] + (when (some? slot-inst) + (or (= slot-main slot-inst) + (= (:id shape-main) slot-inst))))) (defn get-component-shapes "Retrieve all shapes of the component" diff --git a/frontend/src/app/main/data/workspace/libraries.cljs b/frontend/src/app/main/data/workspace/libraries.cljs index e174df3e7..c197e19ab 100644 --- a/frontend/src/app/main/data/workspace/libraries.cljs +++ b/frontend/src/app/main/data/workspace/libraries.cljs @@ -699,6 +699,7 @@ (watch [it state _] (log/info :msg "RESET-COMPONENT of shape" :id (str id)) (let [file (wsh/get-local-file state) + file-full (wsh/get-local-file-full state) libraries (wsh/get-libraries state) page-id (:current-page-id state) @@ -711,7 +712,7 @@ (-> (pcb/empty-changes it) (pcb/with-container container) (pcb/with-objects (:objects container)) - (dwlh/generate-sync-shape-direct libraries container id true components-v2))] + (dwlh/generate-sync-shape-direct file-full libraries container id true components-v2))] (log/debug :msg "RESET-COMPONENT finished" :js/rchanges (log-changes (:redo-changes changes) @@ -750,6 +751,7 @@ (log/info :msg "UPDATE-COMPONENT of shape" :id (str id) :undo-group undo-group) (let [page-id (get state :current-page-id) local-file (wsh/get-local-file state) + full-file (wsh/get-local-file-full state) container (cfh/get-container local-file :page page-id) shape (ctn/get-shape container id) components-v2 (features/active-feature? state "components/v2")] @@ -761,7 +763,7 @@ (-> (pcb/empty-changes it) (pcb/set-undo-group undo-group) (pcb/with-container container) - (dwlh/generate-sync-shape-inverse libraries container id components-v2)) + (dwlh/generate-sync-shape-inverse full-file libraries container id components-v2)) file-id (:component-file shape) file (wsh/get-file state file-id) diff --git a/frontend/src/app/main/data/workspace/libraries_helpers.cljs b/frontend/src/app/main/data/workspace/libraries_helpers.cljs index 8864214a6..97a0ac73f 100644 --- a/frontend/src/app/main/data/workspace/libraries_helpers.cljs +++ b/frontend/src/app/main/data/workspace/libraries_helpers.cljs @@ -425,8 +425,9 @@ (defmethod generate-sync-shape :components [_ changes _library-id state container shape components-v2] (let [shape-id (:id shape) + file (wsh/get-local-file-full state) libraries (wsh/get-libraries state)] - (generate-sync-shape-direct changes libraries container shape-id false components-v2))) + (generate-sync-shape-direct changes file libraries container shape-id false components-v2))) (defmethod generate-sync-shape :colors [_ changes library-id state _ shape _] @@ -594,7 +595,7 @@ (defn generate-sync-shape-direct "Generate changes to synchronize one shape that is the root of a component instance, and all its children, from the given component." - [changes libraries container shape-id reset? components-v2] + [changes file libraries container shape-id reset? components-v2] (log/debug :msg "Sync shape direct" :shape-inst (str shape-id) :reset? reset?) (let [shape-inst (ctn/get-shape container shape-id) library (dm/get-in libraries [(:component-file shape-inst) :data]) @@ -623,6 +624,8 @@ shape-inst component library + file + libraries shape-main root-inst root-main @@ -655,7 +658,7 @@ nil)))))) (defn- generate-sync-shape-direct-recursive - [changes container shape-inst component library shape-main root-inst root-main reset? initial-root? redirect-shaperef components-v2] + [changes container shape-inst component library file libraries shape-main root-inst root-main reset? initial-root? redirect-shaperef components-v2] (log/debug :msg "Sync shape direct recursive" :shape-inst (str (:name shape-inst) " " (pretty-uuid (:id shape-inst))) :component (:name component)) @@ -752,6 +755,8 @@ child-inst component library + file + libraries child-main root-inst root-main @@ -782,6 +787,10 @@ (compare-children changes children-inst children-main + container + component-container + file + libraries only-inst only-main both @@ -812,7 +821,7 @@ (defn generate-sync-shape-inverse "Generate changes to update the component a shape is linked to, from the values in the shape and all its children." - [changes libraries container shape-id components-v2] + [changes file libraries container shape-id components-v2] (log/debug :msg "Sync shape inverse" :shape (str shape-id)) (let [redirect-shaperef (partial redirect-shaperef container libraries) shape-inst (ctn/get-shape container shape-id) @@ -843,6 +852,8 @@ shape-inst component library + file + libraries shape-main root-inst root-main @@ -852,7 +863,7 @@ changes))) (defn- generate-sync-shape-inverse-recursive - [changes container shape-inst component library shape-main root-inst root-main initial-root? redirect-shaperef components-v2] + [changes container shape-inst component library file libraries shape-main root-inst root-main initial-root? redirect-shaperef components-v2] (log/trace :msg "Sync shape inverse recursive" :shape (str (:name shape-inst)) :component (:name component)) @@ -935,6 +946,8 @@ child-inst component library + file + libraries child-main root-inst root-main @@ -962,6 +975,10 @@ (compare-children changes children-inst children-main + container + component-container + file + libraries only-inst only-main both @@ -986,7 +1003,7 @@ ;; ---- Operation generation helpers ---- (defn- compare-children - [changes children-inst children-main only-inst-cb only-main-cb both-cb swapped-cb moved-cb inverse? reset?] + [changes children-inst children-main container-inst container-main file libraries only-inst-cb only-main-cb both-cb swapped-cb moved-cb inverse? reset?] (log/trace :msg "Compare children") (loop [children-inst (seq (or children-inst [])) children-main (seq (or children-main [])) @@ -1007,7 +1024,7 @@ :else (if (or (ctk/is-main-of? child-main child-inst) - (ctk/match-swap-slot? child-main child-inst)) + (ctf/match-swap-slot? child-main child-inst container-inst container-main file libraries)) (recur (next children-inst) (next children-main) (if (or (ctk/is-main-of? child-main child-inst) reset?) @@ -1015,10 +1032,10 @@ (swapped-cb changes child-inst child-main))) (let [child-inst' (d/seek #(or (ctk/is-main-of? child-main %) - (ctk/match-swap-slot? child-main %)) + (ctf/match-swap-slot? child-main % container-inst container-main file libraries)) children-inst) child-main' (d/seek #(or (ctk/is-main-of? % child-inst) - (ctk/match-swap-slot? % child-inst)) + (ctf/match-swap-slot? % child-inst container-inst container-main file libraries)) children-main)] (cond (nil? child-inst') diff --git a/frontend/src/app/main/data/workspace/state_helpers.cljs b/frontend/src/app/main/data/workspace/state_helpers.cljs index c33f62d18..b04fa88f6 100644 --- a/frontend/src/app/main/data/workspace/state_helpers.cljs +++ b/frontend/src/app/main/data/workspace/state_helpers.cljs @@ -110,6 +110,11 @@ [state] (get state :workspace-data)) +(defn get-local-file-full + [state] + (-> (get state :workspace-file) + (assoc :data (get state :workspace-data)))) + (defn get-file "Get the data content of the given file (it may be the current file or one library)." From 2c12790782b9abbdef66aa935a7c46882fd25e25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Moya?= Date: Thu, 7 Mar 2024 17:32:52 +0100 Subject: [PATCH 7/8] :bug: Fix child compare in reset mode --- .../src/app/main/data/workspace/libraries_helpers.cljs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/frontend/src/app/main/data/workspace/libraries_helpers.cljs b/frontend/src/app/main/data/workspace/libraries_helpers.cljs index 97a0ac73f..d157dcd3d 100644 --- a/frontend/src/app/main/data/workspace/libraries_helpers.cljs +++ b/frontend/src/app/main/data/workspace/libraries_helpers.cljs @@ -1024,18 +1024,18 @@ :else (if (or (ctk/is-main-of? child-main child-inst) - (ctf/match-swap-slot? child-main child-inst container-inst container-main file libraries)) + (and (ctf/match-swap-slot? child-main child-inst container-inst container-main file libraries) (not reset?))) (recur (next children-inst) (next children-main) - (if (or (ctk/is-main-of? child-main child-inst) reset?) + (if (ctk/is-main-of? child-main child-inst) (both-cb changes child-inst child-main) (swapped-cb changes child-inst child-main))) (let [child-inst' (d/seek #(or (ctk/is-main-of? child-main %) - (ctf/match-swap-slot? child-main % container-inst container-main file libraries)) + (and (ctf/match-swap-slot? child-main % container-inst container-main file libraries) (not reset?))) children-inst) child-main' (d/seek #(or (ctk/is-main-of? % child-inst) - (ctf/match-swap-slot? % child-inst container-inst container-main file libraries)) + (and (ctf/match-swap-slot? % child-inst container-inst container-main file libraries) (not reset?))) children-main)] (cond (nil? child-inst') From 8529927173938d8b52fb0943d803a2fd778f9c19 Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Fri, 8 Mar 2024 11:56:44 +0100 Subject: [PATCH 8/8] :recycle: Avoid unnecessary logs --- common/src/app/common/types/file.cljc | 6 ------ frontend/src/app/main/data/workspace/libraries.cljs | 1 - 2 files changed, 7 deletions(-) diff --git a/common/src/app/common/types/file.cljc b/common/src/app/common/types/file.cljc index 5d8b8cd1c..676db6dc7 100644 --- a/common/src/app/common/types/file.cljc +++ b/common/src/app/common/types/file.cljc @@ -266,9 +266,7 @@ (defn find-swap-slot [shape container file libraries] - ;; (js/console.log "find-swap-slot" (clj->js shape)) (if-let [swap-slot (ctk/get-swap-slot shape)] - ;; (do (js/console.log "uno" (str swap-slot)) swap-slot) swap-slot (let [ref-shape (find-ref-shape file container @@ -279,12 +277,8 @@ shape-meta (meta ref-shape) ref-file (:file shape-meta) ref-container (:container shape-meta)] - ;; (js/console.log "ref-shape" (clj->js ref-shape)) - ;; (js/console.log "ref-file" (clj->js ref-file)) - ;; (js/console.log "ref-container" (clj->js ref-container)) (when ref-shape (if-let [swap-slot (ctk/get-swap-slot ref-shape)] - ;; (do (js/console.log "dos" (str swap-slot)) swap-slot) swap-slot (if (ctk/main-instance? ref-shape) (:id shape) diff --git a/frontend/src/app/main/data/workspace/libraries.cljs b/frontend/src/app/main/data/workspace/libraries.cljs index c197e19ab..c27c00b0f 100644 --- a/frontend/src/app/main/data/workspace/libraries.cljs +++ b/frontend/src/app/main/data/workspace/libraries.cljs @@ -905,7 +905,6 @@ libraries) (ctk/build-swap-slot-group)))) - ;; _ (js/console.log "new-shape" (str (:id new-shape)) (clj->js new-shape)) changes (-> changes ;; Restore the properties