Update colors and typographies inside components

This commit is contained in:
Andrés Moya 2020-10-02 14:58:49 +02:00 committed by Alonso Torres
parent b97bbd10f0
commit 272d023f3c
3 changed files with 116 additions and 154 deletions

View file

@ -20,9 +20,10 @@
(update data :pages-index #(d/mapm f %))) (update data :pages-index #(d/mapm f %)))
(defn select-objects (defn select-objects
"Get a list of all objects in a page that satisfy a condition" "Get a list of all objects in a container (a page or a component) that
[f page] satisfy a condition"
(filter f (vals (get page :objects)))) [f container]
(filter f (vals (get container :objects))))
(defn update-object-list (defn update-object-list
"Update multiple objects in a page at once" "Update multiple objects in a page at once"

View file

@ -450,12 +450,14 @@
ptk/WatchEvent ptk/WatchEvent
(watch [_ state stream] (watch [_ state stream]
(let [[rchanges1 uchanges1] (dwlh/generate-sync-file-components state file-id) (let [[rchanges1 uchanges1] (dwlh/generate-sync-file :components file-id state)
[rchanges2 uchanges2] (dwlh/generate-sync-library-components state file-id) [rchanges2 uchanges2] (dwlh/generate-sync-library :components file-id state)
[rchanges3 uchanges3] (dwlh/generate-sync-file :colors file-id state) [rchanges3 uchanges3] (dwlh/generate-sync-file :colors file-id state)
[rchanges4 uchanges4] (dwlh/generate-sync-file :typographies file-id state) [rchanges4 uchanges4] (dwlh/generate-sync-library :colors file-id state)
rchanges (d/concat rchanges1 rchanges2 rchanges3 rchanges4) [rchanges5 uchanges5] (dwlh/generate-sync-file :typographies file-id state)
uchanges (d/concat uchanges1 uchanges2 uchanges3 uchanges4)] [rchanges6 uchanges6] (dwlh/generate-sync-library :typographies file-id state)
rchanges (d/concat rchanges1 rchanges2 rchanges3 rchanges4 rchanges5 rchanges6)
uchanges (d/concat uchanges1 uchanges2 uchanges3 uchanges4 uchanges5 uchanges6)]
(rx/concat (rx/concat
(rx/of (dm/hide-tag :sync-dialog)) (rx/of (dm/hide-tag :sync-dialog))
(when rchanges (when rchanges
@ -464,7 +466,7 @@
(rp/mutation :update-sync (rp/mutation :update-sync
{:file-id (get-in state [:workspace-file :id]) {:file-id (get-in state [:workspace-file :id])
:library-id file-id})) :library-id file-id}))
(when (seq rchanges2) (when (or (seq rchanges2) (seq rchanges4) (seq rchanges6))
(rx/of (sync-file-2nd-stage file-id)))))))) (rx/of (sync-file-2nd-stage file-id))))))))
(defn sync-file-2nd-stage (defn sync-file-2nd-stage
@ -481,7 +483,7 @@
(ptk/reify ::sync-file-2nd-stage (ptk/reify ::sync-file-2nd-stage
ptk/WatchEvent ptk/WatchEvent
(watch [_ state stream] (watch [_ state stream]
(let [[rchanges uchanges] (dwlh/generate-sync-file-components state nil)] (let [[rchanges uchanges] (dwlh/generate-sync-file :components nil state)]
(when rchanges (when rchanges
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true}))))))) (rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true})))))))

View file

