mirror of
https://github.com/penpot/penpot.git
synced 2025-06-01 18:11:37 +02:00
✨ Rework changes detection
This commit is contained in:
parent
43b1d3ca43
commit
fe7faf0d0d
5 changed files with 296 additions and 155 deletions
|
@ -77,8 +77,10 @@
|
||||||
(cond-> (and (:shape-ref (get-in data [:objects parent-id]))
|
(cond-> (and (:shape-ref (get-in data [:objects parent-id]))
|
||||||
(not= parent-id frame-id)
|
(not= parent-id frame-id)
|
||||||
(not ignore-touched))
|
(not ignore-touched))
|
||||||
|
(->
|
||||||
(update-in [:objects parent-id :touched]
|
(update-in [:objects parent-id :touched]
|
||||||
cph/set-touched-group :shapes-group)))
|
cph/set-touched-group :shapes-group)
|
||||||
|
(d/dissoc-in [:objects parent-id :remote-synced?]))))
|
||||||
data)))]
|
data)))]
|
||||||
(if page-id
|
(if page-id
|
||||||
(d/update-in-when data [:pages-index page-id] update-fn)
|
(d/update-in-when data [:pages-index page-id] update-fn)
|
||||||
|
@ -110,7 +112,9 @@
|
||||||
(update-in [parent-id :shapes] (fn [s] (filterv #(not= % id) s)))
|
(update-in [parent-id :shapes] (fn [s] (filterv #(not= % id) s)))
|
||||||
|
|
||||||
(and (:shape-ref parent) (not ignore-touched))
|
(and (:shape-ref parent) (not ignore-touched))
|
||||||
|
(->
|
||||||
(update-in [parent-id :touched] cph/set-touched-group :shapes-group)
|
(update-in [parent-id :touched] cph/set-touched-group :shapes-group)
|
||||||
|
(d/dissoc-in [parent-id :remote-synced?]))
|
||||||
|
|
||||||
(contains? objects frame-id)
|
(contains? objects frame-id)
|
||||||
(update-in [frame-id :shapes] (fn [s] (filterv #(not= % id) s)))
|
(update-in [frame-id :shapes] (fn [s] (filterv #(not= % id) s)))
|
||||||
|
@ -185,7 +189,9 @@
|
||||||
(update :shapes check-insert-items parent index shapes)
|
(update :shapes check-insert-items parent index shapes)
|
||||||
|
|
||||||
(and (:shape-ref parent) (= (:type parent) :group) (not ignore-touched))
|
(and (:shape-ref parent) (= (:type parent) :group) (not ignore-touched))
|
||||||
(update :touched cph/set-touched-group :shapes-group)))
|
(->
|
||||||
|
(update :touched cph/set-touched-group :shapes-group)
|
||||||
|
(dissoc :remote-synced?))))
|
||||||
|
|
||||||
(remove-from-old-parent [cpindex objects shape-id]
|
(remove-from-old-parent [cpindex objects shape-id]
|
||||||
(let [prev-parent-id (get cpindex shape-id)]
|
(let [prev-parent-id (get cpindex shape-id)]
|
||||||
|
@ -210,8 +216,10 @@
|
||||||
(and (:shape-ref obj)
|
(and (:shape-ref obj)
|
||||||
(= (:type obj) :group)
|
(= (:type obj) :group)
|
||||||
(not ignore-touched))
|
(not ignore-touched))
|
||||||
|
(->
|
||||||
(update-in [pid :touched]
|
(update-in [pid :touched]
|
||||||
cph/set-touched-group :shapes-group))))))))
|
cph/set-touched-group :shapes-group)
|
||||||
|
(d/dissoc-in [pid :remote-synced?])))))))))
|
||||||
|
|
||||||
(update-parent-id [objects id]
|
(update-parent-id [objects id]
|
||||||
(update objects id assoc :parent-id parent-id))
|
(update objects id assoc :parent-id parent-id))
|
||||||
|
@ -392,7 +400,9 @@
|
||||||
;; touched). For the moment we disable geometry touched
|
;; touched). For the moment we disable geometry touched
|
||||||
;; except width and height that seems to work well.
|
;; except width and height that seems to work well.
|
||||||
(or (not= group :geometry-group) (#{:width :height} attr)))
|
(or (not= group :geometry-group) (#{:width :height} attr)))
|
||||||
|
(->
|
||||||
(update :touched cph/set-touched-group group)
|
(update :touched cph/set-touched-group group)
|
||||||
|
(dissoc :remote-synced?))
|
||||||
|
|
||||||
(nil? val)
|
(nil? val)
|
||||||
(dissoc attr)
|
(dissoc attr)
|
||||||
|
@ -408,6 +418,14 @@
|
||||||
(dissoc shape :touched)
|
(dissoc shape :touched)
|
||||||
(assoc shape :touched touched))))
|
(assoc shape :touched touched))))
|
||||||
|
|
||||||
|
(defmethod process-operation :set-remote-synced
|
||||||
|
[shape op]
|
||||||
|
(let [remote-synced? (:remote-synced? op)
|
||||||
|
shape-ref (:shape-ref shape)]
|
||||||
|
(if (or (nil? shape-ref) (not remote-synced?))
|
||||||
|
(dissoc shape :remote-synced?)
|
||||||
|
(assoc shape :remote-synced? true))))
|
||||||
|
|
||||||
(defmethod process-operation :default
|
(defmethod process-operation :default
|
||||||
[_ op]
|
[_ op]
|
||||||
(ex/raise :type :not-implemented
|
(ex/raise :type :not-implemented
|
||||||
|
|
|
@ -427,6 +427,8 @@
|
||||||
(s/def :internal.operations.set/val any?)
|
(s/def :internal.operations.set/val any?)
|
||||||
(s/def :internal.operations.set/touched
|
(s/def :internal.operations.set/touched
|
||||||
(s/nilable (s/every keyword? :kind set?)))
|
(s/nilable (s/every keyword? :kind set?)))
|
||||||
|
(s/def :internal.operations.set/remote-synced?
|
||||||
|
(s/nilable boolean?))
|
||||||
|
|
||||||
(defmethod operation-spec :set [_]
|
(defmethod operation-spec :set [_]
|
||||||
(s/keys :req-un [:internal.operations.set/attr
|
(s/keys :req-un [:internal.operations.set/attr
|
||||||
|
@ -435,6 +437,9 @@
|
||||||
(defmethod operation-spec :set-touched [_]
|
(defmethod operation-spec :set-touched [_]
|
||||||
(s/keys :req-un [:internal.operations.set/touched]))
|
(s/keys :req-un [:internal.operations.set/touched]))
|
||||||
|
|
||||||
|
(defmethod operation-spec :set-remote-synced [_]
|
||||||
|
(s/keys :req-un [:internal.operations.set/remote-synced?]))
|
||||||
|
|
||||||
(defmulti change-spec :type)
|
(defmulti change-spec :type)
|
||||||
|
|
||||||
(s/def :internal.changes.set-option/option any?)
|
(s/def :internal.changes.set-option/option any?)
|
||||||
|
|
|
@ -384,7 +384,8 @@
|
||||||
(geom/move $ delta)
|
(geom/move $ delta)
|
||||||
(assoc $ :frame-id frame-id)
|
(assoc $ :frame-id frame-id)
|
||||||
(assoc $ :parent-id
|
(assoc $ :parent-id
|
||||||
(or (:parent-id $) (:frame-id $))))
|
(or (:parent-id $) (:frame-id $)))
|
||||||
|
(dissoc $ :touched))
|
||||||
|
|
||||||
(nil? (:shape-ref original-shape))
|
(nil? (:shape-ref original-shape))
|
||||||
(assoc :shape-ref (:id original-shape))
|
(assoc :shape-ref (:id original-shape))
|
||||||
|
@ -448,6 +449,9 @@
|
||||||
{:type :set
|
{:type :set
|
||||||
:attr :component-root?
|
:attr :component-root?
|
||||||
:val nil}
|
:val nil}
|
||||||
|
{:type :set
|
||||||
|
:attr :remote-synced?
|
||||||
|
:val nil}
|
||||||
{:type :set
|
{:type :set
|
||||||
:attr :shape-ref
|
:attr :shape-ref
|
||||||
:val nil}
|
:val nil}
|
||||||
|
@ -469,6 +473,9 @@
|
||||||
{:type :set
|
{:type :set
|
||||||
:attr :component-root?
|
:attr :component-root?
|
||||||
:val (:component-root? obj)}
|
:val (:component-root? obj)}
|
||||||
|
{:type :set
|
||||||
|
:attr :remote-synced?
|
||||||
|
:val (:remote-synced? obj)}
|
||||||
{:type :set
|
{:type :set
|
||||||
:attr :shape-ref
|
:attr :shape-ref
|
||||||
:val (:shape-ref obj)}
|
:val (:shape-ref obj)}
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
(ns app.main.data.workspace.libraries-helpers
|
(ns app.main.data.workspace.libraries-helpers
|
||||||
(:require
|
(:require
|
||||||
[cljs.spec.alpha :as s]
|
[cljs.spec.alpha :as s]
|
||||||
|
[clojure.set :as set]
|
||||||
[app.common.spec :as us]
|
[app.common.spec :as us]
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
[app.common.geom.point :as gpt]
|
[app.common.geom.point :as gpt]
|
||||||
|
@ -48,6 +49,7 @@
|
||||||
(declare remove-shape)
|
(declare remove-shape)
|
||||||
(declare move-shape)
|
(declare move-shape)
|
||||||
(declare change-touched)
|
(declare change-touched)
|
||||||
|
(declare change-remote-synced)
|
||||||
(declare update-attrs)
|
(declare update-attrs)
|
||||||
(declare reposition-shape)
|
(declare reposition-shape)
|
||||||
|
|
||||||
|
@ -122,7 +124,7 @@
|
||||||
|
|
||||||
(defn generate-sync-file
|
(defn generate-sync-file
|
||||||
"Generate changes to synchronize all shapes in all pages of the current file,
|
"Generate changes to synchronize all shapes in all pages of the current file,
|
||||||
with the given asset of the given library."
|
that use assets of the given type in 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)
|
||||||
(s/assert ::us/uuid library-id)
|
(s/assert ::us/uuid library-id)
|
||||||
|
@ -154,8 +156,8 @@
|
||||||
[rchanges uchanges])))))
|
[rchanges uchanges])))))
|
||||||
|
|
||||||
(defn generate-sync-library
|
(defn generate-sync-library
|
||||||
"Generate changes to synchronize all shapes inside components of the current
|
"Generate changes to synchronize all shapes in all components of the current
|
||||||
file library, that use the given type of asset of the given library."
|
file library, that use assets of the given type in the given library."
|
||||||
[asset-type library-id state]
|
[asset-type library-id state]
|
||||||
|
|
||||||
(log/info :msg "Sync local components with library"
|
(log/info :msg "Sync local components with library"
|
||||||
|
@ -185,8 +187,8 @@
|
||||||
[rchanges uchanges])))))
|
[rchanges uchanges])))))
|
||||||
|
|
||||||
(defn- generate-sync-container
|
(defn- generate-sync-container
|
||||||
"Generate changes to synchronize all shapes in a particular container
|
"Generate changes to synchronize all shapes in a particular container (a page
|
||||||
(a page or a component) that are linked to the given library."
|
or a component) that use assets of the given type in the given library."
|
||||||
[asset-type library-id state container]
|
[asset-type library-id state container]
|
||||||
|
|
||||||
(if (cp/page? container)
|
(if (cp/page? container)
|
||||||
|
@ -250,9 +252,9 @@
|
||||||
(= library-id (:typography-ref-file %)))))))))
|
(= library-id (:typography-ref-file %)))))))))
|
||||||
|
|
||||||
(defmulti generate-sync-shape
|
(defmulti generate-sync-shape
|
||||||
"Generate changes to synchronize one shape, that use the given type
|
"Generate changes to synchronize one shape with all assets of the given type
|
||||||
of asset of the given library."
|
that is using, in the given library."
|
||||||
(fn [type _ _ _ _] type))
|
(fn [type library-id state container shape] type))
|
||||||
|
|
||||||
(defmethod generate-sync-shape :components
|
(defmethod generate-sync-shape :components
|
||||||
[_ library-id state container shape]
|
[_ library-id state container shape]
|
||||||
|
@ -353,23 +355,116 @@
|
||||||
node))]
|
node))]
|
||||||
(generate-sync-text-shape shape container update-node)))
|
(generate-sync-text-shape shape container update-node)))
|
||||||
|
|
||||||
|
|
||||||
;; ---- Component synchronization helpers ----
|
|
||||||
|
|
||||||
(defn- get-assets
|
(defn- get-assets
|
||||||
[library-id asset-type state]
|
[library-id asset-type state]
|
||||||
(if (= library-id (:current-file-id state))
|
(if (= library-id (:current-file-id state))
|
||||||
(get-in state [:workspace-data asset-type])
|
(get-in state [:workspace-data asset-type])
|
||||||
(get-in state [:workspace-libraries library-id :data asset-type])))
|
(get-in state [:workspace-libraries library-id :data asset-type])))
|
||||||
|
|
||||||
|
|
||||||
|
;; ---- Component synchronization helpers ----
|
||||||
|
|
||||||
|
;; Three sources of component synchronization:
|
||||||
|
;;
|
||||||
|
;; - NORMAL SYNC: when a component is updated, any shape that use it,
|
||||||
|
;; must be synchronized. All attributes that have changed in the
|
||||||
|
;; component and whose attr group has not been "touched" in the dest
|
||||||
|
;; shape are copied.
|
||||||
|
;;
|
||||||
|
;; generate-sync-shape-direct (reset = false)
|
||||||
|
;;
|
||||||
|
;; - FORCED SYNC: when the "reset" command is applied to some shape,
|
||||||
|
;; all attributes that have changed in the component are copied, and
|
||||||
|
;; the "touched" flags are cleared.
|
||||||
|
;;
|
||||||
|
;; generate-sync-shape-direct (reset = true)
|
||||||
|
;;
|
||||||
|
;; - INVERSE SYNC: when the "update component" command is used in some
|
||||||
|
;; shape, all the attributes that have changed in the shape are copied
|
||||||
|
;; into the linked component. The "touched" flags are also cleared in
|
||||||
|
;; the origin shape.
|
||||||
|
;;
|
||||||
|
;; generate-sync-shape-inverse
|
||||||
|
;;
|
||||||
|
;; The initial shape is always a group (a root instance), so all the
|
||||||
|
;; children are recursively synced, too. A root instance is a group shape
|
||||||
|
;; that has the "component-id" attribute and also "component-root?" is true.
|
||||||
|
;;
|
||||||
|
;; The children lists of the instance and the component shapes are compared
|
||||||
|
;; side-by-side. Any new, deleted or moved child modifies (and "touches")
|
||||||
|
;; the parent shape.
|
||||||
|
;;
|
||||||
|
;; When a shape inside a component is in turn an instance of another
|
||||||
|
;; component, the synchronization is more complex:
|
||||||
|
;;
|
||||||
|
;; [Page]
|
||||||
|
;; Instance-2 #--> Component-2 (#--> = root instance)
|
||||||
|
;; IShape-2-1 --> Shape-2-1 (@--> = nested instance)
|
||||||
|
;; Subinstance-2-2 @--> Component-1 ( --> = shape ref)
|
||||||
|
;; IShape-2-2-1 --> Shape-1-1
|
||||||
|
;;
|
||||||
|
;; [Component-1]
|
||||||
|
;; Component-1
|
||||||
|
;; Shape-1-1
|
||||||
|
;;
|
||||||
|
;; [Component-2]
|
||||||
|
;; Component-2
|
||||||
|
;; Shape-2-1
|
||||||
|
;; Subcomponent-2-2 @--> Component-1
|
||||||
|
;; Shape-2-2-1 --> Shape-1-1
|
||||||
|
;;
|
||||||
|
;; * A SUBINSTANCE ACTUALLY HAS TWO MASTERS. For example IShape-2-2-1
|
||||||
|
;; depends on Shape-2-2-1 (in the "near" component) but also on
|
||||||
|
;; Shape-1-1-1 (in the "remote" component). The "shape-ref" attribute
|
||||||
|
;; always refer to the remote shape, and it's guaranteed that it's
|
||||||
|
;; always a final shape, not an instance. The relationship between the
|
||||||
|
;; shape and the near shape is that both point to the same remote.
|
||||||
|
;;
|
||||||
|
;; * THE INITIAL VALUE of IShape-2-2-1 comes from the near component
|
||||||
|
;; Shape-2-2-1 (although the shape-ref attribute points to the direct
|
||||||
|
;; component Shape-1-1). The touched flags of IShape-2-2-1 start
|
||||||
|
;; cleared at first, and activate on any attribute change onwards.
|
||||||
|
;;
|
||||||
|
;; * IN A NORMAL SYNC, the sync process starts in the root instance and
|
||||||
|
;; continues recursively with the children of the root instance and
|
||||||
|
;; the component. Therefore, IShape-2-2-1 is synced with Shape-2-2-1.
|
||||||
|
;;
|
||||||
|
;; * IN A FORCED SYNC, IF THE INITIAL SHAPE IS THE ROOT INSTANCE, the
|
||||||
|
;; order is the same, and IShape-2-2-1 is reset from Shape-2-2-1 and
|
||||||
|
;; marked as not touched.
|
||||||
|
;;
|
||||||
|
;; * IF THE INITIAL SHAPE IS THE SUBINSTANCE, the sync is done against
|
||||||
|
;; the remote component. Therefore, IShape-2-2-1 is synched with
|
||||||
|
;; Shape-1-1. Then the "touched" flags are reset, and the
|
||||||
|
;; "remote-synced?" flag is set (it will be set until the shape is
|
||||||
|
;; touched again or it's synced forced normal or inverse with the
|
||||||
|
;; near component).
|
||||||
|
;;
|
||||||
|
;; * IN AN INVERSE SYNC, IF THE INITIAL SHAPE IS THE ROOT INSTANCE, the
|
||||||
|
;; order is the same as in the normal sync. Therefore, IShape-2-2-1
|
||||||
|
;; values are copied into Shape-2-2-1, and then its touched flags are
|
||||||
|
;; cleared. Then, the "touched" flags THAT ARE TRUE are copied to
|
||||||
|
;; Shape-2-2-1. This may cause that Shape-2-2-1 is now touched respect
|
||||||
|
;; to Shape-1-1, and so, some attributes are not copied in a subsequent
|
||||||
|
;; normal sync. Or, if "remote-synced?" flag is set in IShape-2-2-1,
|
||||||
|
;; all touched flags are cleared in Shape-2-2-1 and "remote-synced?"
|
||||||
|
;; is removed.
|
||||||
|
;;
|
||||||
|
;; * IN AN INVERSE SYNC INITIATED IN THE SUBINSTANCE, the update is done
|
||||||
|
;; to the remote component. E.g. IShape-2-2-1 attributes are copied into
|
||||||
|
;; Shape-1-1, and then touched cleared and "remote-synced?" flag set.
|
||||||
|
;;
|
||||||
|
;; #### WARNING: there are two conditions that are invisible to user:
|
||||||
|
;; - When the near shape (Shape-2-2-1) is touched respect the remote
|
||||||
|
;; one (Shape-1-1), there is no asterisk displayed anywhere.
|
||||||
|
;; - When the instance shape (IShape-2-2-1) is synced with the remote
|
||||||
|
;; shape (remote-synced? = true), the user will see that this shape
|
||||||
|
;; is different than the one in the near component (Shape-2-2-1)
|
||||||
|
;; but it's not touched.
|
||||||
|
|
||||||
(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."
|
||||||
If reset? is false, all atributes of each component shape that have
|
|
||||||
changed, and whose group has not been touched in the instance shape will
|
|
||||||
be copied to this one.
|
|
||||||
If reset? is true, all changed attributes will be copied and the 'touched'
|
|
||||||
flags in the instance shape will be cleared."
|
|
||||||
[container shape-id local-library libraries reset?]
|
[container shape-id local-library libraries reset?]
|
||||||
(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 (cp/get-shape container shape-id)
|
(let [shape-inst (cp/get-shape container shape-id)
|
||||||
|
@ -379,6 +474,8 @@
|
||||||
libraries)
|
libraries)
|
||||||
shape-master (cp/get-shape component (:shape-ref shape-inst))
|
shape-master (cp/get-shape component (:shape-ref shape-inst))
|
||||||
|
|
||||||
|
initial-root? (:component-root? shape-inst)
|
||||||
|
|
||||||
root-inst shape-inst
|
root-inst shape-inst
|
||||||
root-master (cp/get-component-root component)]
|
root-master (cp/get-component-root component)]
|
||||||
|
|
||||||
|
@ -388,33 +485,41 @@
|
||||||
shape-master
|
shape-master
|
||||||
root-inst
|
root-inst
|
||||||
root-master
|
root-master
|
||||||
{:omit-touched? (not reset?)
|
reset?
|
||||||
:reset-touched? reset?
|
initial-root?)))
|
||||||
:copy-touched? false})))
|
|
||||||
|
|
||||||
(defn- generate-sync-shape-direct-recursive
|
(defn- generate-sync-shape-direct-recursive
|
||||||
[container shape-inst component shape-master root-inst root-master
|
[container shape-inst component shape-master root-inst root-master reset? initial-root?]
|
||||||
{:keys [omit-touched? reset-touched? copy-touched?]
|
(log/debug :msg "Sync shape direct recursive"
|
||||||
:as options :or {omit-touched? false
|
|
||||||
reset-touched? false
|
|
||||||
copy-touched? false}}]
|
|
||||||
(log/trace :msg "Sync shape direct recursive"
|
|
||||||
:shape (str (:name shape-inst))
|
:shape (str (:name shape-inst))
|
||||||
:component (:name component)
|
:component (:name component))
|
||||||
:options options)
|
|
||||||
|
|
||||||
(let [[rchanges uchanges]
|
(let [omit-touched? (not reset?)
|
||||||
|
clear-remote-synced? (and initial-root? reset?)
|
||||||
|
set-remote-synced? (and (not initial-root?) reset?)
|
||||||
|
|
||||||
|
[rchanges uchanges]
|
||||||
(concat-changes
|
(concat-changes
|
||||||
(update-attrs shape-inst
|
(update-attrs shape-inst
|
||||||
shape-master
|
shape-master
|
||||||
root-inst
|
root-inst
|
||||||
root-master
|
root-master
|
||||||
container
|
container
|
||||||
options)
|
omit-touched?)
|
||||||
|
(concat-changes
|
||||||
|
(if reset?
|
||||||
(change-touched shape-inst
|
(change-touched shape-inst
|
||||||
shape-master
|
shape-master
|
||||||
container
|
container
|
||||||
options))
|
{:reset-touched? true})
|
||||||
|
empty-changes)
|
||||||
|
(concat-changes
|
||||||
|
(if clear-remote-synced?
|
||||||
|
(change-remote-synced shape-inst container nil)
|
||||||
|
empty-changes)
|
||||||
|
(if set-remote-synced?
|
||||||
|
(change-remote-synced shape-inst container true)
|
||||||
|
empty-changes))))
|
||||||
|
|
||||||
children-inst (mapv #(cp/get-shape container %)
|
children-inst (mapv #(cp/get-shape container %)
|
||||||
(:shapes shape-inst))
|
(:shapes shape-inst))
|
||||||
|
@ -432,18 +537,12 @@
|
||||||
container
|
container
|
||||||
root-inst
|
root-inst
|
||||||
root-master
|
root-master
|
||||||
omit-touched?))
|
omit-touched?
|
||||||
|
set-remote-synced?))
|
||||||
|
|
||||||
both (fn [child-inst child-master]
|
both (fn [child-inst child-master]
|
||||||
(let [sub-root? (and (:component-id shape-inst)
|
(let [sub-root? (and (:component-id shape-inst)
|
||||||
(not (:component-root? shape-inst)))
|
(not (:component-root? shape-inst)))]
|
||||||
|
|
||||||
options (if-not sub-root?
|
|
||||||
options
|
|
||||||
{:omit-touched? true
|
|
||||||
:reset-touched? false
|
|
||||||
:copy-touched? false})]
|
|
||||||
|
|
||||||
(generate-sync-shape-direct-recursive container
|
(generate-sync-shape-direct-recursive container
|
||||||
child-inst
|
child-inst
|
||||||
component
|
component
|
||||||
|
@ -454,7 +553,8 @@
|
||||||
(if sub-root?
|
(if sub-root?
|
||||||
shape-master
|
shape-master
|
||||||
root-master)
|
root-master)
|
||||||
options)))
|
reset?
|
||||||
|
initial-root?)))
|
||||||
|
|
||||||
moved (fn [shape-inst shape-master]
|
moved (fn [shape-inst shape-master]
|
||||||
(move-shape
|
(move-shape
|
||||||
|
@ -478,12 +578,7 @@
|
||||||
|
|
||||||
(defn- generate-sync-shape-inverse
|
(defn- generate-sync-shape-inverse
|
||||||
"Generate changes to update the component a shape is linked to, from
|
"Generate changes to update the component a shape is linked to, from
|
||||||
the values in the shape and all its children.
|
the values in the shape and all its children."
|
||||||
All atributes of each instance shape that have changed, will be copied
|
|
||||||
to the component shape. Also clears the 'touched' flags in the source
|
|
||||||
shapes.
|
|
||||||
And if the component shapes are, in turn, instances of a second component,
|
|
||||||
their 'touched' flags will be set accordingly."
|
|
||||||
[page-id shape-id local-library libraries]
|
[page-id shape-id local-library libraries]
|
||||||
(log/debug :msg "Sync shape inverse" :shape (str shape-id))
|
(log/debug :msg "Sync shape inverse" :shape (str shape-id))
|
||||||
(let [container (cp/get-container page-id :page local-library)
|
(let [container (cp/get-container page-id :page local-library)
|
||||||
|
@ -494,6 +589,8 @@
|
||||||
libraries)
|
libraries)
|
||||||
shape-master (cp/get-shape component (:shape-ref shape-inst))
|
shape-master (cp/get-shape component (:shape-ref shape-inst))
|
||||||
|
|
||||||
|
initial-root? (:component-root? shape-inst)
|
||||||
|
|
||||||
root-inst shape-inst
|
root-inst shape-inst
|
||||||
root-master (cp/get-component-root component)]
|
root-master (cp/get-component-root component)]
|
||||||
|
|
||||||
|
@ -503,23 +600,20 @@
|
||||||
shape-master
|
shape-master
|
||||||
root-inst
|
root-inst
|
||||||
root-master
|
root-master
|
||||||
{:reset-touched? false
|
initial-root?)))
|
||||||
:set-touched? true
|
|
||||||
:copy-touched? false})))
|
|
||||||
|
|
||||||
(defn- generate-sync-shape-inverse-recursive
|
(defn- generate-sync-shape-inverse-recursive
|
||||||
[container shape-inst component shape-master root-inst root-master
|
[container shape-inst component shape-master root-inst root-master initial-root?]
|
||||||
{:keys [reset-touched? set-touched? copy-touched?]
|
|
||||||
:as options :or {reset-touched? false
|
|
||||||
set-touched? false
|
|
||||||
copy-touched? false}}]
|
|
||||||
(log/trace :msg "Sync shape inverse recursive"
|
(log/trace :msg "Sync shape inverse recursive"
|
||||||
:shape (str (:name shape-inst))
|
:shape (str (:name shape-inst))
|
||||||
:component (:name component)
|
:component (:name component))
|
||||||
:options options)
|
|
||||||
|
|
||||||
(let [component-container (cp/make-container component :component)
|
(let [component-container (cp/make-container component :component)
|
||||||
|
|
||||||
|
omit-touched? false
|
||||||
|
set-remote-synced? (not initial-root?)
|
||||||
|
clear-remote-synced? initial-root?
|
||||||
|
|
||||||
[rchanges uchanges]
|
[rchanges uchanges]
|
||||||
(concat-changes
|
(concat-changes
|
||||||
(update-attrs shape-master
|
(update-attrs shape-master
|
||||||
|
@ -527,15 +621,24 @@
|
||||||
root-master
|
root-master
|
||||||
root-inst
|
root-inst
|
||||||
component-container
|
component-container
|
||||||
options)
|
omit-touched?)
|
||||||
|
(concat-changes
|
||||||
|
(change-touched shape-inst
|
||||||
|
shape-master
|
||||||
|
container
|
||||||
|
{:reset-touched? true})
|
||||||
(concat-changes
|
(concat-changes
|
||||||
(change-touched shape-master
|
(change-touched shape-master
|
||||||
shape-inst
|
shape-inst
|
||||||
component-container
|
component-container
|
||||||
options)
|
{:copy-touched? true})
|
||||||
(if (:set-touched? options)
|
(concat-changes
|
||||||
(change-touched shape-inst nil container {:reset-touched? true})
|
(if clear-remote-synced?
|
||||||
empty-changes)))
|
(change-remote-synced shape-inst container nil)
|
||||||
|
empty-changes)
|
||||||
|
(if set-remote-synced?
|
||||||
|
(change-remote-synced shape-inst container true)
|
||||||
|
empty-changes)))))
|
||||||
|
|
||||||
children-inst (mapv #(cp/get-shape container %)
|
children-inst (mapv #(cp/get-shape container %)
|
||||||
(:shapes shape-inst))
|
(:shapes shape-inst))
|
||||||
|
@ -556,13 +659,7 @@
|
||||||
|
|
||||||
both (fn [child-inst child-master]
|
both (fn [child-inst child-master]
|
||||||
(let [sub-root? (and (:component-id shape-inst)
|
(let [sub-root? (and (:component-id shape-inst)
|
||||||
(not (:component-root? shape-inst)))
|
(not (:component-root? shape-inst)))]
|
||||||
|
|
||||||
options (if-not sub-root?
|
|
||||||
options
|
|
||||||
{:reset-touched? false
|
|
||||||
:set-touched? false
|
|
||||||
:copy-touched? true})]
|
|
||||||
|
|
||||||
(generate-sync-shape-inverse-recursive container
|
(generate-sync-shape-inverse-recursive container
|
||||||
child-inst
|
child-inst
|
||||||
|
@ -574,7 +671,7 @@
|
||||||
(if sub-root?
|
(if sub-root?
|
||||||
shape-master
|
shape-master
|
||||||
root-master)
|
root-master)
|
||||||
options)))
|
initial-root?)))
|
||||||
|
|
||||||
moved (fn [shape-inst shape-master]
|
moved (fn [shape-inst shape-master]
|
||||||
(move-shape
|
(move-shape
|
||||||
|
@ -660,14 +757,15 @@
|
||||||
(concat-changes (moved-cb child-inst' child-master))))))))))))
|
(concat-changes (moved-cb child-inst' child-master))))))))))))
|
||||||
|
|
||||||
(defn- add-shape-to-instance
|
(defn- add-shape-to-instance
|
||||||
[component-shape component container root-instance root-master omit-touched?]
|
[component-shape component container root-instance root-master omit-touched? set-remote-synced?]
|
||||||
(log/info :msg (str "ADD [P] " (:name component-shape)))
|
(log/info :msg (str "ADD [P] " (:name component-shape)))
|
||||||
(let [component-parent-shape (cp/get-shape component (:parent-id component-shape))
|
(let [component-parent-shape (cp/get-shape component (:parent-id component-shape))
|
||||||
parent-shape (d/seek #(cp/is-master-of component-parent-shape %)
|
parent-shape (d/seek #(cp/is-master-of component-parent-shape %)
|
||||||
(cp/get-object-with-children (:id root-instance)
|
(cp/get-object-with-children (:id root-instance)
|
||||||
(:objects container)))
|
(:objects container)))
|
||||||
all-parents (vec (cons (:id parent-shape)
|
all-parents (vec (cons (:id parent-shape)
|
||||||
(cp/get-parents parent-shape (:objects container))))
|
(cp/get-parents (:id parent-shape)
|
||||||
|
(:objects container))))
|
||||||
|
|
||||||
update-new-shape (fn [new-shape original-shape]
|
update-new-shape (fn [new-shape original-shape]
|
||||||
(let [new-shape (reposition-shape new-shape
|
(let [new-shape (reposition-shape new-shape
|
||||||
|
@ -678,7 +776,10 @@
|
||||||
(assoc :frame-id (:frame-id parent-shape))
|
(assoc :frame-id (:frame-id parent-shape))
|
||||||
|
|
||||||
(nil? (:shape-ref original-shape))
|
(nil? (:shape-ref original-shape))
|
||||||
(assoc :shape-ref (:id original-shape)))))
|
(assoc :shape-ref (:id original-shape))
|
||||||
|
|
||||||
|
set-remote-synced?
|
||||||
|
(assoc :remote-synced? true))))
|
||||||
|
|
||||||
update-original-shape (fn [original-shape new-shape]
|
update-original-shape (fn [original-shape new-shape]
|
||||||
original-shape)
|
original-shape)
|
||||||
|
@ -732,7 +833,8 @@
|
||||||
(cp/get-object-with-children (:id root-master)
|
(cp/get-object-with-children (:id root-master)
|
||||||
(:objects component)))
|
(:objects component)))
|
||||||
all-parents (vec (cons (:id component-parent-shape)
|
all-parents (vec (cons (:id component-parent-shape)
|
||||||
(cp/get-parents component-parent-shape (:objects component))))
|
(cp/get-parents (:id component-parent-shape)
|
||||||
|
(:objects component))))
|
||||||
|
|
||||||
update-new-shape (fn [new-shape original-shape]
|
update-new-shape (fn [new-shape original-shape]
|
||||||
(reposition-shape new-shape
|
(reposition-shape new-shape
|
||||||
|
@ -873,10 +975,8 @@
|
||||||
[rchanges uchanges])))
|
[rchanges uchanges])))
|
||||||
|
|
||||||
(defn- change-touched
|
(defn- change-touched
|
||||||
[dest-shape orig-shape container
|
[dest-shape origin-shape container
|
||||||
{:keys [reset-touched? copy-touched?]
|
{:keys [reset-touched? copy-touched?] :as options}]
|
||||||
:as options :or {reset-touched? false
|
|
||||||
copy-touched? false}}]
|
|
||||||
(if (or (nil? (:shape-ref dest-shape))
|
(if (or (nil? (:shape-ref dest-shape))
|
||||||
(not (or reset-touched? copy-touched?)))
|
(not (or reset-touched? copy-touched?)))
|
||||||
empty-changes
|
empty-changes
|
||||||
|
@ -890,10 +990,15 @@
|
||||||
:operations
|
:operations
|
||||||
[{:type :set-touched
|
[{:type :set-touched
|
||||||
:touched
|
:touched
|
||||||
(cond reset-touched?
|
(cond
|
||||||
|
reset-touched?
|
||||||
nil
|
nil
|
||||||
copy-touched?
|
copy-touched?
|
||||||
(:touched orig-shape))}]} $
|
(if (:remote-synced? origin-shape)
|
||||||
|
nil
|
||||||
|
(set/union
|
||||||
|
(:touched dest-shape)
|
||||||
|
(:touched origin-shape))))}]} $
|
||||||
(if (cp/page? container)
|
(if (cp/page? container)
|
||||||
(assoc $ :page-id (:id container))
|
(assoc $ :page-id (:id container))
|
||||||
(assoc $ :component-id (:id container))))]
|
(assoc $ :component-id (:id container))))]
|
||||||
|
@ -908,6 +1013,34 @@
|
||||||
(assoc $ :component-id (:id container))))]]
|
(assoc $ :component-id (:id container))))]]
|
||||||
[rchanges uchanges]))))
|
[rchanges uchanges]))))
|
||||||
|
|
||||||
|
(defn- change-remote-synced
|
||||||
|
[shape container remote-synced?]
|
||||||
|
(if (nil? (:shape-ref shape))
|
||||||
|
empty-changes
|
||||||
|
(do
|
||||||
|
(log/info :msg (str "CHANGE-REMOTE-SYNCED? "
|
||||||
|
(if (cp/page? container) "[P] " "[C] ")
|
||||||
|
(:name shape))
|
||||||
|
:remote-synced? remote-synced?)
|
||||||
|
(let [rchanges [(as-> {:type :mod-obj
|
||||||
|
:id (:id shape)
|
||||||
|
:operations
|
||||||
|
[{:type :set-remote-synced
|
||||||
|
:remote-synced? remote-synced?}]} $
|
||||||
|
(if (cp/page? container)
|
||||||
|
(assoc $ :page-id (:id container))
|
||||||
|
(assoc $ :component-id (:id container))))]
|
||||||
|
|
||||||
|
uchanges [(as-> {:type :mod-obj
|
||||||
|
:id (:id shape)
|
||||||
|
:operations
|
||||||
|
[{:type :set-remote-synced
|
||||||
|
:remote-synced? (:remote-synced? shape)}]} $
|
||||||
|
(if (cp/page? container)
|
||||||
|
(assoc $ :page-id (:id container))
|
||||||
|
(assoc $ :component-id (:id container))))]]
|
||||||
|
[rchanges uchanges]))))
|
||||||
|
|
||||||
(defn- set-touched-shapes-group
|
(defn- set-touched-shapes-group
|
||||||
[shape container]
|
[shape container]
|
||||||
(if-not (:shape-ref shape)
|
(if-not (:shape-ref shape)
|
||||||
|
@ -938,22 +1071,12 @@
|
||||||
[rchanges uchanges]))))
|
[rchanges uchanges]))))
|
||||||
|
|
||||||
(defn- update-attrs
|
(defn- update-attrs
|
||||||
"The main function that implements the sync algorithm. Copy
|
"The main function that implements the attribute sync algorithm. Copy
|
||||||
attributes that have changed in the origin shape to the dest shape.
|
attributes that have changed in the origin shape to the dest shape.
|
||||||
|
|
||||||
If omit-touched? is true, attributes whose group has been touched
|
If omit-touched? is true, attributes whose group has been touched
|
||||||
in the destination shape will be ignored.
|
in the destination shape will not be copied."
|
||||||
If reset-touched? is true, the 'touched' flags will be cleared in
|
[dest-shape origin-shape dest-root origin-root container omit-touched?]
|
||||||
the dest shape.
|
|
||||||
If set-touched? is true, the corresponding 'touched' flags will be
|
|
||||||
set in dest shape if they are different than their current values.
|
|
||||||
If copy-touched? is true, the value of 'touched' flags in the
|
|
||||||
origin shape will be copied as is to the dest shape."
|
|
||||||
[dest-shape origin-shape dest-root origin-root container
|
|
||||||
{:keys [omit-touched? reset-touched? set-touched? copy-touched?]
|
|
||||||
:as options :or {omit-touched? false
|
|
||||||
reset-touched? false
|
|
||||||
set-touched? false
|
|
||||||
copy-touched? false}}]
|
|
||||||
|
|
||||||
(log/info :msg (str "SYNC "
|
(log/info :msg (str "SYNC "
|
||||||
(:name origin-shape)
|
(:name origin-shape)
|
||||||
|
@ -975,27 +1098,8 @@
|
||||||
|
|
||||||
(let [attr (first attrs)]
|
(let [attr (first attrs)]
|
||||||
(if (nil? attr)
|
(if (nil? attr)
|
||||||
(let [roperations (cond
|
(let [all-parents (vec (or (cp/get-parents (:id dest-shape)
|
||||||
reset-touched?
|
(:objects container)) []))
|
||||||
(conj roperations
|
|
||||||
{:type :set-touched
|
|
||||||
:touched nil})
|
|
||||||
copy-touched?
|
|
||||||
(conj roperations
|
|
||||||
{:type :set-touched
|
|
||||||
:touched (:touched origin-shape)})
|
|
||||||
:else
|
|
||||||
roperations)
|
|
||||||
|
|
||||||
uoperations (cond
|
|
||||||
(or reset-touched? copy-touched?)
|
|
||||||
(conj uoperations
|
|
||||||
{:type :set-touched
|
|
||||||
:touched (:touched dest-shape)})
|
|
||||||
:else
|
|
||||||
uoperations)
|
|
||||||
|
|
||||||
all-parents (or (cp/get-parents dest-shape (:objects container)) [])
|
|
||||||
|
|
||||||
rchanges [(as-> {:type :mod-obj
|
rchanges [(as-> {:type :mod-obj
|
||||||
:id (:id dest-shape)
|
:id (:id dest-shape)
|
||||||
|
@ -1019,19 +1123,22 @@
|
||||||
(if (cp/page? container)
|
(if (cp/page? container)
|
||||||
(assoc $ :page-id (:id container))
|
(assoc $ :page-id (:id container))
|
||||||
(assoc $ :component-id (:id container))))]]
|
(assoc $ :component-id (:id container))))]]
|
||||||
[rchanges uchanges])
|
(if (seq roperations)
|
||||||
|
[rchanges uchanges]
|
||||||
|
empty-changes))
|
||||||
|
|
||||||
(let [roperation {:type :set
|
(let [roperation {:type :set
|
||||||
:attr attr
|
:attr attr
|
||||||
:val (get origin-shape attr)
|
:val (get origin-shape attr)
|
||||||
:ignore-touched (not set-touched?)}
|
:ignore-touched true}
|
||||||
uoperation {:type :set
|
uoperation {:type :set
|
||||||
:attr attr
|
:attr attr
|
||||||
:val (get dest-shape attr)
|
:val (get dest-shape attr)
|
||||||
:ignore-touched (not set-touched?)}
|
:ignore-touched true}
|
||||||
|
|
||||||
attr-group (get cp/component-sync-attrs attr)]
|
attr-group (get cp/component-sync-attrs attr)]
|
||||||
(if (and (touched attr-group) omit-touched?)
|
(if (or (= (get origin-shape attr) (get dest-shape attr))
|
||||||
|
(and (touched attr-group) omit-touched?))
|
||||||
(recur (next attrs)
|
(recur (next attrs)
|
||||||
roperations
|
roperations
|
||||||
uoperations)
|
uoperations)
|
||||||
|
|
|
@ -94,10 +94,14 @@
|
||||||
{:length 20
|
{:length 20
|
||||||
:type :right})
|
:type :right})
|
||||||
(show-component shape objects))
|
(show-component shape objects))
|
||||||
(when (and show-touched (seq (:touched shape)))
|
(when show-touched
|
||||||
|
(when (seq (:touched shape))
|
||||||
(println (str (str/repeat " " level)
|
(println (str (str/repeat " " level)
|
||||||
" "
|
" "
|
||||||
(str (:touched shape)))))
|
(str (:touched shape)))))
|
||||||
|
(when (:remote-synced? shape)
|
||||||
|
(println (str (str/repeat " " level)
|
||||||
|
" (remote-synced)"))))
|
||||||
(when (:shapes shape)
|
(when (:shapes shape)
|
||||||
(dorun (for [shape-id (:shapes shape)]
|
(dorun (for [shape-id (:shapes shape)]
|
||||||
(show-shape shape-id (inc level) objects))))))
|
(show-shape shape-id (inc level) objects))))))
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue