diff --git a/backend/src/app/services/mutations/files.clj b/backend/src/app/services/mutations/files.clj index 27da20bd5f..38d971c0e3 100644 --- a/backend/src/app/services/mutations/files.clj +++ b/backend/src/app/services/mutations/files.clj @@ -251,7 +251,8 @@ :add-media :mod-media :del-media :add-component :mod-component :del-component :add-typography :mod-typography :del-typography} (:type change)) - (and (= (:type change) :mod-obj) + (and (#{:add-obj :mod-obj :del-obj + :reg-objects :mov-objects} (:type change)) (some? (:component-id change))))) (declare update-file) diff --git a/common/app/common/pages.cljc b/common/app/common/pages.cljc index 311d9e9673..0188a1eab4 100644 --- a/common/app/common/pages.cljc +++ b/common/app/common/pages.cljc @@ -340,6 +340,7 @@ :rx :radius-group :ry :radius-group :masked-group? :mask-group}) + ;; shapes-group is handled differently (s/def ::minimal-shape (s/keys :req-un [::type ::name] @@ -435,6 +436,14 @@ :internal.file/recent-colors :internal.file/media])) +(s/def :internal.container/type #{:page :component}) + +(s/def ::container + (s/keys :req-un [:internal.container/type + ::id + ::name + :internal.page/objects])) + (defmulti operation-spec :type) (s/def :internal.operations.set/attr keyword?) @@ -460,29 +469,52 @@ (s/def :internal.changes.add-obj/obj ::shape) +(defn- valid-container-id-frame? + [o] + (or (and (contains? o :page-id) + (not (contains? o :component-id)) + (some? (:frame-id o))) + (and (contains? o :component-id) + (not (contains? o :page-id)) + (nil? (:frame-id o))))) + +(defn- valid-container-id? + [o] + (or (and (contains? o :page-id) + (not (contains? o :component-id))) + (and (contains? o :component-id) + (not (contains? o :page-id))))) + (defmethod change-spec :add-obj [_] - (s/keys :req-un [::id ::page-id ::frame-id - :internal.changes.add-obj/obj] - :opt-un [::parent-id])) + (s/and (s/keys :req-un [::id :internal.changes.add-obj/obj] + :opt-un [::page-id ::component-id ::parent-id ::frame-id]) + valid-container-id-frame?)) (s/def ::operation (s/multi-spec operation-spec :type)) (s/def ::operations (s/coll-of ::operation)) (defmethod change-spec :mod-obj [_] - (s/keys :req-un [::id (or ::page-id ::component-id) ::operations])) + (s/and (s/keys :req-un [::id ::operations] + :opt-un [::page-id ::component-id]) + valid-container-id?)) (defmethod change-spec :del-obj [_] - (s/keys :req-un [::id ::page-id])) + (s/and (s/keys :req-un [::id] + :opt-un [::page-id ::component-id]) + valid-container-id?)) (s/def :internal.changes.reg-objects/shapes (s/coll-of uuid? :kind vector?)) (defmethod change-spec :reg-objects [_] - (s/keys :req-un [::page-id :internal.changes.reg-objects/shapes])) + (s/and (s/keys :req-un [:internal.changes.reg-objects/shapes] + :opt-un [::page-id ::component-id]) + valid-container-id?)) (defmethod change-spec :mov-objects [_] - (s/keys :req-un [::page-id ::parent-id :internal.shape/shapes] - :opt-un [::index])) + (s/and (s/keys :req-un [::parent-id :internal.shape/shapes] + :opt-un [::page-id ::component-id ::index]) + valid-container-id?)) (defmethod change-spec :add-page [_] (s/or :empty (s/keys :req-un [::id ::name]) @@ -701,26 +733,33 @@ (assoc data :options (d/dissoc-in (:options data) path))))))) (defmethod process-change :add-obj - [data {:keys [id obj page-id frame-id parent-id index] :as change}] - (d/update-in-when data [:pages-index page-id] - (fn [data] - (let [parent-id (or parent-id frame-id) - objects (:objects data)] - (when (and (contains? objects parent-id) - (contains? objects frame-id)) - (let [obj (assoc obj - :frame-id frame-id - :parent-id parent-id - :id id)] - (-> data - (update :objects assoc id obj) - (update-in [:objects parent-id :shapes] - (fn [shapes] - (let [shapes (or shapes [])] - (cond - (some #{id} shapes) shapes - (nil? index) (conj shapes id) - :else (cph/insert-at-index shapes index [id])))))))))))) + [data {:keys [id obj page-id component-id frame-id parent-id + index ignore-touched] :as change}] + (let [update-fn (fn [data] + (let [parent-id (or parent-id frame-id) + objects (:objects data)] + (let [obj (assoc obj + :frame-id frame-id + :parent-id parent-id + :id id)] + (-> data + (update :objects assoc id obj) + (update-in [:objects parent-id :shapes] + (fn [shapes] + (let [shapes (or shapes [])] + (cond + (some #{id} shapes) shapes + (nil? index) (conj shapes id) + :else (cph/insert-at-index shapes index [id]))))) + (cond-> + (and (:shape-ref (get-in data [:objects parent-id])) + (not= parent-id frame-id) + (not ignore-touched)) + (update-in [:objects parent-id :touched] + cph/set-touched-group :shapes-group))))))] + (if page-id + (d/update-in-when data [:pages-index page-id] update-fn) + (d/update-in-when data [:components component-id] update-fn)))) (defmethod process-change :mod-obj [data {:keys [id page-id component-id operations] :as change}] @@ -733,8 +772,8 @@ (d/update-in-when data [:components component-id :objects] update-fn)))) (defmethod process-change :del-obj - [data {:keys [page-id id] :as change}] - (letfn [(delete-object [objects id] + [data {:keys [page-id component-id id ignore-touched] :as change}] + (letfn [(delete-object [objects] (if-let [target (get objects id)] (let [parent-id (cph/get-parent id objects) frame-id (:frame-id target) @@ -745,6 +784,9 @@ (= :group (:type parent))) (update-in [parent-id :shapes] (fn [s] (filterv #(not= % id) s))) + (and (:shape-ref parent) (not ignore-touched)) + (update-in [parent-id :touched] cph/set-touched-group :shapes-group) + (contains? objects frame-id) (update-in [frame-id :shapes] (fn [s] (filterv #(not= % id) s))) @@ -752,7 +794,9 @@ ; dependend objects (as-> $ (reduce delete-object $ (:shapes target))))) objects))] - (d/update-in-when data [:pages-index page-id :objects] delete-object id))) + (if page-id + (d/update-in-when data [:pages-index page-id :objects] delete-object) + (d/update-in-when data [:components component-id :objects] delete-object)))) (defn rotation-modifiers [center shape angle] @@ -765,7 +809,7 @@ ;; reg-objects operation "regenerates" the values for the parent groups (defmethod process-change :reg-objects - [data {:keys [page-id shapes]}] + [data {:keys [page-id component-id shapes]}] (letfn [(reg-objects [objects] (reduce #(update %1 %2 update-group %1) objects (sequence (comp @@ -797,10 +841,12 @@ (assoc-in [:modifiers :rotation] (:rotation group 0)) (geom/transform-shape))))] - (d/update-in-when data [:pages-index page-id :objects] reg-objects))) + (if page-id + (d/update-in-when data [:pages-index page-id :objects] reg-objects) + (d/update-in-when data [:components component-id :objects] reg-objects)))) (defmethod process-change :mov-objects - [data {:keys [parent-id shapes index page-id] :as change}] + [data {:keys [parent-id shapes index page-id component-id ignore-touched] :as change}] (letfn [(is-valid-move? [objects shape-id] (let [invalid-targets (cph/calculate-invalid-targets shape-id objects)] (and (not (invalid-targets parent-id)) @@ -827,6 +873,14 @@ (strip-id [coll id] (filterv #(not= % id) coll)) + (add-to-parent [parent index shapes] + (cond-> parent + true + (update :shapes check-insert-items parent index shapes) + + (and (:shape-ref parent) (= (:type parent) :group) (not ignore-touched)) + (update :touched cph/set-touched-group :shapes-group))) + (remove-from-old-parent [cpindex objects shape-id] (let [prev-parent-id (get cpindex shape-id)] ;; Do nothing if the parent id of the shape is the same as @@ -843,7 +897,15 @@ (recur pid (:parent-id obj) (dissoc objects pid)) - (update-in objects [pid :shapes] strip-id sid))))))) + (cond-> objects + true + (update-in [pid :shapes] strip-id sid) + + (and (:shape-ref obj) + (= (:type obj) :group) + (not ignore-touched)) + (update-in [pid :touched] + cph/set-touched-group :shapes-group)))))))) (update-parent-id [objects id] (update objects id assoc :parent-id parent-id)) @@ -875,13 +937,15 @@ (if valid? (as-> objects $ - (update-in $ [parent-id :shapes] check-insert-items parent index shapes) + (update $ parent-id #(add-to-parent % index shapes)) (reduce update-parent-id $ shapes) (reduce (partial remove-from-old-parent cpindex) $ shapes) (reduce (partial update-frame-ids frm-id) $ (get-in $ [parent-id :shapes]))) objects)))] - (d/update-in-when data [:pages-index page-id :objects] move-objects))) + (if page-id + (d/update-in-when data [:pages-index page-id :objects] move-objects) + (d/update-in-when data [:components component-id :objects] move-objects)))) (defmethod process-change :add-page [data {:keys [id name page]}] @@ -1001,7 +1065,7 @@ (cond-> shape (and shape-ref group (not ignore) (not= val (get shape attr))) - (update :touched #(conj (or % #{}) group)) + (update :touched cph/set-touched-group group) (nil? val) (dissoc attr) diff --git a/common/app/common/pages_helpers.cljc b/common/app/common/pages_helpers.cljc index e9722ec2dc..8c2f1e5619 100644 --- a/common/app/common/pages_helpers.cljc +++ b/common/app/common/pages_helpers.cljc @@ -42,11 +42,26 @@ objects) nil))) +(defn make-container + [page-or-component type] + (assoc page-or-component + :type type)) + +(defn page? + [container] + (assert (some? (:type container))) + (= (:type container) :page)) + +(defn component? + [container] + (= (:type container) :component)) + (defn get-container - [page-id component-id local-file] - (if (some? page-id) - (get-in local-file [:pages-index page-id]) - (get-in local-file [:components component-id]))) + [id type local-file] + (-> (if (= type :page) + (get-in local-file [:pages-index id]) + (get-in local-file [:components id])) + (assoc :type type))) (defn get-shape [container shape-id] @@ -59,6 +74,12 @@ (get-in libraries [file-id :data]))] (get-in file [:components component-id]))) +(defn is-master-of + [shape-master shape-inst] + (and (:shape-ref shape-inst) + (or (= (:shape-ref shape-inst) (:id shape-master)) + (= (:shape-ref shape-inst) (:shape-ref shape-master))))) + (defn get-component-root [component] (get-in component [:objects (:id component)])) @@ -75,12 +96,12 @@ (defn get-children-objects "Retrieve all children objects recursively for a given object" [id objects] - (map #(get objects %) (get-children id objects))) + (mapv #(get objects %) (get-children id objects))) (defn get-object-with-children - "Retrieve a list with an object and all of its children" + "Retrieve a vector with an object and all of its children" [id objects] - (map #(get objects %) (cons id (get-children id objects)))) + (mapv #(get objects %) (cons id (get-children id objects)))) (defn is-shape-grouped "Checks if a shape is inside a group" @@ -210,17 +231,17 @@ :parent-id parent-id) (some? (:shapes object)) - (assoc :shapes (map :id new-direct-children))) + (assoc :shapes (mapv :id new-direct-children))) new-object (update-new-object new-object object) - new-objects (concat [new-object] new-children) + new-objects (d/concat [new-object] new-children) updated-object (update-original-object object new-object) updated-objects (if (identical? object updated-object) updated-children - (concat [updated-object] updated-children))] + (d/concat [updated-object] updated-children))] [new-object new-objects updated-objects]) @@ -232,9 +253,9 @@ (recur (next child-ids) - (concat new-direct-children [new-child]) - (concat new-children new-child-objects) - (concat updated-children updated-child-objects)))))))) + (d/concat new-direct-children [new-child]) + (d/concat new-children new-child-objects) + (d/concat updated-children updated-child-objects)))))))) (defn indexed-shapes @@ -277,3 +298,12 @@ (d/seek #(gsh/has-point? % position)) :id) uuid/zero))) + +(defn set-touched-group + [touched group] + (conj (or touched #{}) group)) + +(defn touched-group? + [shape group] + ((or (:touched shape) #{}) group)) + diff --git a/frontend/src/app/main/data/workspace.cljs b/frontend/src/app/main/data/workspace.cljs index f75a89a672..fbb8c9e3a7 100644 --- a/frontend/src/app/main/data/workspace.cljs +++ b/frontend/src/app/main/data/workspace.cljs @@ -711,6 +711,7 @@ (reduce (fn [res id] (let [children (cph/get-children id objects) parents (cph/get-parents id objects) + parent (get objects (first parents)) add-change (fn [id] (let [item (get objects id)] {:type :add-obj @@ -726,7 +727,13 @@ (map add-change children) [{:type :reg-objects :page-id page-id - :shapes (vec parents)}]))) + :shapes (vec parents)}] + (when (some? parent) + [{:type :mod-obj + :page-id page-id + :id (:id parent) + :operations [{:type :set-touched + :touched (:touched parent)}]}])))) [] ids) (map #(array-map diff --git a/frontend/src/app/main/data/workspace/libraries.cljs b/frontend/src/app/main/data/workspace/libraries.cljs index 9bcf04238b..3eb8f60db7 100644 --- a/frontend/src/app/main/data/workspace/libraries.cljs +++ b/frontend/src/app/main/data/workspace/libraries.cljs @@ -32,6 +32,7 @@ [cljs.spec.alpha :as s] [potok.core :as ptk])) +;; Change this to :info :debug or :trace to debug this module (log/set-level! :warn) (declare sync-file) @@ -397,13 +398,15 @@ :page-id page-id :frame-id (:frame-id obj) :parent-id (:parent-id obj) + :ignore-touched true :obj obj}) new-shapes) uchanges (map (fn [obj] {:type :del-obj :id (:id obj) - :page-id page-id}) + :page-id page-id + :ignore-touched true}) new-shapes)] (rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true}) @@ -493,16 +496,18 @@ (ptk/reify ::reset-component ptk/WatchEvent (watch [_ state stream] - ;; ===== Uncomment this to debug ===== (log/info :msg "RESET-COMPONENT of shape" :id (str id)) - (let [[rchanges uchanges] - (dwlh/generate-sync-shape-and-children-components (get state :current-page-id) - nil - id - (get state :workspace-data) - (get state :workspace-libraries) - true)] - ;; ===== Uncomment this to debug ===== + (let [local-file (get state :workspace-data) + libraries (get state :workspace-libraries) + container (cph/get-container (get state :current-page-id) + :page + local-file) + [rchanges uchanges] + (dwlh/generate-sync-shape-direct container + id + local-file + libraries + true)] (log/debug :msg "RESET-COMPONENT finished" :js/rchanges rchanges) (rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true})))))) @@ -516,7 +521,6 @@ (ptk/reify ::update-component ptk/WatchEvent (watch [_ state stream] - ;; ===== Uncomment this to debug ===== (log/info :msg "UPDATE-COMPONENT of shape" :id (str id)) (let [page-id (:current-page-id state) objects (dwc/lookup-page-objects state page-id) @@ -529,7 +533,6 @@ (get state :workspace-data) (get state :workspace-libraries))] - ;; ===== Uncomment this to debug ===== (log/debug :msg "UPDATE-COMPONENT finished" :js/rchanges rchanges) (rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true})))))) @@ -552,7 +555,6 @@ ptk/WatchEvent (watch [_ state stream] - ;; ===== Uncomment this to debug ===== (log/info :msg "SYNC-FILE" :file (str (or file-id "local"))) (let [library-changes [(dwlh/generate-sync-library :components file-id state) (dwlh/generate-sync-library :colors file-id state) @@ -566,7 +568,6 @@ uchanges (d/concat [] (->> library-changes (remove nil?) (map second) (flatten)) (->> file-changes (remove nil?) (map second) (flatten)))] - ;; ===== Uncomment this to debug ===== (log/debug :msg "SYNC-FILE finished" :js/rchanges rchanges) (rx/concat (rx/of (dm/hide-tag :sync-dialog)) @@ -593,14 +594,12 @@ (ptk/reify ::sync-file-2nd-stage ptk/WatchEvent (watch [_ state stream] - ;; ===== Uncomment this to debug ===== (log/info :msg "SYNC-FILE (2nd stage)" :file (str (or file-id "local"))) (let [[rchanges1 uchanges1] (dwlh/generate-sync-file :components nil state) [rchanges2 uchanges2] (dwlh/generate-sync-library :components file-id state) rchanges (d/concat rchanges1 rchanges2) uchanges (d/concat uchanges1 uchanges2)] (when rchanges - ;; ===== Uncomment this to debug ===== (log/debug :msg "SYNC-FILE (2nd stage) finished" :js/rchanges rchanges) (rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true}))))))) diff --git a/frontend/src/app/main/data/workspace/libraries_helpers.cljs b/frontend/src/app/main/data/workspace/libraries_helpers.cljs index 9298399738..bd6608a3b5 100644 --- a/frontend/src/app/main/data/workspace/libraries_helpers.cljs +++ b/frontend/src/app/main/data/workspace/libraries_helpers.cljs @@ -18,6 +18,7 @@ [app.util.logging :as log] [app.util.text :as ut])) +;; Change this to :info :debug or :trace to debug this module (log/set-level! :warn) (defonce empty-changes [[] []]) @@ -36,17 +37,18 @@ (declare has-asset-reference-fn) (declare get-assets) -(declare generate-sync-shape-and-children-components) -(declare generate-sync-shape-and-children-normal) -(declare generate-sync-shape-and-children-nested) +(declare generate-sync-shape-direct) +(declare generate-sync-shape-direct-recursive) (declare generate-sync-shape-inverse) -(declare generate-sync-shape-inverse-normal) -(declare generate-sync-shape-inverse-nested) -(declare generate-sync-shape<-component) -(declare generate-sync-shape->component) -(declare remove-component-and-ref) -(declare remove-ref) -(declare reset-touched) +(declare generate-sync-shape-inverse-recursive) + +(declare compare-children) +(declare concat-changes) +(declare add-shape-to-instance) +(declare add-shape-to-master) +(declare remove-shape) +(declare move-shape) +(declare change-touched) (declare update-attrs) (declare calc-new-pos) @@ -133,9 +135,7 @@ (generate-sync-container asset-type library-id state - page - (:id page) - nil)] + (cph/make-container page :page))] (recur (next pages) (d/concat rchanges page-rchanges) (d/concat uchanges page-uchanges))) @@ -165,9 +165,8 @@ (generate-sync-container asset-type library-id state - local-component - nil - (:id local-component))] + (cph/make-container local-component + :component))] (recur (next local-components) (d/concat rchanges comp-rchanges) (d/concat uchanges comp-uchanges))) @@ -176,11 +175,11 @@ (defn- generate-sync-container "Generate changes to synchronize all shapes in a particular container (a page or a component) that are linked to the given library." - [asset-type library-id state container page-id component-id] + [asset-type library-id state container] - (if page-id - (log/debug :msg "Sync page in local file" :page-id page-id) - (log/debug :msg "Sync component in local library" :component-id component-id)) + (if (cph/page? container) + (log/debug :msg "Sync page in local file" :page-id (:id container)) + (log/debug :msg "Sync component in local library" :component-id (:id container))) (let [has-asset-reference? (has-asset-reference-fn asset-type library-id) linked-shapes (cph/select-objects has-asset-reference? container)] @@ -192,9 +191,7 @@ (generate-sync-shape asset-type library-id state - (get container :objects) - page-id - component-id + container shape)] (recur (next shapes) (d/concat rchanges shape-rchanges) @@ -241,45 +238,46 @@ (defmulti generate-sync-shape "Generate changes to synchronize one shape, that use the given type of asset of the given library." - (fn [type _ _ _ _ _ _ _] type)) + (fn [type _ _ _ _] type)) (defmethod generate-sync-shape :components - [_ library-id state objects page-id component-id shape] - (generate-sync-shape-and-children-components page-id - component-id - (:id shape) - (get state :workspace-data) - (get state :workspace-libraries) - false)) + [_ library-id state container shape] + (generate-sync-shape-direct container + (:id shape) + (get state :workspace-data) + (get state :workspace-libraries) + false)) -(defn- generate-sync-text-shape [shape page-id component-id update-node] +(defn- generate-sync-text-shape [shape container update-node] (let [old-content (:content shape) new-content (ut/map-node update-node old-content) - rchanges [(d/without-nils {:type :mod-obj - :page-id page-id - :component-id component-id - :id (:id shape) - :operations [{:type :set - :attr :content - :val new-content}]})] - lchanges [(d/without-nils {:type :mod-obj - :page-id page-id - :component-id component-id - :id (:id shape) - :operations [{:type :set - :attr :content - :val old-content}]})]] + rchanges [(as-> {:type :mod-obj + :id (:id shape) + :operations [{:type :set + :attr :content + :val new-content}]} $ + (if (cph/page? container) + (assoc $ :page-id (:id container)) + (assoc $ :component-id (:id container))))] + uchanges [(as-> {:type :mod-obj + :id (:id shape) + :operations [{:type :set + :attr :content + :val old-content}]} $ + (if (cph/page? container) + (assoc $ :page-id (:id container)) + (assoc $ :component-id (:id container))))]] + (if (= new-content old-content) empty-changes - [rchanges lchanges]))) - + [rchanges uchanges]))) (defmethod generate-sync-shape :colors - [_ library-id state _ page-id component-id shape] + [_ library-id state container shape] ;; Synchronize a shape that uses some colors of the library. The value of the ;; color in the library is copied to the shape. - (let [colors (get-assets library-id :colors state)] + (let [colors (get-assets library-id :colors state)] (if (= :text (:type shape)) (let [update-node (fn [node] (if-let [color (get colors (:fill-color-ref-id node))] @@ -288,7 +286,7 @@ :fill-opacity (:opacity color) :fill-color-gradient (:gradient color)) node))] - (generate-sync-text-shape shape page-id component-id update-node)) + (generate-sync-text-shape shape container update-node)) (loop [attrs (seq color-sync-attrs) roperations [] uoperations []] @@ -296,16 +294,18 @@ (if (nil? attr) (if (empty? roperations) empty-changes - (let [rchanges [(d/without-nils {:type :mod-obj - :page-id page-id - :component-id component-id - :id (:id shape) - :operations roperations})] - uchanges [(d/without-nils {:type :mod-obj - :page-id page-id - :component-id component-id - :id (:id shape) - :operations uoperations})]] + (let [rchanges [(as-> {:type :mod-obj + :id (:id shape) + :operations roperations} $ + (if (cph/page? container) + (assoc $ :page-id (:id container)) + (assoc $ :component-id (:id container))))] + uchanges [(as-> {:type :mod-obj + :id (:id shape) + :operations uoperations} $ + (if (cph/page? container) + (assoc $ :page-id (:id container)) + (assoc $ :component-id (:id container))))]] [rchanges uchanges])) (if-not (contains? shape attr-ref-id) (recur (next attrs) @@ -325,7 +325,7 @@ (conj uoperations uoperation)))))))))) (defmethod generate-sync-shape :typographies - [_ library-id state _ page-id component-id shape] + [_ library-id state container shape] ;; Synchronize a shape that uses some typographies of the library. The attributes ;; of the typography are copied to the shape." @@ -334,7 +334,7 @@ (if-let [typography (get typographies (:typography-ref-id node))] (merge node (d/without-keys typography [:name :id])) node))] - (generate-sync-text-shape shape page-id component-id update-node))) + (generate-sync-text-shape shape container update-node))) ;; ---- Component synchronization helpers ---- @@ -345,7 +345,7 @@ (get-in state [:workspace-data asset-type]) (get-in state [:workspace-libraries library-id :data asset-type]))) -(defn generate-sync-shape-and-children-components +(defn generate-sync-shape-direct "Generate changes to synchronize one shape that the root of a component instance, and all its children, from the given component. If reset? is false, all atributes of each component shape that have @@ -353,121 +353,111 @@ be copied to this one. If reset? is true, all changed attributes will be copied and the 'touched' flags in the instance shape will be cleared." - [page-id component-id shape-id local-file libraries reset?] - (log/debug :msg "Sync shape and children" :shape (str shape-id) :reset? reset?) - (let [container (cph/get-container page-id component-id local-file) - shape (cph/get-shape container shape-id) - component (cph/get-component (:component-id shape) - (:component-file shape) - local-file - libraries) - root-shape shape - root-component (cph/get-component-root component)] + [container shape-id local-file libraries reset?] + (log/debug :msg "Sync shape direct" :shape (str shape-id) :reset? reset?) + (let [shape-inst (cph/get-shape container shape-id) + component (cph/get-component (:component-id shape-inst) + (:component-file shape-inst) + local-file + libraries) + shape-master (cph/get-shape component (:shape-ref shape-inst)) - (generate-sync-shape-and-children-normal page-id - component-id - container - shape - component - root-shape - root-component - reset?))) + root-inst shape-inst + root-master (cph/get-component-root component)] -(defn- generate-sync-shape-and-children-normal - [page-id component-id container shape component root-shape root-component reset?] - (log/trace :msg "Sync shape (normal)" - :shape (str (:name shape)) - :component (:name component)) - (let [[rchanges uchanges] - (generate-sync-shape<-component shape - root-shape - root-component - component - page-id - component-id - reset?) + (generate-sync-shape-direct-recursive container + shape-inst + component + shape-master + root-inst + root-master + {:omit-touched? (not reset?) + :reset-touched? reset? + :copy-touched? false}))) - children-ids (get shape :shapes [])] +(defn- generate-sync-shape-direct-recursive + [container shape-inst component shape-master root-inst root-master + {:keys [omit-touched? reset-touched? copy-touched?] + :as options :or {omit-touched? false + reset-touched? false + copy-touched? false}}] + (log/trace :msg "Sync shape direct recursive" + :shape (str (:name shape-inst)) + :component (:name component) + :options options) - (loop [children-ids (seq children-ids) - rchanges rchanges - uchanges uchanges] - (let [child-id (first children-ids)] - (if (nil? child-id) - [rchanges uchanges] - (let [child-shape (cph/get-shape container child-id) - - [child-rchanges child-uchanges] - (if (nil? (:component-id child-shape)) - (generate-sync-shape-and-children-normal page-id - component-id - container - child-shape - component - root-shape - root-component - reset?) - (generate-sync-shape-and-children-nested page-id - component-id - container - child-shape - component - root-shape - root-component - reset?))] - (recur (next children-ids) - (d/concat rchanges child-rchanges) - (d/concat uchanges child-uchanges)))))))) - -(defn- generate-sync-shape-and-children-nested - [page-id component-id container shape component root-shape root-component reset?] - (log/trace :msg "Sync shape (nested)" - :shape (str (:name shape)) - :component (:name component)) - (let [component-shape (d/seek #(= (:shape-ref %) - (:shape-ref shape)) - (vals (:objects component))) - root-shape (if (:component-id shape) - shape - root-shape) - root-component (if (:component-id shape) - component-shape - root-component) + (let [root-inst (if (:component-id shape-inst) + shape-inst + root-inst) + root-master (if (:component-id shape-inst) + shape-master + root-master) [rchanges uchanges] - (update-attrs shape - component-shape - root-shape - root-component - page-id - component-id - {:omit-touched? false - :reset-touched? false - :set-touched? false - :copy-touched? true}) + (concat-changes + (update-attrs shape-inst + shape-master + root-inst + root-master + container + options) + (change-touched shape-inst + shape-master + container + options)) - children-ids (get shape :shapes [])] + children-inst (mapv #(cph/get-shape container %) + (:shapes shape-inst)) + children-master (mapv #(cph/get-shape component %) + (:shapes shape-master)) - (loop [children-ids (seq children-ids) - rchanges rchanges - uchanges uchanges] - (let [child-id (first children-ids)] - (if (nil? child-id) - [rchanges uchanges] - (let [child-shape (cph/get-shape container child-id) + only-inst (fn [shape-inst] + (remove-shape shape-inst + container + omit-touched?)) - [child-rchanges child-uchanges] - (generate-sync-shape-and-children-nested page-id - component-id - container - child-shape - component - root-shape - root-component - reset?)] - (recur (next children-ids) - (d/concat rchanges child-rchanges) - (d/concat uchanges child-uchanges)))))))) + only-master (fn [shape-master] + (add-shape-to-instance shape-master + component + container + root-inst + root-master + omit-touched?)) + + both (fn [shape-inst shape-master] + (let [options (if-not (:component-id shape-inst) + options + {:omit-touched? false + :reset-touched? false + :copy-touched? true})] + + (generate-sync-shape-direct-recursive container + shape-inst + component + shape-master + root-inst + root-master + options))) + + moved (fn [shape-inst shape-master] + (move-shape + shape-inst + (d/index-of children-inst shape-inst) + (d/index-of children-master shape-master) + container + omit-touched?)) + + [child-rchanges child-uchanges] + (compare-children children-inst + children-master + only-inst + only-master + both + moved + false)] + + [(d/concat rchanges child-rchanges) + (d/concat uchanges child-uchanges)])) (defn- generate-sync-shape-inverse "Generate changes to update the component a shape is linked to, from @@ -478,229 +468,479 @@ And if the component shapes are, in turn, instances of a second component, their 'touched' flags will be set accordingly." [page-id shape-id local-file libraries] - (log/debug :msg "Sync inverse shape and children" :shape (str shape-id)) - (let [page (cph/get-container page-id nil local-file) - shape (cph/get-shape page shape-id) - component (cph/get-component (:component-id shape) - (:component-file shape) + (log/debug :msg "Sync shape inverse" :shape (str shape-id)) + (let [container (cph/get-container page-id :page local-file) + shape-inst (cph/get-shape container shape-id) + component (cph/get-component (:component-id shape-inst) + (:component-file shape-inst) local-file libraries) - root-shape shape - root-component (cph/get-component-root component)] + shape-master (cph/get-shape component (:shape-ref shape-inst)) - (generate-sync-shape-inverse-normal page - shape - component - root-shape - root-component))) + root-inst shape-inst + root-master (cph/get-component-root component)] -(defn- generate-sync-shape-inverse-normal - [page shape component root-shape root-component] - (log/trace :msg "Sync shape inverse (normal)" - :shape (str (:name shape)) - :component (:name component)) - (let [[rchanges uchanges] - (generate-sync-shape->component shape - root-shape - root-component - component - (:id page)) + (generate-sync-shape-inverse-recursive container + shape-inst + component + shape-master + root-inst + root-master + {:reset-touched? false + :set-touched? true + :copy-touched? false}))) - children-ids (get shape :shapes [])] +(defn- generate-sync-shape-inverse-recursive + [container shape-inst component shape-master root-inst root-master + {:keys [reset-touched? set-touched? copy-touched?] + :as options :or {reset-touched? false + set-touched? false + copy-touched? false}}] + (log/trace :msg "Sync shape inverse recursive" + :shape (str (:name shape-inst)) + :component (:name component) + :options options) - (loop [children-ids (seq children-ids) - rchanges rchanges - uchanges uchanges] - (let [child-id (first children-ids)] - (if (nil? child-id) - [rchanges uchanges] - (let [child-shape (cph/get-shape page child-id) + (let [root-inst (if (:component-id shape-inst) + shape-inst + root-inst) + root-master (if (:component-id shape-inst) + shape-master + root-master) - [child-rchanges child-uchanges] - (if (nil? (:component-id child-shape)) - (generate-sync-shape-inverse-normal page - child-shape - component - root-shape - root-component) - (generate-sync-shape-inverse-nested page - child-shape - component - root-shape - root-component))] - (recur (next children-ids) - (d/concat rchanges child-rchanges) - (d/concat uchanges child-uchanges)))))))) - -(defn- generate-sync-shape-inverse-nested - [page shape component root-shape root-component] - (log/trace :msg "Sync shape inverse (nested)" - :shape (str (:name shape)) - :component (:name component)) - (let [component-shape (d/seek #(= (:shape-ref %) - (:shape-ref shape)) - (vals (:objects component))) - root-shape (if (:component-id shape) - shape - root-shape) - root-component (if (:component-id shape) - component-shape - root-component) + component-container (cph/make-container component :component) [rchanges uchanges] - (update-attrs component-shape - shape - root-component - root-shape - nil - (:id component) - {:omit-touched? false - :reset-touched? false - :set-touched? false - :copy-touched? true}) + (concat-changes + (update-attrs shape-master + shape-inst + root-master + root-inst + component-container + options) + (concat-changes + (change-touched shape-master + shape-inst + component-container + options) + (if (:set-touched? options) + (change-touched shape-inst nil container {:reset-touched? true}) + empty-changes))) - children-ids (get shape :shapes [])] + children-inst (mapv #(cph/get-shape container %) + (:shapes shape-inst)) + children-master (mapv #(cph/get-shape component %) + (:shapes shape-master)) - (loop [children-ids (seq children-ids) - rchanges rchanges - uchanges uchanges] - (let [child-id (first children-ids)] - (if (nil? child-id) - [rchanges uchanges] - (let [child-shape (cph/get-shape page child-id) + only-inst (fn [shape-inst] + (add-shape-to-master shape-inst + component + container + root-inst + root-master)) - [child-rchanges child-uchanges] - (generate-sync-shape-inverse-nested page - child-shape - component - root-shape - root-component)] - (recur (next children-ids) - (d/concat rchanges child-rchanges) - (d/concat uchanges child-uchanges)))))))) + only-master (fn [shape-master] + (remove-shape shape-master + component-container + false)) + + both (fn [shape-inst shape-master] + (let [options (if-not (:component-id shape-inst) + options + {:reset-touched? false + :set-touched? false + :copy-touched? true})] -(defn- generate-sync-shape<-component - "Generate changes to synchronize one shape that is linked to other shape - inside a component. Same considerations as above about reset-touched?" - [shape root-shape root-component component page-id component-id reset?] - (if (nil? component) - (remove-component-and-ref shape page-id component-id) - (let [component-shape (get (:objects component) (:shape-ref shape))] - (if (nil? component-shape) - (remove-ref shape page-id component-id) - (update-attrs shape - component-shape - root-shape - root-component - page-id - component-id - {:omit-touched? (not reset?) - :reset-touched? reset? - :set-touched? false}))))) + (generate-sync-shape-inverse-recursive container + shape-inst + component + shape-master + root-inst + root-master + options))) -(defn- generate-sync-shape->component - "Generate changes to synchronize one shape inside a component, with other - shape that is linked to it." - [shape root-shape root-component component page-id] - (if (nil? component) - empty-changes - (let [component-shape (get (:objects component) (:shape-ref shape))] - (if (nil? component-shape) - empty-changes - (let [[rchanges1 uchanges1] - (update-attrs component-shape - shape - root-component - root-shape - nil - (:id root-component) - {:omit-touched? false - :reset-touched? false - :set-touched? true}) - [rchanges2 uchanges2] - (reset-touched shape - page-id - nil)] - [(d/concat rchanges1 rchanges2) - (d/concat uchanges2 uchanges2)]))))) + moved (fn [shape-inst shape-master] + (move-shape + shape-master + (d/index-of children-master shape-master) + (d/index-of children-inst shape-inst) + component-container + false)) + + [child-rchanges child-uchanges] + (compare-children children-inst + children-master + only-inst + only-master + both + moved + true)] + + [(d/concat rchanges child-rchanges) + (d/concat uchanges child-uchanges)])) ; ---- Operation generation helpers ---- -(defn- remove-component-and-ref - [shape page-id component-id] - [[(d/without-nils {:type :mod-obj - :id (:id shape) - :page-id page-id - :component-id component-id - :operations [{:type :set - :attr :component-root? - :val nil} - {:type :set - :attr :component-id - :val nil} - {:type :set - :attr :component-file - :val nil} - {:type :set - :attr :shape-ref - :val nil} - {:type :set-touched - :touched nil}]})] - [(d/without-nils {:type :mod-obj - :id (:id shape) - :page-id page-id - :component-id component-id - :operations [{:type :set - :attr :component-root? - :val (:component-root? shape)} - {:type :set - :attr :component-id - :val (:component-id shape)} - {:type :set - :attr :component-file - :val (:component-file shape)} - {:type :set - :attr :shape-ref - :val (:shape-ref shape)} - {:type :set-touched - :touched (:touched shape)}]})]]) +(defn- compare-children + [children-inst children-master only-inst-cb only-master-cb both-cb moved-cb inverse?] + (loop [children-inst (seq (or children-inst [])) + children-master (seq (or children-master [])) + [rchanges uchanges] [[] []]] + (let [child-inst (first children-inst) + child-master (first children-master)] + (cond + (and (nil? child-inst) (nil? child-master)) + [rchanges uchanges] -(defn- -remove-ref - [shape page-id component-id] - [[(d/without-nils {:type :mod-obj - :id (:id shape) - :page-id page-id - :component-id component-id - :operations [{:type :set - :attr :shape-ref - :val nil} - {:type :set-touched - :touched nil}]})] - [(d/without-nils {:type :mod-obj - :id (:id shape) - :page-id page-id - :component-id component-id - :operations [{:type :set - :attr :shape-ref - :val (:shape-ref shape)} - {:type :set-touched - :touched (:touched shape)}]})]]) + (nil? child-inst) + (reduce (fn [changes child] + (concat-changes changes (only-master-cb child))) + [rchanges uchanges] + children-master) -(defn- reset-touched - [shape page-id component-id] - [[(d/without-nils {:type :mod-obj - :id (:id shape) - :page-id page-id - :component-id component-id - :operations [{:type :set-touched - :touched nil}]})] - [(d/without-nils {:type :mod-obj - :id (:id shape) - :page-id page-id - :component-id component-id - :operations [{:type :set-touched - :touched (:touched shape)}]})]]) + (nil? child-master) + (reduce (fn [changes child] + (concat-changes changes (only-inst-cb child))) + [rchanges uchanges] + children-inst) + + :else + (if (cph/is-master-of child-master child-inst) + (recur (next children-inst) + (next children-master) + (concat-changes [rchanges uchanges] + (both-cb child-inst child-master))) + + (let [child-inst' (d/seek #(cph/is-master-of child-master %) + children-inst) + child-master' (d/seek #(cph/is-master-of % child-inst) + children-master)] + (cond + (nil? child-inst') + (recur children-inst + (next children-master) + (concat-changes [rchanges uchanges] + (only-master-cb child-master))) + + (nil? child-master') + (recur (next children-inst) + children-master + (concat-changes [rchanges uchanges] + (only-inst-cb child-inst))) + + :else + (if inverse? + (recur (next children-inst) + (remove #(= (:id %) (:id child-master')) children-master) + (-> [rchanges uchanges] + (concat-changes (both-cb child-inst' child-master)) + (concat-changes (moved-cb child-inst child-master')))) + (recur (remove #(= (:id %) (:id child-inst')) children-inst) + (next children-master) + (-> [rchanges uchanges] + (concat-changes (both-cb child-inst child-master')) + (concat-changes (moved-cb child-inst' child-master)))))))))))) + +(defn concat-changes + [[rchanges1 uchanges1] [rchanges2 uchanges2]] + [(d/concat rchanges1 rchanges2) + (d/concat uchanges1 uchanges2)]) + +(defn- add-shape-to-instance + [component-shape component container root-instance root-master omit-touched?] + (log/info :msg (str "ADD [P] " (:name component-shape))) + (let [component-parent-shape (cph/get-shape component (:parent-id component-shape)) + parent-shape (d/seek #(cph/is-master-of component-parent-shape %) + (cph/get-object-with-children (:id root-instance) + (:objects container))) + all-parents (vec (cons (:id parent-shape) + (cph/get-parents parent-shape (:objects container)))) + + update-new-shape (fn [new-shape original-shape] + (let [new-pos (calc-new-pos new-shape + original-shape + root-instance + root-master)] + (cond-> new-shape + true + (assoc :shape-ref (:id original-shape) + :frame-id (:frame-id parent-shape) + :x (:x new-pos) + :y (:y new-pos)) + + (:component-id original-shape) + (assoc :component-id (:component-id original-shape)) + + (:component-file original-shape) + (assoc :component-file (:component-file original-shape)) + + (:component-root original-shape) + (assoc :component-root (:component-root original-shape)) + + (:touched original-shape) + (assoc :touched (:touched original-shape))))) + + update-original-shape (fn [original-shape new-shape] + original-shape) + + [new-shape new-shapes _] + (cph/clone-object component-shape + (:id parent-shape) + (get container :objects) + update-new-shape + update-original-shape) + + rchanges (d/concat + (mapv (fn [shape'] + (as-> {:type :add-obj + :id (:id shape') + :parent-id (:parent-id shape') + :ignore-touched true + :obj shape'} $ + (cond-> $ + (:frame-id shape') + (assoc :frame-id (:frame-id shape'))) + (if (cph/page? container) + (assoc $ :page-id (:id container)) + (assoc $ :component-id (:id container))))) + new-shapes) + [(as-> {:type :reg-objects + :shapes all-parents} $ + (if (cph/page? container) + (assoc $ :page-id (:id container)) + (assoc $ :component-id (:id container))))]) + + uchanges (d/concat + (mapv (fn [shape'] + (as-> {:type :del-obj + :id (:id shape') + :ignore-touched true} $ + (if (cph/page? container) + (assoc $ :page-id (:id container)) + (assoc $ :component-id (:id container))))) + new-shapes))] + + (if (and (cph/touched-group? parent-shape :shapes-group) omit-touched?) + empty-changes + [rchanges uchanges]))) + +(defn- add-shape-to-master + [shape component page root-instance root-master] + (log/info :msg (str "ADD [C] " (:name shape))) + (let [parent-shape (cph/get-shape page (:parent-id shape)) + component-parent-shape (d/seek #(cph/is-master-of % parent-shape) + (cph/get-object-with-children (:id root-master) + (:objects component))) + all-parents (vec (cons (:id component-parent-shape) + (cph/get-parents component-parent-shape (:objects component)))) + + update-new-shape (fn [new-shape original-shape] + (let [new-pos (calc-new-pos new-shape + original-shape + root-master + root-instance)] + (assoc new-shape + :x (:x new-pos) + :y (:y new-pos)))) + + update-original-shape (fn [original-shape new-shape] + (if-not (:shape-ref original-shape) + (assoc original-shape + :shape-ref (:id new-shape)) + original-shape)) + + [new-shape new-shapes updated-shapes] + (cph/clone-object shape + (:id component-parent-shape) + (get page :objects) + update-new-shape + update-original-shape) + + rchanges (d/concat + (mapv (fn [shape'] + {:type :add-obj + :id (:id shape') + :component-id (:id component) + :parent-id (:parent-id shape') + :ignore-touched true + :obj shape'}) + new-shapes) + [{:type :reg-objects + :component-id (:id component) + :shapes all-parents}] + (mapv (fn [shape'] + {:type :mod-obj + :page-id (:id page) + :id (:id shape') + :operations [{:type :set + :attr :component-id + :val (:component-id shape')} + {:type :set + :attr :component-file + :val (:component-file shape')} + {:type :set + :attr :component-root? + :val (:component-root? shape')} + {:type :set + :attr :shape-ref + :val (:shape-ref shape')} + {:type :set + :attr :touched + :val (:touched shape')}]}) + updated-shapes)) + + uchanges (d/concat + (mapv (fn [shape'] + {:type :del-obj + :id (:id shape') + :page-id (:id page) + :ignore-touched true}) + new-shapes))] + + [rchanges uchanges])) + +(defn- remove-shape + [shape container omit-touched?] + (log/info :msg (str "REMOVE-SHAPE " + (if (cph/page? container) "[P] " "[C] ") + (:name shape))) + (let [objects (get container :objects) + parents (cph/get-parents (:id shape) objects) + parent (first parents) + children (cph/get-children (:id shape) objects) + + rchanges [(as-> {:type :del-obj + :id (:id shape) + :ignore-touched true} $ + (if (cph/page? container) + (assoc $ :page-id (:id container)) + (assoc $ :component-id (:id container))))] + + add-change (fn [id] + (let [shape' (get objects id)] + (as-> {:type :add-obj + :id id + :index (cph/position-on-parent id objects) + :parent-id (:parent-id shape') + :ignore-touched true + :obj shape'} $ + (cond-> $ + (:frame-id shape') + (assoc :frame-id (:frame-id shape'))) + (if (cph/page? container) + (assoc $ :page-id (:id container)) + (assoc $ :component-id (:id container)))))) + + uchanges (d/concat + [(add-change (:id shape))] + (map add-change children) + [(as-> {:type :reg-objects + :shapes (vec parents)} $ + (if (cph/page? container) + (assoc $ :page-id (:id container)) + (assoc $ :component-id (:id container))))])] + + (if (and (cph/touched-group? parent :shapes-group) omit-touched?) + empty-changes + [rchanges uchanges]))) + +(defn- move-shape + [shape index-before index-after container omit-touched?] + (log/info :msg (str "MOVE " + (if (cph/page? container) "[P] " "[C] ") + (:name shape) + " " + index-before + " -> " + index-after)) + (let [parent (cph/get-shape container (:parent-id shape)) + + rchanges [(as-> {:type :mov-objects + :parent-id (:parent-id shape) + :shapes [(:id shape)] + :index index-after + :ignore-touched true} $ + (if (cph/page? container) + (assoc $ :page-id (:id container)) + (assoc $ :component-id (:id container))))] + uchanges [(as-> {:type :mov-objects + :parent-id (:parent-id shape) + :shapes [(:id shape)] + :index index-before + :ignore-touched true} $ + (if (cph/page? container) + (assoc $ :page-id (:id container)) + (assoc $ :component-id (:id container))))]] + + (if (and (cph/touched-group? parent :shapes-group) omit-touched?) + empty-changes + [rchanges uchanges]))) + +(defn- change-touched + [dest-shape orig-shape container + {:keys [reset-touched? copy-touched?] + :as options :or {reset-touched? false + copy-touched? false}}] + (if (or (nil? (:shape-ref dest-shape)) + (not (or reset-touched? copy-touched?))) + empty-changes + (do + (log/info :msg (str "CHANGE-TOUCHED " + (if (cph/page? container) "[P] " "[C] ") + (:name dest-shape)) + :options options) + (let [rchanges [(as-> {:type :mod-obj + :id (:id dest-shape) + :operations + [{:type :set-touched + :touched + (cond reset-touched? + nil + copy-touched? + (:touched orig-shape))}]} $ + (if (cph/page? container) + (assoc $ :page-id (:id container)) + (assoc $ :component-id (:id container))))] + + uchanges [(as-> {:type :mod-obj + :id (:id dest-shape) + :operations + [{:type :set-touched + :touched (:touched dest-shape)}]} $ + (if (cph/page? container) + (assoc $ :page-id (:id container)) + (assoc $ :component-id (:id container))))]] + [rchanges uchanges])))) + +(defn- set-touched-shapes-group + [shape container] + (if-not (:shape-ref shape) + empty-changes + (do + (log/info :msg (str "SET-TOUCHED-SHAPES-GROUP " + (if (cph/page? container) "[P] " "[C] ") + (:name shape))) + (let [rchanges [(as-> {:type :mod-obj + :id (:id shape) + :operations + [{:type :set-touched + :touched (cph/set-touched-group + (:touched shape) + :shapes-group)}]} $ + (if (cph/page? container) + (assoc $ :page-id (:id container)) + (assoc $ :component-id (:id container))))] + + uchanges [(as-> {:type :mod-obj + :id (:id shape) + :operations + [{:type :set-touched + :touched (:touched shape)}]} $ + (if (cph/page? container) + (assoc $ :page-id (:id container)) + (assoc $ :component-id (:id container))))]] + [rchanges uchanges])))) (defn- update-attrs "The main function that implements the sync algorithm. Copy @@ -710,8 +950,10 @@ If reset-touched? is true, the 'touched' flags will be cleared in the dest shape. If set-touched? is true, the corresponding 'touched' flags will be - set in dest shape if they are different than their current values." - [dest-shape origin-shape dest-root origin-root page-id component-id + set in dest shape if they are different than their current values. + If copy-touched? is true, the value of 'touched' flags in the + origin shape will be copied as is to the dest shape." + [dest-shape origin-shape dest-root origin-root container {:keys [omit-touched? reset-touched? set-touched? copy-touched?] :as options :or {omit-touched? false reset-touched? false @@ -721,7 +963,7 @@ (log/info :msg (str "SYNC " (:name origin-shape) " -> " - (if page-id "[W] " "[C] ") + (if (cph/page? container) "[P] " "[C] ") (:name dest-shape))) (let [; The position attributes need a special sync algorith, because we do @@ -764,16 +1006,18 @@ :else uoperations) - rchanges [(d/without-nils {:type :mod-obj - :id (:id dest-shape) - :page-id page-id - :component-id component-id - :operations roperations})] - uchanges [(d/without-nils {:type :mod-obj - :id (:id dest-shape) - :page-id page-id - :component-id component-id - :operations uoperations})]] + rchanges [(as-> {:type :mod-obj + :id (:id dest-shape) + :operations roperations} $ + (if (cph/page? container) + (assoc $ :page-id (:id container)) + (assoc $ :component-id (:id container))))] + uchanges [(as-> {:type :mod-obj + :id (:id dest-shape) + :operations uoperations} $ + (if (cph/page? container) + (assoc $ :page-id (:id container)) + (assoc $ :component-id (:id container))))]] [rchanges uchanges]) (if-not (contains? dest-shape attr) diff --git a/frontend/src/app/main/data/workspace/selection.cljs b/frontend/src/app/main/data/workspace/selection.cljs index f2ef8af360..2d33d9e543 100644 --- a/frontend/src/app/main/data/workspace/selection.cljs +++ b/frontend/src/app/main/data/workspace/selection.cljs @@ -330,6 +330,7 @@ :old-id (:id obj) :frame-id frame-id :parent-id parent-id + :ignore-touched true :obj (dissoc reframed-obj :shapes)}] children-changes)))