🐛 Create the swapped copy with its swap slot already set

generate-new-shape-for-swap set the swap slot after instantiating the
new copy, through change-touched. The validation made when the copy is
instantiated ran before that and reported :missing-slot.

The slot only depends on the old shape, so compute it before the copy is
created and pass it to generate-instantiate-component with the new
:swap-slot option. The copy is added with its slot, and the validation
sees it complete. The resulting file and its undo are the same as
before.

AI-assisted-by: claude-opus-5-5
This commit is contained in:
Eva Marco 2026-10-01 12:12:21 +02:00 committed by Andrés Moya
parent b136097e18
commit 16c381ccef
2 changed files with 69 additions and 13 deletions

View File

@ -245,7 +245,7 @@
([changes objects file-id component-id position page libraries old-id parent-id frame-id params]
(generate-instantiate-component changes objects file-id component-id position page libraries old-id parent-id frame-id {} params))
([changes objects file-id component-id position page libraries old-id parent-id frame-id ids-map
{:keys [force-frame?]
{:keys [force-frame? swap-slot]
:or {force-frame? false}}]
(let [component (ctf/get-component libraries file-id component-id)
@ -261,6 +261,13 @@
parent-id (d/nilv (:id parent) parent-id)
frame-id (d/nilv (:frame-id parent) frame-id)
;; A copy created by a swap keeps the slot of the shape it
;; replaces. Set it from the start, so the validation below sees
;; the copy complete
set-swap-slot #(cond-> %
(nil? (ctk/get-swap-slot %))
(ctk/set-swap-slot swap-slot))
[new-shape new-shapes]
(ctn/make-component-instance page
component
@ -273,8 +280,10 @@
force-frame?
(assoc :force-frame-id frame-id)))
new-shape (set-swap-slot new-shape)
first-shape
(cond-> (first new-shapes)
(cond-> (set-swap-slot (first new-shapes))
(not (nil? parent-id))
(assoc :parent-id parent-id)
(and (not (nil? parent)) (= :frame (:type parent)))
@ -2730,6 +2739,14 @@
parent (get objects (:parent-id shape))
inside-comp? (ctn/in-any-component? objects parent)
;; if the shape isn't inside a main component, it shouldn't have a swap slot
swap-slot (when inside-comp?
(ctf/find-swap-slot shape
page
{:id (:id file)
:data file}
libraries))
[new-shape changes]
;; When we make a swap of an item, there can be copies which swap-slot points to that item
;; so we want to assign the item id to the new instanciated copy, to mantain that reference
@ -2744,17 +2761,8 @@
(:parent-id shape)
(:frame-id shape)
{(:id shape) (:id shape)} ;; keep the id of the original shape
{:force-frame? true})
new-shape (cond-> new-shape
;; if the shape isn't inside a main component, it shouldn't have a swap slot
(and (nil? (ctk/get-swap-slot new-shape))
inside-comp?)
(ctk/set-swap-slot (ctf/find-swap-slot shape
page
{:id (:id file)
:data file}
libraries)))]
{:force-frame? true
:swap-slot swap-slot})]
[new-shape (-> changes
;; Restore the properties

View File

@ -142,3 +142,51 @@
(t/is (= :rect (:type (tho/bottom-shape-by-id file' (:id restored)))))
(t/is (= rectangle-id (:component-id restored)))
(t/is (nil? (ctk/get-swap-slot restored))))))
(t/deftest test-swap-creates-copy-with-slot
;; The new copy is added with its swap slot already set, so the
;; validation made when it is instantiated sees it complete
(let [;; ==== Setup
file (-> (thf/sample-file :file1)
(tho/add-frame :frame-rectangle)
(ths/add-sample-shape :rectangle-shape :parent-label :frame-rectangle :type :rect)
(thc/make-component :rectangle :frame-rectangle)
(tho/add-frame :frame-circle)
(ths/add-sample-shape :circle :parent-label :frame-circle :type :circle)
(thc/make-component :circle :frame-circle)
(tho/add-frame :frame-main)
(thc/instantiate-component :rectangle :copy-rectangle :parent-label :frame-main)
(thc/make-component :main :frame-main))
page (thf/current-page file)
shape (ths/get-shape file :copy-rectangle)
;; ==== Action
[new-shape _ changes]
(cll/generate-component-swap (pcb/empty-changes nil (:id page))
(:objects page)
shape
(:data file)
page
{(:id file) file}
(:id (thc/get-component file :circle))
0
nil
{}
false)
add-change (->> (:redo-changes changes)
(filter #(and (= :add-obj (:type %))
(= (:id shape) (:id %))))
first)
file' (thf/apply-changes file changes)
copy' (ths/get-shape-by-id file' (:id shape))]
;; ==== Check
(t/is (some? (ctk/get-swap-slot copy')))
(t/is (= (ctk/get-swap-slot copy') (ctk/get-swap-slot new-shape)))
(t/is (= (ctk/get-swap-slot copy') (ctk/get-swap-slot (:obj add-change))))))