Merge pull request #2390 from penpot/hiru-delete-comp-assets

🐛 Fix delete component from assets panel in v2
This commit is contained in:
Pablo Alba 2022-10-04 11:47:28 +02:00 committed by GitHub
commit 5bbfe376cf
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 166 additions and 146 deletions

View file

@ -16,6 +16,7 @@
[app.common.pages.helpers :as cph] [app.common.pages.helpers :as cph]
[app.common.spec :as us] [app.common.spec :as us]
[app.common.types.color :as ctc] [app.common.types.color :as ctc]
[app.common.types.components-list :as ctkl]
[app.common.types.container :as ctn] [app.common.types.container :as ctn]
[app.common.types.file :as ctf] [app.common.types.file :as ctf]
[app.common.types.pages-list :as ctpl] [app.common.types.pages-list :as ctpl]
@ -29,6 +30,7 @@
[app.main.data.workspace.groups :as dwg] [app.main.data.workspace.groups :as dwg]
[app.main.data.workspace.libraries-helpers :as dwlh] [app.main.data.workspace.libraries-helpers :as dwlh]
[app.main.data.workspace.selection :as dws] [app.main.data.workspace.selection :as dws]
[app.main.data.workspace.shapes :as dwsh]
[app.main.data.workspace.state-helpers :as wsh] [app.main.data.workspace.state-helpers :as wsh]
[app.main.data.workspace.undo :as dwu] [app.main.data.workspace.undo :as dwu]
[app.main.features :as features] [app.main.features :as features]
@ -402,13 +404,16 @@
(ptk/reify ::delete-component (ptk/reify ::delete-component
ptk/WatchEvent ptk/WatchEvent
(watch [it state _] (watch [it state _]
(let [data (get state :workspace-data) (let [data (get state :workspace-data)]
components-v2 (features/active-feature? state :components-v2) (if (features/active-feature? state :components-v2)
changes (-> (pcb/empty-changes it) (let [component (ctkl/get-component data id)
(pcb/with-library-data data) page (ctpl/get-page data (:main-instance-page component))
(pcb/delete-component id components-v2))] shape (ctn/get-shape page (:main-instance-id component))]
(rx/of (dwsh/delete-shapes (:id page) #{(:id shape)})))
(rx/of (dch/commit-changes changes)))))) (let [changes (-> (pcb/empty-changes it)
(pcb/with-library-data data)
(pcb/delete-component id false))]
(rx/of (dch/commit-changes changes))))))))
(defn restore-component (defn restore-component
"Restore a deleted component, with the given id, on the current file library." "Restore a deleted component, with the given id, on the current file library."
@ -554,11 +559,14 @@
page-id (:current-page-id state) page-id (:current-page-id state)
container (cph/get-container file :page page-id) container (cph/get-container file :page page-id)
components-v2
(features/active-feature? state :components-v2)
changes changes
(-> (pcb/empty-changes it) (-> (pcb/empty-changes it)
(pcb/with-container container) (pcb/with-container container)
(pcb/with-objects (:objects container)) (pcb/with-objects (:objects container))
(dwlh/generate-sync-shape-direct libraries container id true))] (dwlh/generate-sync-shape-direct libraries container id true components-v2))]
(log/debug :msg "RESET-COMPONENT finished" :js/rchanges (log-changes (log/debug :msg "RESET-COMPONENT finished" :js/rchanges (log-changes
(:redo-changes changes) (:redo-changes changes)

View file

@ -177,7 +177,8 @@
:file (pretty-file file-id state) :file (pretty-file file-id state)
:library (pretty-file library-id state)) :library (pretty-file library-id state))
(let [file (wsh/get-file state file-id)] (let [file (wsh/get-file state file-id)
components-v2 (get-in file [:options :components-v2])]
(loop [pages (vals (get file :pages-index)) (loop [pages (vals (get file :pages-index))
changes (pcb/empty-changes it)] changes (pcb/empty-changes it)]
(if-let [page (first pages)] (if-let [page (first pages)]
@ -189,7 +190,8 @@
asset-id asset-id
library-id library-id
state state
(cph/make-container page :page)))) (cph/make-container page :page)
components-v2)))
changes)))) changes))))
(defn generate-sync-library (defn generate-sync-library
@ -211,7 +213,8 @@
:file (pretty-file file-id state) :file (pretty-file file-id state)
:library (pretty-file library-id state)) :library (pretty-file library-id state))
(let [file (wsh/get-file state file-id)] (let [file (wsh/get-file state file-id)
components-v2 (get-in file [:options :components-v2])]
(loop [local-components (vals (get file :components)) (loop [local-components (vals (get file :components))
changes (pcb/empty-changes it)] changes (pcb/empty-changes it)]
(if-let [local-component (first local-components)] (if-let [local-component (first local-components)]
@ -223,13 +226,14 @@
asset-id asset-id
library-id library-id
state state
(cph/make-container local-component :component)))) (cph/make-container local-component :component)
components-v2)))
changes)))) changes))))
(defn- generate-sync-container (defn- generate-sync-container
"Generate changes to synchronize all shapes in a particular container (a page "Generate changes to synchronize all shapes in a particular container (a page
or a component) that use assets of the given type in the given library." or a component) that use assets of the given type in the given library."
[it asset-type asset-id library-id state container] [it asset-type asset-id library-id state container components-v2]
(if (cph/page? container) (if (cph/page? container)
(log/debug :msg "Sync page in local file" :page-id (:id container)) (log/debug :msg "Sync page in local file" :page-id (:id container))
@ -248,7 +252,8 @@
library-id library-id
state state
container container
shape)) shape
components-v2))
changes)))) changes))))
(defmulti uses-assets? (defmulti uses-assets?
@ -276,16 +281,16 @@
(defmulti generate-sync-shape (defmulti generate-sync-shape
"Generate changes to synchronize one shape from all assets of the given type "Generate changes to synchronize one shape from all assets of the given type
that is using, in the given library." that is using, in the given library."
(fn [asset-type _changes _library-id _state _container _shape] asset-type)) (fn [asset-type _changes _library-id _state _container _shape _components-v2] asset-type))
(defmethod generate-sync-shape :components (defmethod generate-sync-shape :components
[_ changes _library-id state container shape] [_ changes _library-id state container shape components-v2]
(let [shape-id (:id shape) (let [shape-id (:id shape)
libraries (wsh/get-libraries state)] libraries (wsh/get-libraries state)]
(generate-sync-shape-direct changes libraries container shape-id false))) (generate-sync-shape-direct changes libraries container shape-id false components-v2)))
(defmethod generate-sync-shape :colors (defmethod generate-sync-shape :colors
[_ changes library-id state _ shape] [_ changes library-id state _ shape _]
(log/debug :msg "Sync colors of shape" :shape (:name shape)) (log/debug :msg "Sync colors of shape" :shape (:name shape))
;; Synchronize a shape that uses some colors of the library. The value of the ;; Synchronize a shape that uses some colors of the library. The value of the
@ -296,7 +301,7 @@
#(ctc/sync-shape-colors % library-id library-colors)))) #(ctc/sync-shape-colors % library-id library-colors))))
(defmethod generate-sync-shape :typographies (defmethod generate-sync-shape :typographies
[_ changes library-id state container shape] [_ changes library-id state container shape _]
(log/debug :msg "Sync typographies of shape" :shape (:name shape)) (log/debug :msg "Sync typographies of shape" :shape (:name shape))
;; Synchronize a shape that uses some typographies of the library. The attributes ;; Synchronize a shape that uses some typographies of the library. The attributes
@ -442,7 +447,7 @@
(defn generate-sync-shape-direct (defn generate-sync-shape-direct
"Generate changes to synchronize one shape that the root of a component "Generate changes to synchronize one shape that the root of a component
instance, and all its children, from the given component." instance, and all its children, from the given component."
[changes libraries container shape-id reset?] [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 (str shape-id) :reset? reset?)
(let [shape-inst (ctn/get-shape container shape-id) (let [shape-inst (ctn/get-shape container shape-id)
component (cph/get-component libraries component (cph/get-component libraries
@ -466,20 +471,25 @@
root-inst root-inst
root-main root-main
reset? reset?
initial-root?) initial-root?
components-v2)
; If the component is not found, because the master component has been ; If the component is not found, because the master component has been
; deleted or the library unlinked, detach the instance. ; deleted or the library unlinked, do nothing in v2 or detach in v1.
(generate-detach-instance changes container shape-id)))) (if components-v2
changes
(generate-detach-instance changes container shape-id)))))
(defn- generate-sync-shape-direct-recursive (defn- generate-sync-shape-direct-recursive
[changes container shape-inst component shape-main root-inst root-main reset? initial-root?] [changes container shape-inst component shape-main root-inst root-main reset? initial-root? components-v2]
(log/debug :msg "Sync shape direct recursive" (log/debug :msg "Sync shape direct recursive"
:shape (str (:name shape-inst)) :shape (str (:name shape-inst))
:component (:name component)) :component (:name component))
(if (nil? shape-main) (if (nil? shape-main)
;; This should not occur, but protect against it in any case ;; This should not occur, but protect against it in any case
(generate-detach-instance changes container (:id shape-inst)) (if components-v2
changes
(generate-detach-instance changes container (:id shape-inst)))
(let [omit-touched? (not reset?) (let [omit-touched? (not reset?)
clear-remote-synced? (and initial-root? reset?) clear-remote-synced? (and initial-root? reset?)
set-remote-synced? (and (not initial-root?) reset?) set-remote-synced? (and (not initial-root?) reset?)
@ -545,7 +555,8 @@
root-inst root-inst
root-main root-main
reset? reset?
initial-root?)) initial-root?
components-v2))
moved (fn [changes child-inst child-main] moved (fn [changes child-inst child-main]
(move-shape (move-shape

View file

@ -131,141 +131,142 @@
(rx/empty)))))) (rx/empty))))))
(defn delete-shapes (defn delete-shapes
[ids] ([ids] (delete-shapes nil ids))
(us/assert ::us/set-of-uuid ids) ([page-id ids]
(ptk/reify ::delete-shapes (us/assert ::us/set-of-uuid ids)
ptk/WatchEvent (ptk/reify ::delete-shapes
(watch [it state _] ptk/WatchEvent
(let [file-id (:current-file-id state) (watch [it state _]
page-id (:current-page-id state) (let [file-id (:current-file-id state)
file (wsh/get-file state file-id) page-id (or page-id (:current-page-id state))
page (wsh/lookup-page state page-id) file (wsh/get-file state file-id)
objects (wsh/lookup-page-objects state page-id) page (wsh/lookup-page state page-id)
objects (wsh/lookup-page-objects state page-id)
ids (cph/clean-loops objects ids) ids (cph/clean-loops objects ids)
lookup (d/getf objects) lookup (d/getf objects)
components-v2 (features/active-feature? state :components-v2) components-v2 (features/active-feature? state :components-v2)
groups-to-unmask groups-to-unmask
(reduce (fn [group-ids id] (reduce (fn [group-ids id]
;; When the shape to delete is the mask of a masked group, ;; When the shape to delete is the mask of a masked group,
;; the mask condition must be removed, and it must be ;; the mask condition must be removed, and it must be
;; converted to a normal group. ;; converted to a normal group.
(let [obj (lookup id) (let [obj (lookup id)
parent (lookup (:parent-id obj))] parent (lookup (:parent-id obj))]
(if (and (:masked-group? parent) (if (and (:masked-group? parent)
(= id (first (:shapes parent)))) (= id (first (:shapes parent))))
(conj group-ids (:id parent)) (conj group-ids (:id parent))
group-ids))) group-ids)))
#{} #{}
ids) ids)
interacting-shapes interacting-shapes
(filter (fn [shape] (filter (fn [shape]
;; If any of the deleted shapes is the destination of ;; If any of the deleted shapes is the destination of
;; some interaction, this must be deleted, too. ;; some interaction, this must be deleted, too.
(let [interactions (:interactions shape)] (let [interactions (:interactions shape)]
(some #(and (ctsi/has-destination %) (some #(and (ctsi/has-destination %)
(contains? ids (:destination %))) (contains? ids (:destination %)))
interactions))) interactions)))
(vals objects)) (vals objects))
;; If any of the deleted shapes is a frame with guides ;; If any of the deleted shapes is a frame with guides
guides (into {} guides (into {}
(comp (map second) (comp (map second)
(remove #(contains? ids (:frame-id %))) (remove #(contains? ids (:frame-id %)))
(map (juxt :id identity))) (map (juxt :id identity)))
(dm/get-in page [:options :guides])) (dm/get-in page [:options :guides]))
starting-flows starting-flows
(filter (fn [flow] (filter (fn [flow]
;; If any of the deleted is a frame that starts a flow, ;; If any of the deleted is a frame that starts a flow,
;; this must be deleted, too. ;; this must be deleted, too.
(contains? ids (:starting-frame flow))) (contains? ids (:starting-frame flow)))
(-> page :options :flows)) (-> page :options :flows))
all-parents all-parents
(reduce (fn [res id] (reduce (fn [res id]
;; All parents of any deleted shape must be resized. ;; All parents of any deleted shape must be resized.
(into res (cph/get-parent-ids objects id))) (into res (cph/get-parent-ids objects id)))
(d/ordered-set) (d/ordered-set)
ids) ids)
all-children all-children
(->> ids ;; Children of deleted shapes must be also deleted. (->> ids ;; Children of deleted shapes must be also deleted.
(reduce (fn [res id] (reduce (fn [res id]
(into res (cph/get-children-ids objects id))) (into res (cph/get-children-ids objects id)))
[]) [])
(reverse) (reverse)
(into (d/ordered-set))) (into (d/ordered-set)))
find-all-empty-parents find-all-empty-parents
(fn recursive-find-empty-parents [empty-parents] (fn recursive-find-empty-parents [empty-parents]
(let [all-ids (into empty-parents ids) (let [all-ids (into empty-parents ids)
contains? (partial contains? all-ids) contains? (partial contains? all-ids)
xform (comp (map lookup) xform (comp (map lookup)
(filter cph/group-shape?) (filter cph/group-shape?)
(remove #(->> (:shapes %) (remove contains?) seq)) (remove #(->> (:shapes %) (remove contains?) seq))
(map :id)) (map :id))
parents (into #{} xform all-parents)] parents (into #{} xform all-parents)]
(if (= empty-parents parents) (if (= empty-parents parents)
empty-parents empty-parents
(recursive-find-empty-parents parents)))) (recursive-find-empty-parents parents))))
empty-parents empty-parents
;; Any parent whose children are all deleted, must be deleted too. ;; Any parent whose children are all deleted, must be deleted too.
(into (d/ordered-set) (find-all-empty-parents #{})) (into (d/ordered-set) (find-all-empty-parents #{}))
components-to-delete components-to-delete
(if components-v2 (if components-v2
(reduce (fn [components id] (reduce (fn [components id]
(let [shape (get objects id)] (let [shape (get objects id)]
(if (and (= (:component-file shape) file-id) ;; Main instances should exist only in local file (if (and (= (:component-file shape) file-id) ;; Main instances should exist only in local file
(:main-instance? shape)) ;; but check anyway (:main-instance? shape)) ;; but check anyway
(conj components (:component-id shape)) (conj components (:component-id shape))
components))) components)))
[] []
(into ids all-children)) (into ids all-children))
[]) [])
changes (-> (pcb/empty-changes it page-id) changes (-> (pcb/empty-changes it page-id)
(pcb/with-page page) (pcb/with-page page)
(pcb/with-objects objects) (pcb/with-objects objects)
(pcb/with-library-data file) (pcb/with-library-data file)
(pcb/set-page-option :guides guides)) (pcb/set-page-option :guides guides))
changes (reduce (fn [changes component-id] changes (reduce (fn [changes component-id]
;; It's important to delete the component before the main instance, because we ;; It's important to delete the component before the main instance, because we
;; need to store the instance position if we want to restore it later. ;; need to store the instance position if we want to restore it later.
(pcb/delete-component changes component-id components-v2)) (pcb/delete-component changes component-id components-v2))
changes changes
components-to-delete) components-to-delete)
changes (-> changes changes (-> changes
(pcb/remove-objects all-children) (pcb/remove-objects all-children)
(pcb/remove-objects ids) (pcb/remove-objects ids)
(pcb/remove-objects empty-parents) (pcb/remove-objects empty-parents)
(pcb/resize-parents all-parents) (pcb/resize-parents all-parents)
(pcb/update-shapes groups-to-unmask (pcb/update-shapes groups-to-unmask
(fn [shape] (fn [shape]
(assoc shape :masked-group? false))) (assoc shape :masked-group? false)))
(pcb/update-shapes (map :id interacting-shapes) (pcb/update-shapes (map :id interacting-shapes)
(fn [shape] (fn [shape]
(d/update-when shape :interactions (d/update-when shape :interactions
(fn [interactions] (fn [interactions]
(into [] (into []
(remove #(and (ctsi/has-destination %) (remove #(and (ctsi/has-destination %)
(contains? ids (:destination %)))) (contains? ids (:destination %))))
interactions))))) interactions)))))
(cond-> (seq starting-flows) (cond-> (seq starting-flows)
(pcb/update-page-option :flows (fn [flows] (pcb/update-page-option :flows (fn [flows]
(->> (map :id starting-flows) (->> (map :id starting-flows)
(reduce ctp/remove-flow flows))))))] (reduce ctp/remove-flow flows))))))]
(rx/of (dc/detach-comment-thread ids) (rx/of (dc/detach-comment-thread ids)
(dwsl/update-layout-positions all-parents) (dwsl/update-layout-positions all-parents)
(dch/commit-changes changes)))))) (dch/commit-changes changes)))))))
(defn- viewport-center (defn- viewport-center
[state] [state]