@ -19,7 +19,7 @@
(defonce empty-changes [[] []]) (defonce empty-changes [[] []])
(declare generate-sync-page) (declare generate-sync-container)
(declare generate-sync-shape) (declare generate-sync-shape)
(declare generate-sync-component-components) (declare generate-sync-component-components)
@ -30,9 +30,11 @@
(declare update-attrs) (declare update-attrs)
(declare calc-new-pos) (declare calc-new-pos)
;; ---- General library synchronization functions ----
(defn generate-sync-file (defn generate-sync-file
"Generic method that given a type of asset will iterate through the file pages "Generate changes to synchronize all shapes in all pages of the current file,
and call synchronize" with the given asset of the given library."
[asset-type library-id state] [asset-type library-id state]
(s/assert #{:colors :components :typographies} asset-type) (s/assert #{:colors :components :typographies} asset-type)
@ -51,13 +53,47 @@
uchanges []] uchanges []]
(if-let [page (first pages)] (if-let [page (first pages)]
(let [[page-rchanges page-uchanges] (let [[page-rchanges page-uchanges]
(generate-sync-page asset-type library-id library-items page)] (generate-sync-container asset-type
library-id
library-items
page
(:id page)
nil)]
(recur (next pages) (recur (next pages)
(d/concat rchanges page-rchanges) (d/concat rchanges page-rchanges)
(d/concat uchanges page-uchanges))) (d/concat uchanges page-uchanges)))
[rchanges uchanges]))))) [rchanges uchanges])))))
(defn generate-sync-library
"Generate changes to synchronize all shapes inside components of the current
file library, that use the given type of asset of the given library."
[asset-type library-id state]
(let [library-items
(if (nil? library-id)
(get-in state [:workspace-data asset-type])
(get-in state [:workspace-libraries library-id :data asset-type]))]
(if (empty? library-items)
empty-changes
(loop [local-components (seq (vals (get-in state [:workspace-data :components])))
rchanges []
uchanges []]
(if-let [local-component (first local-components)]
(let [[comp-rchanges comp-uchanges]
(generate-sync-container asset-type
library-id
library-items
local-component
nil
(:id local-component))]
(recur (next local-components)
(d/concat rchanges comp-rchanges)
(d/concat uchanges comp-uchanges)))
[rchanges uchanges])))))
(defn has-asset-reference-fn (defn has-asset-reference-fn
"Gets a function that checks if a shape uses some asset of the given type
in the given library."
[asset-type library-id] [asset-type library-id]
(case asset-type (case asset-type
:components :components
@ -83,35 +119,54 @@
#(and (some? (:typography-ref-id %)) #(and (some? (:typography-ref-id %))
(= library-id (:typography-ref-file %))))))))) (= library-id (:typography-ref-file %)))))))))
(defn generate-sync-page (defn generate-sync-container
[asset-type library-id library-items page] "Generate changes to synchronize all shapes in a particular container
(a page or a component)."
[asset-type library-id library-items container page-id component-id]
(let [has-asset-reference? (has-asset-reference-fn asset-type library-id) (let [has-asset-reference? (has-asset-reference-fn asset-type library-id)
linked-shapes (cph/select-objects has-asset-reference? page)] linked-shapes (cph/select-objects has-asset-reference? container)]
(loop [shapes (seq linked-shapes) (loop [shapes (seq linked-shapes)
rchanges [] rchanges []
uchanges []] uchanges []]
(if-let [shape (first shapes)] (if-let [shape (first shapes)]
(let [[shape-rchanges shape-uchanges] (let [[shape-rchanges shape-uchanges]
(generate-sync-shape asset-type library-id library-items page shape)] (generate-sync-shape asset-type
library-id
library-items
(get container :objects)
page-id
component-id
shape)]
(recur (next shapes) (recur (next shapes)
(d/concat rchanges shape-rchanges) (d/concat rchanges shape-rchanges)
(d/concat uchanges shape-uchanges))) (d/concat uchanges shape-uchanges)))
[rchanges uchanges])))) [rchanges uchanges]))))
(defmulti generate-sync-shape (fn [type _ _ _ _ _] type)) (defmulti generate-sync-shape (fn [type _ _ _ _ _ _ _] type))
(defmethod generate-sync-shape :components (defmethod generate-sync-shape :components
[_ library-id library-items page shape] [_ library-id library-items objects page-id component-id shape]
;; Synchronize a shape that is the root instance of a component, and all of its
;; children. All attributes of the component shape that have changed, and whose
;; group have not been touched in the linked shape, will be copied to the shape.
;; Any shape that is linked to a no-longer existent component shape will be
;; detached.
(let [root-shape shape (let [root-shape shape
objects (:objects page)
components library-items components library-items
page-id nil
component-id (:id page)
reset-touched? false] reset-touched? false]
(generate-sync-shape-and-children-components root-shape objects components page-id component-id reset-touched?))) (generate-sync-shape-and-children-components root-shape
objects
components
page-id
component-id
reset-touched?)))
(defmethod generate-sync-shape :colors (defmethod generate-sync-shape :colors
[_ library-id library-items page shape] [_ library-id library-items _ page-id component-id shape]
;; Synchronize a shape that uses some colors of the library. The value of the
;; color in the library is copied to the shape.
(loop [attrs (seq cp/color-sync-attrs) (loop [attrs (seq cp/color-sync-attrs)
roperations [] roperations []
uoperations []] uoperations []]
@ -119,14 +174,16 @@
(if (nil? attr) (if (nil? attr)
(if (empty? roperations) (if (empty? roperations)
empty-changes empty-changes
(let [rchanges [{:type :mod-obj (let [rchanges [(d/without-nils {:type :mod-obj
:page-id (:id page) :page-id page-id
:component-id component-id
:id (:id shape) :id (:id shape)
:operations roperations}] :operations roperations})]
uchanges [{:type :mod-obj uchanges [(d/without-nils {:type :mod-obj
:page-id (:id page) :page-id page-id
:component-id component-id
:id (:id shape) :id (:id shape)
:operations uoperations}]] :operations uoperations})]]
[rchanges uchanges])) [rchanges uchanges]))
(let [attr-ref-id (keyword (str (name attr) "-ref-id"))] (let [attr-ref-id (keyword (str (name attr) "-ref-id"))]
(if-not (contains? shape attr-ref-id) (if-not (contains? shape attr-ref-id)
@ -147,29 +204,35 @@
(conj uoperations uoperation))))))))) (conj uoperations uoperation)))))))))
(defmethod generate-sync-shape :typographies (defmethod generate-sync-shape :typographies
[_ library-id library-items page shape] [_ library-id library-items _ page-id component-id shape]
;; Synchronize a shape that uses some typographies of the library. The attributes
;; of the typography are copied to the shape."
(let [update-node (fn [node] (let [update-node (fn [node]
(if-let [typography (get library-items (:typography-ref-id node))] (if-let [typography (get library-items (:typography-ref-id node))]
(merge node (d/without-keys typography [:name :id])) (merge node (d/without-keys typography [:name :id]))
node)) node))
old-content (:content shape) old-content (:content shape)
new-content (ut/map-node update-node old-content) new-content (ut/map-node update-node old-content)
rchanges [{:type :mod-obj rchanges [(d/without-nils {:type :mod-obj
:page-id (:id page) :page-id page-id
:component-id component-id
:id (:id shape) :id (:id shape)
:operations [{:type :set :operations [{:type :set
:attr :content :attr :content
:val new-content}]}] :val new-content}]})]
lchanges [{:type :mod-obj lchanges [(d/without-nils {:type :mod-obj
:page-id (:id page) :page-id page-id
:component-id component-id
:id (:id shape) :id (:id shape)
:operations [{:type :set :operations [{:type :set
:attr :content :attr :content
:val old-content}]}]] :val old-content}]})]]
(if (= new-content old-content) (if (= new-content old-content)
empty-changes empty-changes
[rchanges lchanges]))) [rchanges lchanges])))
;; ---- Create a new component ---- ;; ---- Create a new component ----
(defn make-component-shape (defn make-component-shape
@ -199,118 +262,14 @@
(cph/clone-object shape nil objects update-new-shape update-original-shape))) (cph/clone-object shape nil objects update-new-shape update-original-shape)))
;; ---- Synchronize shapes with components ;; ---- Component synchronization helpers ----
(declare generate-sync-page-components)
(defn generate-sync-file-components
"Generate changes to synchronize all shapes in current file that are linked
to some component in the given library. All attributes of the components
that have changed, and whose group have not been touched in the linked shape,
will be copied to the shape. Any shape that is linked to a no-longer
existent component will be detached."
[state library-id]
(let [components
(if (nil? library-id)
(get-in state [:workspace-data :components])
(get-in state [:workspace-libraries library-id :data :components]))]
(if (nil? components)
[[] []]
(loop [pages (seq (vals (get-in state [:workspace-data :pages-index])))
rchanges []
uchanges []]
(let [page (first pages)]
(if (nil? page)
[rchanges uchanges]
(let [[page-rchanges page-uchanges]
(generate-sync-page-components page library-id components)]
(recur (next pages)
(d/concat rchanges page-rchanges)
(d/concat uchanges page-uchanges)))))))))
(defn generate-sync-page-components
"Generate changes to synchronize all shapes in a particular page.
Same considerations as above."
[page library-id components]
(let [objects (get page :objects)
linked-shapes (cph/select-objects #(and (some? (:component-id %))
(= (:component-file %) library-id))
page)]
(loop [shapes (seq linked-shapes)
rchanges []
uchanges []]
(let [shape (first shapes)]
(if (nil? shape)
[rchanges uchanges]
(let [[shape-rchanges shape-uchanges]
(generate-sync-shape-and-children-components shape
objects
components
(:id page)
nil
false)]
(recur (next shapes)
(d/concat rchanges shape-rchanges)
(d/concat uchanges shape-uchanges))))))))
(defn generate-sync-library-components
"Generate changes to synchronize all shapes inside components of the current
file library, that are linked to other component in the given library.
Same considerations as above."
[state library-id]
(let [components
(if (nil? library-id)
(get-in state [:workspace-data :components])
(get-in state [:workspace-libraries library-id :data :components]))]
(if (nil? components)
empty-changes
(loop [local-components (seq (vals (get-in state [:workspace-data :components])))
rchanges []
uchanges []]
(let [local-component (first local-components)]
(if (nil? local-component)
[rchanges uchanges]
(let [[comp-rchanges comp-uchanges]
(generate-sync-component-components
local-component library-id components)]
(recur (next local-components)
(d/concat rchanges comp-rchanges)
(d/concat uchanges comp-uchanges)))))))))
(defn generate-sync-component-components
"Generate changes to synchronize all shapes in a particular component.
Same considerations as above."
[local-component library-id components]
(let [objects (get local-component :objects)
linked-shapes (filter #(and (some? (:component-id %))
(= (:component-file %) library-id))
(vals objects))]
(loop [shapes (seq linked-shapes)
rchanges []
uchanges []]
(let [shape (first shapes)]
(if (nil? shape)
[rchanges uchanges]
(let [[shape-rchanges shape-uchanges]
(generate-sync-shape-and-children-components shape
objects
components
nil
(:id local-component)
false)]
(recur (next shapes)
(d/concat rchanges shape-rchanges)
(d/concat uchanges shape-uchanges))))))))
(defn generate-sync-shape-and-children-components (defn generate-sync-shape-and-children-components
"Generate changes to synchronize one shape that is linked to a component, "Generate changes to synchronize one shape that is linked to a component,
and all its children. If reset-touched? is false, same considerations as and all its children. If reset-touched? is false, same considerations as
above. If it's true, all attributes of the component that have changed in generate-sync-shape :components. If it's true, all attributes of the
will be copied, and the 'touched' flags in the shapes will be cleared." component that have changed will be copied, and the 'touched' flags in
the shapes will be cleared."
[root-shape objects components page-id component-id reset-touched?] [root-shape objects components page-id component-id reset-touched?]
(let [all-shapes (cph/get-object-with-children (:id root-shape) objects) (let [all-shapes (cph/get-object-with-children (:id root-shape) objects)
component (get components (:component-id root-shape)) component (get components (:component-id root-shape))