Rework changes detection

This commit is contained in:
Andrés Moya 2021-01-13 15:11:47 +01:00 committed by Alonso Torres
parent 43b1d3ca43
commit fe7faf0d0d
5 changed files with 296 additions and 155 deletions

View file

@ -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

View file

@ -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?)

View file

@ -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)}

View file

@ -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)

View file

@ -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))))))