diff --git a/common/src/app/common/files/migrations.cljc b/common/src/app/common/files/migrations.cljc index 6971b5d234..b6c1ddb49c 100644 --- a/common/src/app/common/files/migrations.cljc +++ b/common/src/app/common/files/migrations.cljc @@ -1777,6 +1777,28 @@ component))] (d/update-when data :components check-component))) +(defmethod migrate-data "0018-sync-component-id-with-near-main" + [data _] + (letfn [(fix-shape [page shape] + (if (and (ctk/subcopy-head? shape) + (nil? (ctk/get-swap-slot shape))) + (let [file {:id (:id data) :data data} + libs (some-> (:libs data) deref) + ref-shape (ctf/find-ref-shape file page libs shape {:include-deleted? true :with-context? true})] + (if (and (some? ref-shape) + (or (not= (:component-id shape) (:component-id ref-shape)) + (not= (:component-file shape) (:component-file ref-shape)))) + (assoc shape + :component-id (:component-id ref-shape) + :component-file (:component-file ref-shape)) + shape)) + shape)) + + (update-page [page] + (d/update-when page :objects d/update-vals (partial fix-shape page)))] + (-> data + (update :pages-index d/update-vals update-page)))) + (def available-migrations (into (d/ordered-set) ["legacy-2" @@ -1850,4 +1872,6 @@ "0014-clear-components-nil-objects" "0015-fix-text-attrs-blank-strings" "0015-clean-shadow-color" - "0016-copy-fills-from-position-data-to-text-node"])) + "0016-copy-fills-from-position-data-to-text-node" + "0017-remove-unneeded-objects-from-components" + "0018-sync-component-id-with-near-main"])) diff --git a/common/src/app/common/files/repair.cljc b/common/src/app/common/files/repair.cljc index 925e2c714b..100090a8ed 100644 --- a/common/src/app/common/files/repair.cljc +++ b/common/src/app/common/files/repair.cljc @@ -333,6 +333,22 @@ (pcb/with-file-data file-data) (pcb/update-shapes [(:id shape)] repair-shape)))) +(defmethod repair-error :component-id-mismatch + [_ {:keys [shape page-id args] :as error} file-data _] + (let [repair-shape + (fn [shape] + ; Set the component-id and component-file to the ones of the near main + (log/debug :hint (str " -> set component-id to " (:component-id args))) + (log/debug :hint (str " -> set component-file to " (:component-file args))) + (assoc shape + :component-id (:component-id args) + :component-file (:component-file args)))] + + (log/dbg :hint "repairing shape :component-id-mismatch" :id (:id shape) :name (:name shape) :page-id page-id) + (-> (pcb/empty-changes nil page-id) + (pcb/with-file-data file-data) + (pcb/update-shapes [(:id shape)] repair-shape)))) + (defmethod repair-error :ref-shape-is-head [_ {:keys [shape page-id args] :as error} file-data _] (let [repair-shape diff --git a/common/src/app/common/files/validate.cljc b/common/src/app/common/files/validate.cljc index a61c8d05a1..59f1f5fad0 100644 --- a/common/src/app/common/files/validate.cljc +++ b/common/src/app/common/files/validate.cljc @@ -51,6 +51,7 @@ :ref-shape-is-head :ref-shape-is-not-head :shape-ref-in-main + :component-id-mismatch :root-main-not-allowed :nested-main-not-allowed :root-copy-not-allowed @@ -327,6 +328,20 @@ :component-file (:component-file ref-shape) :component-id (:component-id ref-shape))))) +(defn- check-ref-component-id + "Validate that if the copy has not been swwpped, the component-id and component-file are + the same as in the referenced shape in the near main." + [shape file page libraries] + (when (nil? (ctk/get-swap-slot shape)) + (when-let [ref-shape (ctf/find-ref-shape file page libraries shape :include-deleted? true)] + (when (or (not= (:component-id shape) (:component-id ref-shape)) + (not= (:component-file shape) (:component-file ref-shape))) + (report-error :component-id-mismatch + "Nested copy component-id and component-file must be the same as the near main" + shape file page + :component-id (:component-id ref-shape) + :component-file (:component-file ref-shape)))))) + (defn- check-empty-swap-slot "Validate that this shape does not have any swap slot." [shape file page] @@ -419,6 +434,7 @@ (check-component-not-main-head shape file page libraries) (check-component-not-root shape file page) (check-valid-touched shape file page) + (check-ref-component-id shape file page libraries) ;; We can have situations where the nested copy and the ancestor copy come from different libraries and some of them have been dettached ;; so we only validate the shape-ref if the ancestor is from a valid library (when library-exists diff --git a/common/src/app/common/logic/libraries.cljc b/common/src/app/common/logic/libraries.cljc index 0d291a94f4..f476910670 100644 --- a/common/src/app/common/logic/libraries.cljc +++ b/common/src/app/common/logic/libraries.cljc @@ -1769,6 +1769,23 @@ (pcb/update-shapes changes [(:id dest-shape)] ctk/unhead-shape {:ignore-touched true}) changes)) +(defn- check-swapped-main + [changes dest-shape origin-shape] + ;; Only for direct updates (from main to copy). Check if the main shape + ;; has been swapped. If so, the new component-id and component-file must + ;; be put into the copy. + (if (and (= (:shape-ref dest-shape) (:id origin-shape)) + (ctk/instance-head? dest-shape) + (ctk/instance-head? origin-shape) + (or (not= (:component-id dest-shape) (:component-id origin-shape)) + (not= (:component-file dest-shape) (:component-file origin-shape)))) + (pcb/update-shapes changes [(:id dest-shape)] + #(assoc % + :component-id (:component-id origin-shape) + :component-file (:component-file origin-shape)) + {:ignore-touched true}) + changes)) + (defn- update-attrs "The main function that implements the attribute sync algorithm. Copy attributes that have changed in the origin shape to the dest shape. @@ -1811,6 +1828,8 @@ :always (check-detached-main dest-shape origin-shape) :always + (check-swapped-main dest-shape origin-shape) + :always (generate-update-tokens container dest-shape origin-shape touched omit-touched? nil)) (let [attr-group (get ctk/sync-attrs attr)