🔧 Refactor delete/restore components

This commit is contained in:
Andrés Moya 2023-03-10 15:43:26 +01:00
parent b91f1959b4
commit 7391a4086a
27 changed files with 378 additions and 328 deletions

View file

@ -13,6 +13,7 @@
[app.common.pages.helpers :as cph] [app.common.pages.helpers :as cph]
[app.common.pages.migrations :as pmg] [app.common.pages.migrations :as pmg]
[app.common.spec :as us] [app.common.spec :as us]
[app.common.types.components-list :as ctkl]
[app.common.types.file :as ctf] [app.common.types.file :as ctf]
[app.common.types.shape-tree :as ctt] [app.common.types.shape-tree :as ctt]
[app.config :as cf] [app.config :as cf]
@ -485,16 +486,19 @@
(defn get-team-shared-files (defn get-team-shared-files
[conn team-id] [conn team-id]
(letfn [(assets-sample [assets limit] (letfn [(assets-sample [assets limit]
(let [sorted-assets (->> (vals assets) (let [sorted-assets (->> (vals assets)
(sort-by #(str/lower (:name %))))] (sort-by #(str/lower (:name %))))]
{:count (count sorted-assets) {:count (count sorted-assets)
:sample (into [] (take limit sorted-assets))})) :sample (into [] (take limit sorted-assets))}))
(library-summary [{:keys [id data] :as file}] (library-summary [{:keys [id data] :as file}]
(binding [pmap/*load-fn* (partial load-pointer conn id)] (binding [pmap/*load-fn* (partial load-pointer conn id)]
(let [components-sample (-> (assets-sample (:components data) 4) (let [load-objects (fn [component]
(binding [pmap/*load-fn* (partial load-pointer conn id)]
(ctf/load-component-objects data component)))
components-sample (-> (assets-sample (ctkl/components data) 4)
(update :sample (update :sample
#(map (partial ctf/load-component-objects data) %)))] #(map load-objects %)))]
{:components components-sample {:components components-sample
:media (assets-sample (:media data) 3) :media (assets-sample (:media data) 3)
:colors (assets-sample (:colors data) 3) :colors (assets-sample (:colors data) 3)

View file

@ -13,6 +13,7 @@
[app.common.data :as d] [app.common.data :as d]
[app.common.logging :as l] [app.common.logging :as l]
[app.common.pages.migrations :as pmg] [app.common.pages.migrations :as pmg]
[app.common.types.components-list :as ctkl]
[app.common.types.file :as ctf] [app.common.types.file :as ctf]
[app.common.types.shape-tree :as ctt] [app.common.types.shape-tree :as ctt]
[app.config :as cf] [app.config :as cf]
@ -204,36 +205,31 @@
(filter #(ctf/used-in? file-data library-id % :component)) (filter #(ctf/used-in? file-data library-id % :component))
components)) components))
find-used-components find-unused-components
(fn [components files-data] (fn [components files-data]
; Find what components are used in any of the files. ; Find what components are NOT used in any of the files.
(loop [files-data files-data (loop [files-data files-data
components components components components]
used-components #{}]
(let [file-data (first files-data)] (let [file-data (first files-data)]
(if (or (nil? file-data) (empty? components)) (if (or (nil? file-data) (empty? components))
used-components components
(let [used-components-file (find-used-components-file components file-data)] (let [used-components-file (find-used-components-file components file-data)]
(recur (rest files-data) (recur (rest files-data)
(into #{} (remove used-components-file) components) (into #{} (remove used-components-file) components)))))))
(into used-components used-components-file)))))))
deleted-components (set (vals (:deleted-components library-data))) deleted-components (set (ctkl/deleted-components-seq library-data))
saved-components (find-used-components deleted-components unused-components (find-unused-components deleted-components
(cons library-data (cons library-data
(retrieve-client-files conn library-id))) (retrieve-client-files conn library-id)))
new-deleted-components (d/index-by :id (vec saved-components)) total (count unused-components)]
total (- (count deleted-components)
(count saved-components))]
(when-not (zero? total) (when-not (zero? total)
(l/debug :hint "clean deleted components" :total total) (l/debug :hint "clean deleted components" :total total)
(let [new-data (-> library-data (let [new-data (reduce #(ctkl/delete-component %1 (:id %2))
(assoc :deleted-components new-deleted-components) library-data
(blob/encode))] unused-components)]
(db/update! conn :file (db/update! conn :file
{:data new-data} {:data (blob/encode new-data)}
{:id library-id}))))) {:id library-id})))))
(def ^:private sql:get-unused-fragments (def ^:private sql:get-unused-fragments

View file

@ -255,6 +255,11 @@
(subvec v 0 index) (subvec v 0 index)
(subvec v (inc index)))) (subvec v (inc index))))
(defn without-obj
"Clear collection from specified obj and without nil values."
[coll o]
(into [] (filter #(not= % o)) coll))
(defn zip [col1 col2] (defn zip [col1 col2]
(map vector col1 col2)) (map vector col1 col2))
@ -477,6 +482,17 @@
(->> (apply c/iteration args) (->> (apply c/iteration args)
(concat-all))) (concat-all)))
(defn insert-at-index
"Insert a list of elements at the given index of a previous list.
Replace all existing elems."
[elems index new-elems]
(let [[before after] (split-at index elems)
p? (set new-elems)]
(concat-vec []
(remove p? before)
new-elems
(remove p? after))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Data Parsing / Conversion ;; Data Parsing / Conversion

View file

@ -26,15 +26,6 @@
[app.common.types.shape-tree :as ctst] [app.common.types.shape-tree :as ctst]
[app.common.types.typographies-list :as ctyl])) [app.common.types.typographies-list :as ctyl]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Specific helpers
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn- without-obj
"Clear collection from specified obj and without nil values."
[coll o]
(into [] (filter #(not= % o)) coll))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Page Transformation Changes ;; Page Transformation Changes
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@ -111,27 +102,9 @@
(defmethod process-change :del-obj (defmethod process-change :del-obj
[data {:keys [page-id component-id id ignore-touched]}] [data {:keys [page-id component-id id ignore-touched]}]
(letfn [(delete-from-parent [parent] (if page-id
(let [parent (update parent :shapes without-obj id)] (d/update-in-when data [:pages-index page-id] ctst/delete-shape id ignore-touched)
(cond-> parent (d/update-in-when data [:components component-id] ctst/delete-shape id ignore-touched)))
(and (:shape-ref parent)
(not ignore-touched))
(-> (update :touched cph/set-touched-group :shapes-group)
(dissoc :remote-synced?)))))
(delete-from-objects [objects]
(if-let [target (get objects id)]
(let [parent-id (or (:parent-id target)
(:frame-id target))
children (cph/get-children id objects)]
(-> (reduce dissoc objects children)
(dissoc id)
(d/update-when parent-id delete-from-parent)))
objects))]
(if page-id
(d/update-in-when data [:pages-index page-id :objects] delete-from-objects)
(d/update-in-when data [:components component-id :objects] delete-from-objects))))
;; reg-objects operation "regenerates" the geometry and selrect of the parent groups ;; reg-objects operation "regenerates" the geometry and selrect of the parent groups
(defmethod process-change :reg-objects (defmethod process-change :reg-objects
@ -197,7 +170,7 @@
(insert-items [prev-shapes index shapes] (insert-items [prev-shapes index shapes]
(let [prev-shapes (or prev-shapes [])] (let [prev-shapes (or prev-shapes [])]
(if index (if index
(cph/insert-at-index prev-shapes index shapes) (d/insert-at-index prev-shapes index shapes)
(cph/append-at-the-end prev-shapes shapes)))) (cph/append-at-the-end prev-shapes shapes))))
(add-to-parent [parent index shapes] (add-to-parent [parent index shapes]
@ -226,7 +199,7 @@
(not ignore-touched))] (not ignore-touched))]
(-> objects (-> objects
(d/update-in-when [pid :shapes] without-obj sid) (d/update-in-when [pid :shapes] d/without-obj sid)
(d/update-in-when [pid :shapes] d/vec-without-nils) (d/update-in-when [pid :shapes] d/vec-without-nils)
(cond-> component? (d/update-when pid #(-> % (cond-> component? (d/update-when pid #(-> %
(update :touched cph/set-touched-group :shapes-group) (update :touched cph/set-touched-group :shapes-group)
@ -301,7 +274,7 @@
(defmethod process-change :mov-page (defmethod process-change :mov-page
[data {:keys [id index]}] [data {:keys [id index]}]
(update data :pages cph/insert-at-index index [id])) (update data :pages d/insert-at-index index [id]))
(defmethod process-change :add-color (defmethod process-change :add-color
[data {:keys [color]}] [data {:keys [color]}]

View file

@ -15,7 +15,6 @@
[app.common.math :as mth] [app.common.math :as mth]
[app.common.pages :as cp] [app.common.pages :as cp]
[app.common.pages.helpers :as cph] [app.common.pages.helpers :as cph]
[app.common.types.components-list :as ctkl]
[app.common.types.file :as ctf] [app.common.types.file :as ctf]
[app.common.uuid :as uuid])) [app.common.uuid :as uuid]))
@ -612,7 +611,8 @@
(fn [undo-changes] (fn [undo-changes]
(-> undo-changes (-> undo-changes
(d/preconj {:type :del-component (d/preconj {:type :del-component
:id id}) :id id
:skip-undelete? true})
(into (comp (map :id) (into (comp (map :id)
(map lookupf) (map lookupf)
(map mk-change)) (map mk-change))
@ -631,7 +631,7 @@
:id id :id id
:name (:name new-component) :name (:name new-component)
:path (:path new-component) :path (:path new-component)
:objects (:objects new-component)}) :objects (:objects new-component)}) ;; this won't exist in components-v2
(update :undo-changes d/preconj {:type :mod-component (update :undo-changes d/preconj {:type :mod-component
:id id :id id
:name (:name prev-component) :name (:name prev-component)
@ -640,29 +640,13 @@
changes))) changes)))
(defn delete-component (defn delete-component
[changes id components-v2] [changes id]
(assert-library changes) (assert-library changes)
(let [library-data (::library-data (meta changes)) (-> changes
component (ctkl/get-component library-data id) (update :redo-changes conj {:type :del-component
shapes (ctf/get-component-shapes library-data component)] :id id})
(-> changes (update :undo-changes d/preconj {:type :restore-component
(update :redo-changes conj {:type :del-component :id id})))
:id id})
(update :undo-changes
(fn [undo-changes]
(cond-> undo-changes
components-v2
(d/preconj {:type :purge-component
:id id})
:always
(d/preconj {:type :add-component
:id id
:name (:name component)
:path (:path component)
:main-instance-id (:main-instance-id component)
:main-instance-page (:main-instance-page component)
:shapes shapes})))))))
(defn restore-component (defn restore-component
[changes id] [changes id]

View file

@ -9,6 +9,8 @@
[app.common.data :as d] [app.common.data :as d]
[app.common.data.macros :as dm] [app.common.data.macros :as dm]
[app.common.spec :as us] [app.common.spec :as us]
[app.common.types.components-list :as ctkl]
[app.common.types.pages-list :as ctpl]
[app.common.types.shape.layout :as ctl] [app.common.types.shape.layout :as ctl]
[app.common.uuid :as uuid] [app.common.uuid :as uuid]
[cuerdas.core :as str])) [cuerdas.core :as str]))
@ -300,8 +302,8 @@
(us/assert uuid? id) (us/assert uuid? id)
(-> (if (= type :page) (-> (if (= type :page)
(get-in file [:pages-index id]) (ctpl/get-page file id)
(get-in file [:components id])) (ctkl/get-component file id))
(assoc :type type))) (assoc :type type)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@ -321,15 +323,6 @@
(update page :objects (update page :objects
#(into % (d/index-by :id objects-list)))) #(into % (d/index-by :id objects-list))))
(defn insert-at-index
[objects index ids]
(let [[before after] (split-at index objects)
p? (set ids)]
(d/concat-vec []
(remove p? before)
ids
(remove p? after))))
(defn append-at-the-end (defn append-at-the-end
[prev-ids ids] [prev-ids ids]
(reduce (fn [acc id] (reduce (fn [acc id]

View file

@ -34,10 +34,11 @@
(and (= shape-id (:main-instance-id component)) (and (= shape-id (:main-instance-id component))
(= page-id (:main-instance-page component)))) (= page-id (:main-instance-page component))))
;; Obsolete in components-v2
(defn get-component-root (defn get-component-root
[component] [component]
(get-in component [:objects (:id component)])) (if (some? (:main-instance-id component))
(get-in component [:objects (:main-instance-id component)])
(get-in component [:objects (:id component)])))
(defn uses-library-components? (defn uses-library-components?
"Check if the shape uses any component in the given library." "Check if the shape uses any component in the given library."
@ -67,5 +68,3 @@
:remote-synced? :remote-synced?
:shape-ref :shape-ref
:touched)) :touched))

View file

@ -10,9 +10,18 @@
[app.common.data.macros :as dm] [app.common.data.macros :as dm]
[app.common.files.features :as feat])) [app.common.files.features :as feat]))
(defn components
[file-data]
(d/removem (fn [[_ component]] (:deleted component))
(:components file-data)))
(defn components-seq (defn components-seq
[file-data] [file-data]
(vals (:components file-data))) (remove :deleted (vals (:components file-data))))
(defn deleted-components-seq
[file-data]
(filter :deleted (vals (:components file-data))))
(defn add-component (defn add-component
[file-data {:keys [id name path main-instance-id main-instance-page shapes]}] [file-data {:keys [id name path main-instance-id main-instance-page shapes]}]
@ -53,7 +62,15 @@
(defn get-component (defn get-component
[file-data component-id] [file-data component-id]
(get-in file-data [:components component-id])) (let [component (get-in file-data [:components component-id])]
(when-not (:deleted component)
component)))
(defn get-deleted-component
[file-data component-id]
(let [component (get-in file-data [:components component-id])]
(when (:deleted component)
component)))
(defn update-component (defn update-component
[file-data component-id f] [file-data component-id f]
@ -62,3 +79,11 @@
(defn delete-component (defn delete-component
[file-data component-id] [file-data component-id]
(update file-data :components dissoc component-id)) (update file-data :components dissoc component-id))
(defn mark-component-deleted
[file-data component-id]
(assoc-in file-data [:components component-id :deleted] true))
(defn mark-component-undeleted
[file-data component-id]
(d/dissoc-in file-data [:components component-id :deleted]))

View file

@ -6,11 +6,11 @@
(ns app.common.types.container (ns app.common.types.container
(:require (:require
[app.common.data.macros :as dm]
[app.common.geom.point :as gpt] [app.common.geom.point :as gpt]
[app.common.geom.shapes :as gsh] [app.common.geom.shapes :as gsh]
[app.common.pages.common :as common] [app.common.pages.common :as common]
[app.common.spec :as us] [app.common.spec :as us]
[app.common.types.components-list :as ctkl]
[app.common.types.pages-list :as ctpl] [app.common.types.pages-list :as ctpl]
[app.common.types.shape-tree :as ctst] [app.common.types.shape-tree :as ctst]
[clojure.spec.alpha :as s])) [clojure.spec.alpha :as s]))
@ -43,8 +43,8 @@
(us/assert uuid? id) (us/assert uuid? id)
(-> (if (= type :page) (-> (if (= type :page)
(dm/get-in file [:pages-index id]) (ctpl/get-page file id)
(dm/get-in file [:components id])) (ctkl/get-component file id))
(assoc :type type))) (assoc :type type)))
(defn get-shape (defn get-shape

View file

@ -131,7 +131,7 @@
or the component itself in v1)" or the component itself in v1)"
[file-data component] [file-data component]
(let [components-v2 (dm/get-in file-data [:options :components-v2])] (let [components-v2 (dm/get-in file-data [:options :components-v2])]
(if components-v2 (if (and components-v2 (not (:deleted component)))
(let [component-page (get-component-page file-data component)] (let [component-page (get-component-page file-data component)]
(cph/make-container component-page :page)) (cph/make-container component-page :page))
(cph/make-container component :component)))) (cph/make-container component :component))))
@ -140,26 +140,17 @@
"Retrieve the root shape of the component." "Retrieve the root shape of the component."
[file-data component] [file-data component]
(let [components-v2 (dm/get-in file-data [:options :components-v2])] (let [components-v2 (dm/get-in file-data [:options :components-v2])]
(if components-v2 (if (and components-v2 (not (:deleted component)))
(-> file-data (-> file-data
(get-component-page component) (get-component-page component)
(ctn/get-shape (:main-instance-id component))) (ctn/get-shape (:main-instance-id component)))
(ctk/get-component-root component)))) (ctk/get-component-root component))))
(defn get-component-shapes
"Retrieve all shapes of the component"
[file-data component]
(let [components-v2 (dm/get-in file-data [:options :components-v2])]
(if components-v2
(let [instance-page (get-component-page file-data component)]
(cph/get-children-with-self (:objects instance-page) (:main-instance-id component)))
(vals (:objects component)))))
(defn get-component-shape (defn get-component-shape
"Retrieve one shape in the component." "Retrieve one shape in the component by id."
[file-data component shape-id] [file-data component shape-id]
(let [components-v2 (dm/get-in file-data [:options :components-v2])] (let [components-v2 (dm/get-in file-data [:options :components-v2])]
(if components-v2 (if (and components-v2 (not (:deleted component)))
(let [component-page (get-component-page file-data component)] (let [component-page (get-component-page file-data component)]
(ctn/get-shape component-page shape-id)) (ctn/get-shape component-page shape-id))
(dm/get-in component [:objects shape-id])))) (dm/get-in component [:objects shape-id]))))
@ -171,73 +162,54 @@
(when (:shape-ref shape) (when (:shape-ref shape)
(get-component-shape file-data component (:shape-ref shape)))) (get-component-shape file-data component (:shape-ref shape))))
(defn get-component-shapes
"Retrieve all shapes of the component"
[file-data component]
(let [components-v2 (dm/get-in file-data [:options :components-v2])]
(if components-v2
(let [instance-page (get-component-page file-data component)]
(cph/get-children-with-self (:objects instance-page) (:main-instance-id component)))
(vals (:objects component)))))
(defn load-component-objects (defn load-component-objects
"Add an :objects property to the component, with only the shapes that belong to it" "Add an :objects property to the component, with only the shapes that belong to it"
[file-data component] [file-data component]
(let [components-v2 (dm/get-in file-data [:options :components-v2])] (let [components-v2 (dm/get-in file-data [:options :components-v2])]
(if components-v2 (if (and components-v2 component (nil? (:objects component))) ;; This operation may be called twice, e.g. in an idempotent change
(let [component-page (get-component-page file-data component) (let [component-page (get-component-page file-data component)
page-objects (:objects component-page) page-objects (:objects component-page)
objects (->> (cons (:main-instance-id component) objects (->> (cons (:main-instance-id component)
(cph/get-children-ids page-objects (:main-instance-id component))) (cph/get-children-ids page-objects (:main-instance-id component)))
(map #(get page-objects %)) (map #(get page-objects %))
(d/index-by :id))] (d/index-by :id))]
(assoc component :objects objects)) (assoc component :objects objects))
component))) component)))
(defn delete-component (defn delete-component
"Delete a component and store it to be able to be recovered later. "Mark a component as deleted and store the main instance shapes iside it, to
be able to be recovered later."
Remember also the position of the main instance."
([file-data component-id] ([file-data component-id]
(delete-component file-data component-id false)) (delete-component file-data component-id false))
([file-data component-id _skip-undelete?] ([file-data component-id skip-undelete?]
(let [_components-v2 (dm/get-in file-data [:options :components-v2]) (let [components-v2 (dm/get-in file-data [:options :components-v2])]
(if (or (not components-v2) skip-undelete?)
;; TODO: replace :deleted-components with a :deleted? flag in normal shapes (ctkl/delete-component file-data component-id)
;; see task https://tree.taiga.io/project/penpot/task/4998 (-> file-data
;; (ctkl/update-component component-id (partial load-component-objects file-data))
;; add-to-deleted-components (ctkl/mark-component-deleted component-id))))))
;; (fn [file-data]
;; (let [component (ctkl/get-component file-data component-id)]
;; (if (some? component)
;; (let [main-instance (get-component-root file-data component)
;; component (assoc component
;; :main-instance-x (:x main-instance) ; An instance root is always a group,
;; :main-instance-y (:y main-instance))] ; or a frame, so it will have :x and :y
;; (when (nil? main-instance)
;; (throw (ex-info "Cannot delete the main instance before the component" {:component-id component-id})))
;; (assoc-in file-data [:deleted-components component-id] component))
;; file-data)))
]
(cond-> file-data
;; (and components-v2 (not skip-undelete?))
;; (add-to-deleted-components)
:always
(ctkl/delete-component component-id)))))
(defn get-deleted-component
"Retrieve a component that has been deleted but still is in the safe store."
[file-data component-id]
(dm/get-in file-data [:deleted-components component-id]))
(defn restore-component (defn restore-component
"Recover a deleted component and put it again in place." "Recover a deleted component and all its shapes and put all this again in place."
[file-data component-id] [file-data component-id]
(let [component (-> (dm/get-in file-data [:deleted-components component-id]) (-> file-data
(dissoc :main-instance-x :main-instance-y))] (ctkl/update-component component-id #(dissoc % :objects))
(cond-> file-data (ctkl/mark-component-undeleted component-id)))
(some? component)
(-> (assoc-in [:components component-id] component)
(d/dissoc-in [:deleted-components component-id])))))
(defn purge-component (defn purge-component
"Remove permanently a component." "Remove permanently a component."
[file-data component-id] [file-data component-id]
(d/dissoc-in file-data [:deleted-components component-id])) (ctkl/delete-component file-data component-id))
(defmulti uses-asset? (defmulti uses-asset?
"Checks if a shape uses the given asset." "Checks if a shape uses the given asset."
@ -555,7 +527,7 @@
([file-data page-id libraries show-ids show-touched] ([file-data page-id libraries show-ids show-touched]
(let [page (ctpl/get-page file-data page-id) (let [page (ctpl/get-page file-data page-id)
objects (:objects page) objects (:objects page)
components (:components file-data) components (ctkl/components file-data)
root (d/seek #(nil? (:parent-id %)) (vals objects))] root (d/seek #(nil? (:parent-id %)) (vals objects))]
(letfn [(show-shape [shape-id level objects] (letfn [(show-shape [shape-id level objects]
@ -590,7 +562,7 @@
component-file (when component-file-id (get libraries component-file-id nil)) component-file (when component-file-id (get libraries component-file-id nil))
component (when component-id component (when component-id
(if component-file (if component-file
(dm/get-in component-file [:data :components component-id]) (ctkl/get-component (:data component-file) component-id)
(get components component-id))) (get components component-id)))
component-shape (when component component-shape (when component
(if component-file (if component-file
@ -611,7 +583,7 @@
component-file-id (:component-file shape) component-file-id (:component-file shape)
component-file (when component-file-id (get libraries component-file-id nil)) component-file (when component-file-id (get libraries component-file-id nil))
component (if component-file component (if component-file
(dm/get-in component-file [:data :components component-id]) (ctkl/get-component (:data component-file) component-id)
(get components component-id))] (get components component-id))]
(str/format " (%s%s)" (str/format " (%s%s)"
(when component-file (str/format "<%s> " (:name component-file))) (when component-file (str/format "<%s> " (:name component-file)))

View file

@ -709,7 +709,7 @@
(update shape :shapes (update shape :shapes
(fn [shapes] (fn [shapes]
(if (vector? shapes) (if (vector? shapes)
(cph/insert-at-index shapes index value) (d/insert-at-index shapes index value)
(d/concat-vec shapes value)))) (d/concat-vec shapes value))))
(update shape :shapes d/concat-vec value))) (update shape :shapes d/concat-vec value)))

View file

@ -6,8 +6,8 @@
(ns app.common.types.pages-list (ns app.common.types.pages-list
(:require (:require
[app.common.data.macros :as dm] [app.common.data :as d]
[app.common.pages.helpers :as cph])) [app.common.data.macros :as dm]))
(defn get-page (defn get-page
[file-data id] [file-data id]
@ -23,7 +23,7 @@
(cond (cond
exists? pages exists? pages
(nil? index) (conj pages id) (nil? index) (conj pages id)
:else (cph/insert-at-index pages index [id]))))) :else (d/insert-at-index pages index [id])))))
(update :pages-index assoc id (dissoc page :index)))) (update :pages-index assoc id (dissoc page :index))))
(defn pages-seq (defn pages-seq

View file

@ -36,7 +36,7 @@
(conj shapes id) (conj shapes id)
:else :else
(cph/insert-at-index shapes index [id])))) (d/insert-at-index shapes index [id]))))
update-parent update-parent
(fn [parent] (fn [parent]
@ -49,28 +49,63 @@
(-> (update :touched cph/set-touched-group :shapes-group) (-> (update :touched cph/set-touched-group :shapes-group)
(dissoc :remote-synced?))))) (dissoc :remote-synced?)))))
;; TODO: this looks wrong, why we allow nil values?
update-objects update-objects
(fn [objects parent-id] (fn [objects parent-id]
(if (and (or (nil? parent-id) (contains? objects parent-id)) (let [parent-id (if (contains? objects parent-id)
(or (nil? frame-id) (contains? objects frame-id))) parent-id
uuid/zero)
frame-id (if (contains? objects frame-id)
frame-id
uuid/zero)]
(-> objects (-> objects
(assoc id (-> shape (assoc id (-> shape
(assoc :frame-id frame-id) (assoc :frame-id frame-id)
(assoc :parent-id parent-id) (assoc :parent-id parent-id)
(assoc :id id))) (assoc :id id)))
(update parent-id update-parent)) (update parent-id update-parent))))
objects))
parent-id (or parent-id frame-id)] parent-id (or parent-id frame-id)]
(update container :objects update-objects parent-id))) (update container :objects update-objects parent-id)))
(defn get-shape
"Get a shape identified by id"
[container id]
(-> container :objects (get id)))
(defn set-shape (defn set-shape
"Replace a shape in the tree with a new one" "Replace a shape in the tree with a new one"
[container shape] [container shape]
(assoc-in container [:objects (:id shape)] shape)) (assoc-in container [:objects (:id shape)] shape))
(defn delete-shape
"Remove a shape and all its children from the tree.
Remove it also from its parent, and marks it as touched
if needed, unless ignore-touched is true."
([container shape-id]
(delete-shape container shape-id false))
([container shape-id ignore-touched]
(letfn [(delete-from-parent [parent]
(let [parent (update parent :shapes d/without-obj shape-id)]
(cond-> parent
(and (:shape-ref parent) (not ignore-touched))
(-> (update :touched cph/set-touched-group :shapes-group)
(dissoc :remote-synced?)))))
(delete-from-objects [objects]
(if-let [target (get objects shape-id)]
(let [parent-id (or (:parent-id target)
(:frame-id target))
children-ids (cph/get-children-ids objects shape-id)]
(-> (reduce dissoc objects children-ids)
(dissoc shape-id)
(d/update-when parent-id delete-from-parent)))
objects))]
(update container :objects delete-from-objects))))
(defn get-frames (defn get-frames
"Retrieves all frame objects as vector" "Retrieves all frame objects as vector"
[objects] [objects]

View file

@ -65,3 +65,41 @@
(t/is (= 0 (d/check-num [] 0))) (t/is (= 0 (d/check-num [] 0)))
(t/is (= 0 (d/check-num :foo 0))) (t/is (= 0 (d/check-num :foo 0)))
(t/is (= 0 (d/check-num nil 0)))) (t/is (= 0 (d/check-num nil 0))))
(t/deftest insert-at-index
;; insert different object
(t/is (= (d/insert-at-index [:a :b] 1 [:c :d])
[:a :c :d :b]))
;; insert on the start
(t/is (= (d/insert-at-index [:a :b] 0 [:c])
[:c :a :b]))
;; insert on the end 1
(t/is (= (d/insert-at-index [:a :b] 2 [:c])
[:a :b :c]))
;; insert on the end with not existing index
(t/is (= (d/insert-at-index [:a :b] 10 [:c])
[:a :b :c]))
;; insert existing in a contiguous index
(t/is (= (d/insert-at-index [:a :b] 1 [:a])
[:a :b]))
;; insert existing in the same index
(t/is (= (d/insert-at-index [:a :b] 0 [:a])
[:a :b]))
;; insert existing in other index case 1
(t/is (= (d/insert-at-index [:a :b :c] 2 [:a])
[:b :a :c]))
;; insert existing in other index case 2
(t/is (= (d/insert-at-index [:a :b :c :d] 0 [:d])
[:d :a :b :c]))
;; insert existing in other index case 3
(t/is (= (d/insert-at-index [:a :b :c :d] 1 [:a])
[:a :b :c :d])))

View file

@ -10,50 +10,10 @@
[clojure.pprint :refer [pprint]] [clojure.pprint :refer [pprint]]
[app.common.pages.helpers :as cph])) [app.common.pages.helpers :as cph]))
(t/deftest insert-at-index
;; insert different object
(t/is (= (cph/insert-at-index [:a :b] 1 [:c :d])
[:a :c :d :b]))
;; insert on the start
(t/is (= (cph/insert-at-index [:a :b] 0 [:c])
[:c :a :b]))
;; insert on the end 1
(t/is (= (cph/insert-at-index [:a :b] 2 [:c])
[:a :b :c]))
;; insert on the end with not existing index
(t/is (= (cph/insert-at-index [:a :b] 10 [:c])
[:a :b :c]))
;; insert existing in a contiguous index
(t/is (= (cph/insert-at-index [:a :b] 1 [:a])
[:a :b]))
;; insert existing in the same index
(t/is (= (cph/insert-at-index [:a :b] 0 [:a])
[:a :b]))
;; insert existing in other index case 1
(t/is (= (cph/insert-at-index [:a :b :c] 2 [:a])
[:b :a :c]))
;; insert existing in other index case 2
(t/is (= (cph/insert-at-index [:a :b :c :d] 0 [:d])
[:d :a :b :c]))
;; insert existing in other index case 3
(t/is (= (cph/insert-at-index [:a :b :c :d] 1 [:a])
[:a :b :c :d]))
)
(t/deftest parse-path-name (t/deftest parse-path-name
(t/is (= ["foo" "bar"] (cph/parse-path-name "foo/bar"))) (t/is (= ["foo" "bar"] (cph/parse-path-name "foo/bar")))
(t/is (= ["" "foo"] (cph/parse-path-name "foo"))) (t/is (= ["" "foo"] (cph/parse-path-name "foo")))
(t/is (= ["" "foo"] (cph/parse-path-name "/foo"))) (t/is (= ["" "foo"] (cph/parse-path-name "/foo")))
(t/is (= ["" ""] (cph/parse-path-name ""))) (t/is (= ["" ""] (cph/parse-path-name "")))
(t/is (= ["" ""] (cph/parse-path-name nil))) (t/is (= ["" ""] (cph/parse-path-name nil))))
)

View file

@ -9,6 +9,7 @@
[app.common.data :as d] [app.common.data :as d]
[app.common.file-builder :as fb] [app.common.file-builder :as fb]
[app.common.media :as cm] [app.common.media :as cm]
[app.common.types.components-list :as ctkl]
[app.common.uuid :as uuid] [app.common.uuid :as uuid]
[app.util.dom :as dom] [app.util.dom :as dom]
[app.util.json :as json] [app.util.json :as json]
@ -108,7 +109,7 @@
components-stream components-stream
(->> files-stream (->> files-stream
(rx/flat-map vals) (rx/flat-map vals)
(rx/filter #(d/not-empty? (get-in % [:data :components]))) (rx/filter #(d/not-empty? (ctkl/components-seq (:data %))))
(rx/flat-map e/parse-library-components)) (rx/flat-map e/parse-library-components))
pages-stream pages-stream

View file

@ -34,7 +34,6 @@
(declare commit-changes) (declare commit-changes)
(defn- add-group-id (defn- add-group-id
[changes state] [changes state]
(let [undo (:workspace-undo state) (let [undo (:workspace-undo state)
@ -215,7 +214,7 @@
add-page-id add-page-id
(fn [{:keys [id type page] :as change}] (fn [{:keys [id type page] :as change}]
(cond-> change (cond-> change
(page-change? type) (and (page-change? type) (nil? (:page-id change)))
(assoc :page-id (or id (:id page))))) (assoc :page-id (or id (:id page)))))
changes-by-pages changes-by-pages

View file

@ -22,7 +22,6 @@
[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.file.media-object :as ctfm] [app.common.types.file.media-object :as ctfm]
[app.common.types.pages-list :as ctpl]
[app.common.types.typography :as ctt] [app.common.types.typography :as ctt]
[app.common.uuid :as uuid] [app.common.uuid :as uuid]
[app.main.data.dashboard :as dd] [app.main.data.dashboard :as dd]
@ -421,12 +420,12 @@
(let [data (get state :workspace-data)] (let [data (get state :workspace-data)]
(if (features/active-feature? state :components-v2) (if (features/active-feature? state :components-v2)
(let [component (ctkl/get-component data id) (let [component (ctkl/get-component data id)
page (ctpl/get-page data (:main-instance-page component)) page (ctf/get-component-page data component)
shape (ctn/get-shape page (:main-instance-id component))] shape (ctf/get-component-root data component)]
(rx/of (dwsh/delete-shapes (:id page) #{(:id shape)}))) (rx/of (dwsh/delete-shapes (:id page) #{(:id shape)}))) ;; Deleting main root triggers component delete
(let [changes (-> (pcb/empty-changes it) (let [changes (-> (pcb/empty-changes it)
(pcb/with-library-data data) (pcb/with-library-data data)
(pcb/delete-component id false))] (pcb/delete-component id))]
(rx/of (dch/commit-changes changes)))))))) (rx/of (dch/commit-changes changes))))))))
(defn restore-component (defn restore-component
@ -437,39 +436,18 @@
(ptk/reify ::restore-component (ptk/reify ::restore-component
ptk/WatchEvent ptk/WatchEvent
(watch [it state _] (watch [it state _]
(assert "Restore component not implemented") ; until we allow a :deleted flag in shapes (let [library-data (wsh/get-file state library-id)
(let [file-data (wsh/get-file state library-id) component (ctkl/get-deleted-component library-data component-id)
component (ctf/get-deleted-component file-data component-id) page (ctf/get-component-page library-data component)
page (ctpl/get-page file-data (:main-instance-page component)) shapes (cph/get-children-with-self (:objects component) (:main-instance-id component))
changes (-> (pcb/empty-changes it)
components-v2 (pcb/with-library-data library-data)
(features/active-feature? state :components-v2) (pcb/with-page page))
changes (reduce #(pcb/add-object %1 %2 {:ignore-touched true})
; Make a new main instance, with the same id of the original changes
[_main-instance shapes] shapes)
(ctn/make-component-instance page changes (pcb/restore-component changes component-id)]
component (rx/of (dch/commit-changes changes))))))
file-data
(gpt/point (:main-instance-x component)
(:main-instance-y component))
components-v2
{:main-instance? true
:force-id (:main-instance-id component)})
changes (-> (pcb/empty-changes it)
(pcb/with-library-data file-data)
(pcb/with-page page))
changes (reduce #(pcb/add-object %1 %2 {:ignore-touched true})
changes
shapes)
; restore-component change needs to be done after add main instance
; because when undo changes, the orden is inverse
changes (pcb/restore-component changes component-id)]
(rx/of (dch/commit-changes (assoc changes :file-id library-id)))))))
(defn instantiate-component (defn instantiate-component
"Create a new shape in the current page, from the component with the given id "Create a new shape in the current page, from the component with the given id

View file

@ -241,7 +241,7 @@
(let [file (wsh/get-file state file-id) (let [file (wsh/get-file state file-id)
components-v2 (get-in file [:options :components-v2])] components-v2 (get-in file [:options :components-v2])]
(loop [local-components (vals (get file :components)) (loop [local-components (ctkl/components-seq file)
changes (pcb/empty-changes it)] changes (pcb/empty-changes it)]
(if-let [local-component (first local-components)] (if-let [local-component (first local-components)]
(recur (next local-components) (recur (next local-components)
@ -480,7 +480,7 @@
(let [library (dm/get-in libraries [(:component-file shape-inst) :data]) (let [library (dm/get-in libraries [(:component-file shape-inst) :data])
component (or (ctkl/get-component library (:component-id shape-inst)) component (or (ctkl/get-component library (:component-id shape-inst))
(and reset? (and reset?
(ctf/get-deleted-component library (:component-id shape-inst)))) (ctkl/get-deleted-component library (:component-id shape-inst))))
shape-main (when component shape-main (when component
(ctf/get-ref-shape library component shape-inst)) (ctf/get-ref-shape library component shape-inst))

View file

@ -292,7 +292,7 @@
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))
changes changes
components-to-delete) components-to-delete)

View file

@ -40,10 +40,6 @@
([state page-id] ([state page-id]
(dm/get-in state [:workspace-data :pages-index page-id :options]))) (dm/get-in state [:workspace-data :pages-index page-id :options])))
(defn lookup-component-objects
([state component-id]
(dm/get-in state [:workspace-data :components component-id :objects])))
(defn lookup-local-components (defn lookup-local-components
([state] ([state]
(dm/get-in state [:workspace-data :components]))) (dm/get-in state [:workspace-data :components])))

View file

@ -7,6 +7,7 @@
(ns app.main.ui.workspace.libraries (ns app.main.ui.workspace.libraries
(:require (:require
[app.common.data :as d] [app.common.data :as d]
[app.common.types.components-list :as ctkl]
[app.main.data.dashboard :as dd] [app.main.data.dashboard :as dd]
[app.main.data.modal :as modal] [app.main.data.modal :as modal]
[app.main.data.workspace.libraries :as dwl] [app.main.data.workspace.libraries :as dwl]
@ -44,7 +45,7 @@
(defn local-library-str (defn local-library-str
[library] [library]
(let [components-count (count (get-in library [:data :components] [])) (let [components-count (count (or (ctkl/components-seq (:data library)) []))
graphics-count (count (get-in library [:data :media] [])) graphics-count (count (get-in library [:data :media] []))
colors-count (count (get-in library [:data :colors] [])) colors-count (count (get-in library [:data :colors] []))
typography-count (count (get-in library [:data :typographies] []))] typography-count (count (get-in library [:data :typographies] []))]

View file

@ -12,6 +12,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.text :as txt] [app.common.text :as txt]
[app.common.types.components-list :as ctkl]
[app.common.types.file :as ctf] [app.common.types.file :as ctf]
[app.config :as cf] [app.config :as cf]
[app.main.data.events :as ev] [app.main.data.events :as ev]
@ -374,8 +375,11 @@
components-v2 (mf/use-ctx ctx/components-v2) components-v2 (mf/use-ctx ctx/components-v2)
file (or (:data file) file) file (or (:data file) file)
root-shape (ctf/get-component-root file component)
component-container (if components-v2
(ctf/get-component-page file component)
component)
unselect-all unselect-all
(mf/use-fn (mf/use-fn
(fn [] (fn []
@ -444,26 +448,26 @@
:on-drag-over on-drag-over :on-drag-over on-drag-over
:on-drop on-drop} :on-drop on-drop}
[:& component-svg {:root-shape (ctf/get-component-root file component) (when (and (some? root-shape) (some? component-container))
:objects (:objects (if components-v2
(ctf/get-component-page file component)
component))}]
(let [renaming? (= renaming (:id component))]
[:* [:*
[:& editable-label [:& component-svg {:root-shape root-shape
{:class-name (dom/classnames :objects (:objects component-container)}]
:cell-name listing-thumbs? (let [renaming? (= renaming (:id component))]
:item-name (not listing-thumbs?) [:*
:editing renaming?) [:& editable-label
:value (cph/merge-path-item (:path component) (:name component)) {:class-name (dom/classnames
:tooltip (cph/merge-path-item (:path component) (:name component)) :cell-name listing-thumbs?
:display-value (:name component) :item-name (not listing-thumbs?)
:editing? renaming? :editing renaming?)
:disable-dbl-click? true :value (cph/merge-path-item (:path component) (:name component))
:on-change do-rename :tooltip (cph/merge-path-item (:path component) (:name component))
:on-cancel cancel-rename}] :display-value (:name component)
(when @dragging? :editing? renaming?
[:div.dragging])])])) :disable-dbl-click? true
:on-change do-rename
:on-cancel cancel-rename}]
(when @dragging?
[:div.dragging])])])]))
(mf/defc components-group (mf/defc components-group
[{:keys [file prefix groups open-groups renaming listing-thumbs? selected-components on-asset-click [{:keys [file prefix groups open-groups renaming listing-thumbs? selected-components on-asset-click
@ -1938,8 +1942,8 @@
(l/derived (fn [state] (l/derived (fn [state]
(let [wfile (:workspace-data state)] (let [wfile (:workspace-data state)]
(if (= (:id wfile) id) (if (= (:id wfile) id)
(vals (get wfile :components)) (ctkl/components-seq wfile)
(vals (get-in state [:workspace-libraries id :data :components]))))) (ctkl/components-seq (get-in state [:workspace-libraries id :data])))))
st/state =)) st/state =))
(defn file-typography-ref (defn file-typography-ref

View file

@ -11,6 +11,7 @@
[app.common.logging :as l] [app.common.logging :as l]
[app.common.math :as mth] [app.common.math :as mth]
[app.common.spec :as us] [app.common.spec :as us]
[app.common.types.components-list :as ctkl]
[app.common.uri :as u] [app.common.uri :as u]
[app.main.data.fonts :as df] [app.main.data.fonts :as df]
[app.main.features :as features] [app.main.features :as features]
@ -247,7 +248,7 @@
:border "0px solid black"}]]])] :border "0px solid black"}]]])]
[:ul.nav [:ul.nav
(for [[id data] (get-in file [:data :components])] (for [[id data] (ctkl/components (:data file))]
(let [on-click (fn [event] (let [on-click (fn [event]
(dom/prevent-default event) (dom/prevent-default event)
(swap! state assoc :component-id id))] (swap! state assoc :component-id id))]

View file

@ -9,6 +9,7 @@
[app.common.data :as d] [app.common.data :as d]
[app.common.media :as cm] [app.common.media :as cm]
[app.common.text :as ct] [app.common.text :as ct]
[app.common.types.components-list :as ctkl]
[app.config :as cfg] [app.config :as cfg]
[app.main.render :as r] [app.main.render :as r]
[app.main.repo :as rp] [app.main.repo :as rp]
@ -51,7 +52,7 @@
:version current-version :version current-version
:libraries (->> (:libraries file) (into #{}) (mapv str)) :libraries (->> (:libraries file) (into #{}) (mapv str))
:exportType (d/name export-type) :exportType (d/name export-type)
:hasComponents (d/not-empty? (get-in file [:data :components])) :hasComponents (d/not-empty? (ctkl/components-seq (:data file)))
:hasDeletedComponents (d/not-empty? (get-in file [:data :deleted-components])) :hasDeletedComponents (d/not-empty? (get-in file [:data :deleted-components]))
:hasMedia (d/not-empty? (get-in file [:data :media])) :hasMedia (d/not-empty? (get-in file [:data :media]))
:hasColors (d/not-empty? (get-in file [:data :colors])) :hasColors (d/not-empty? (get-in file [:data :colors]))
@ -329,7 +330,7 @@
(select-keys (get external-refs (:id file)))) (select-keys (get external-refs (:id file))))
media (-> (get-in file [:data :media]) media (-> (get-in file [:data :media])
(select-keys (get external-refs (:id file)))) (select-keys (get external-refs (:id file))))
components (-> (get-in file [:data :components]) components (-> (ctkl/components (:data file))
(select-keys (get external-refs (:id file))))] (select-keys (get external-refs (:id file))))]
(cond-> target (cond-> target
(d/not-empty? colors) (d/not-empty? colors)
@ -434,7 +435,7 @@
components-stream components-stream
(->> files-stream (->> files-stream
(rx/flat-map vals) (rx/flat-map vals)
(rx/filter #(d/not-empty? (get-in % [:data :components]))) (rx/filter #(d/not-empty? (ctkl/components-seq (:data %))))
(rx/flat-map parse-library-components)) (rx/flat-map parse-library-components))
deleted-components-stream deleted-components-stream

View file

@ -7,11 +7,9 @@
(ns frontend-tests.helpers.libraries (ns frontend-tests.helpers.libraries
(:require (:require
[app.common.pages.helpers :as cph] [app.common.pages.helpers :as cph]
[app.common.types.component :as ctk]
[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.main.data.workspace.state-helpers :as wsh] [app.main.data.workspace.state-helpers :as wsh]
[cljs.pprint :refer [pprint]]
[cljs.test :as t :include-macros true] [cljs.test :as t :include-macros true]
[frontend-tests.helpers.pages :as thp])) [frontend-tests.helpers.pages :as thp]))

View file

@ -1,6 +1,7 @@
(ns frontend-tests.state-components-test (ns frontend-tests.state-components-test
(:require (:require
[app.common.geom.point :as gpt] [app.common.geom.point :as gpt]
[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.main.data.workspace :as dw] [app.main.data.workspace :as dw]
@ -303,14 +304,14 @@
(filter #(not= % component-id)) (filter #(not= % component-id))
(first)) (first))
[[instance1 shape1] [[_instance1 _shape1]
[c-instance1 c-shape1] [_c-instance1 _c-shape1]
component1] _component1]
(thl/resolve-instance-and-main (thl/resolve-instance-and-main
new-state new-state
(:id main1)) (:id main1))
[[c-component2 c-shape2] [[_c-component2 _c-shape2]
component2] component2]
(thl/resolve-component (thl/resolve-component
new-state new-state
@ -331,39 +332,114 @@
(thp/sample-shape :shape1 :rect (thp/sample-shape :shape1 :rect
{:name "Rect 1"}) {:name "Rect 1"})
(thp/make-component :main1 :component1 (thp/make-component :main1 :component1
[(thp/id :shape1)])) [(thp/id :shape1)])
(thp/instantiate-component :instance1
file (wsh/get-local-file state) (thp/id :component1)))
main1 (thp/get-shape state :main1)
component-id (:component-id main1)
store (the/prepare-store state done store (the/prepare-store state done
(fn [new-state] (fn [new-state]
; Expected shape tree: ; Expected shape tree:
; ;
; [Page] ; [Page]
; Root Frame ; Root Frame
; ; Rect 1 #--> ?
(let [[main1 shape1] ; Rect 1 ---> ?
(thl/resolve-noninstance ;
new-state (let [[main1 shape1]
(:id main1)) (thl/resolve-noninstance
new-state
(thp/id :main1))
libs (wsh/get-libraries new-state) [[instance1 shape2] [c-instance1 c-shape2] component1]
component (ctf/get-component libs (thl/resolve-instance-and-main-allow-dangling
(:component-file main1) new-state
(:component-id main1))] (thp/id :instance1))
(t/is (nil? main1)) file (wsh/get-local-file new-state)
(t/is (nil? shape1)) component2 (ctkl/get-component file (thp/id :component1))
(t/is (nil? component)))))] component3 (ctkl/get-deleted-component file (thp/id :component1))
saved-objects (:objects component3)
saved-main1 (get saved-objects (:shape-ref instance1))
saved-shape2 (get saved-objects (:shape-ref shape2))]
(t/is (nil? main1))
(t/is (nil? shape1))
(t/is (= (:name instance1) "Rect 1"))
(t/is (= (:touched instance1) nil))
(t/is (not= (:shape-ref instance1) nil))
(t/is (= (:name shape2) "Rect 1"))
(t/is (= (:touched shape2) nil))
(t/is (not= (:shape-ref shape2) nil))
(t/is (nil? c-instance1))
(t/is (nil? c-shape2))
(t/is (nil? component1))
(t/is (nil? component2))
(t/is (= (:name component3) "Rect 1"))
(t/is (= (:deleted component3) true))
(t/is (some? (:objects component3)))
(t/is (= (:name saved-main1) "Rect 1"))
(t/is (= (:name saved-shape2) "Rect 1")))))]
(ptk/emit! (ptk/emit!
store store
(dwl/delete-component {:id component-id}) (dwl/delete-component {:id (thp/id :component1)})
(dwl/sync-file (:id file) (:id file) :components component-id) :the/end))))
:the/end))))
(t/deftest test-restore-component
(t/async
done
(let [state (-> thp/initial-state
(thp/sample-page)
(thp/sample-shape :shape1 :rect
{:name "Rect 1"})
(thp/make-component :main1 :component1
[(thp/id :shape1)])
(thp/instantiate-component :instance1
(thp/id :component1)))
store (the/prepare-store state done
(fn [new-state]
; Expected shape tree:
;
; [Page]
; Root Frame
; Rect 1 #--> Rect 1
; Rect 1 ---> Rect 1
; Rect 1
; Rect 1
;
; [Rect 1]
; page1 / Rect 1
;
(let [[[instance1 shape2] [c-instance1 c-shape2] component1]
(thl/resolve-instance-and-main
new-state
(thp/id :instance1))
file (wsh/get-local-file new-state)
component2 (ctkl/get-component file (thp/id :component1))
saved-objects (:objects component2)]
(t/is (= (:name instance1) "Rect 1"))
(t/is (= (:name shape2) "Rect 1"))
(t/is (= (:name c-instance1) "Rect 1"))
(t/is (= (:name c-shape2) "Rect 1"))
(t/is (some? component1))
(t/is (some? component2))
(t/is (nil? saved-objects)))))]
(ptk/emit!
store
(dwl/delete-component {:id (thp/id :component1)})
(dwl/restore-component thp/current-file-id (thp/id :component1))
:the/end))))
(t/deftest test-instantiate-component (t/deftest test-instantiate-component
(t/async (t/async