♻️ Always set component-file-id, even in local file

This commit is contained in:
Andrés Moya 2020-12-04 15:31:03 +01:00 committed by Andrey Antukh
parent 53b5d78cdc
commit baec7838b4
24 changed files with 284 additions and 258 deletions

View file

@ -112,7 +112,7 @@
(create-file [conn owner-id project-id index] (create-file [conn owner-id project-id index]
(let [id (mk-uuid "file" project-id index) (let [id (mk-uuid "file" project-id index)
name (str "file" index) name (str "file" index)
data (cp/make-file-data)] data (cp/make-file-data id)]
(log/info "create file" id) (log/info "create file" id)
(db/insert! conn :file (db/insert! conn :file
{:id id {:id id
@ -186,7 +186,7 @@
id (mk-uuid "file" "draft" owner-id index) id (mk-uuid "file" "draft" owner-id index)
name (str "file" index) name (str "file" index)
project-id (:default-project-id owner) project-id (:default-project-id owner)
data (cp/make-file-data)] data (cp/make-file-data id)]
(log/info "create draft file" id) (log/info "create draft file" id)
(db/insert! conn :file (db/insert! conn :file

View file

@ -63,7 +63,7 @@
:or {is-shared false} :or {is-shared false}
:as params}] :as params}]
(let [id (or id (uuid/next)) (let [id (or id (uuid/next))
data (cp/make-file-data) data (cp/make-file-data id)
file (db/insert! conn :file file (db/insert! conn :file
{:id id {:id id
:project-id project-id :project-id project-id
@ -276,6 +276,7 @@
changes (:changes params) changes (:changes params)
file (-> file file (-> file
(update :data blob/decode) (update :data blob/decode)
(update :data assoc :id (:id file))
(update :data pmg/migrate-data) (update :data pmg/migrate-data)
(update :data cp/process-changes changes) (update :data cp/process-changes changes)
(update :data blob/encode) (update :data blob/encode)

View file

@ -67,6 +67,7 @@
(db/with-atomic [conn db/pool] (db/with-atomic [conn db/pool]
(let [mobjs (map :id (db/query conn :media-object {:file-id id})) (let [mobjs (map :id (db/query conn :media-object {:file-id id}))
data (-> (blob/decode data) data (-> (blob/decode data)
(assoc :id id)
(pmg/migrate-data)) (pmg/migrate-data))
used (collect-used-media data) used (collect-used-media data)

View file

@ -18,8 +18,9 @@
[app.tests.helpers :as th])) [app.tests.helpers :as th]))
(t/deftest process-change-set-option (t/deftest process-change-set-option
(let [page-id (uuid/custom 1 1) (let [file-id (uuid/custom 2 2)
data (cp/make-file-data page-id)] page-id (uuid/custom 1 1)
data (cp/make-file-data file-id page-id)]
(t/testing "Sets option single" (t/testing "Sets option single"
(let [chg {:type :set-option (let [chg {:type :set-option
:page-id page-id :page-id page-id
@ -83,8 +84,9 @@
)) ))
(t/deftest process-change-add-obj (t/deftest process-change-add-obj
(let [page-id (uuid/custom 1 1) (let [file-id (uuid/custom 2 2)
data (cp/make-file-data page-id) page-id (uuid/custom 1 1)
data (cp/make-file-data file-id page-id)
id-a (uuid/custom 2 1) id-a (uuid/custom 2 1)
id-b (uuid/custom 2 2) id-b (uuid/custom 2 2)
id-c (uuid/custom 2 3)] id-c (uuid/custom 2 3)]
@ -136,8 +138,9 @@
)) ))
(t/deftest process-change-mod-obj (t/deftest process-change-mod-obj
(let [page-id (uuid/custom 1 1) (let [file-id (uuid/custom 2 2)
data (cp/make-file-data page-id)] page-id (uuid/custom 1 1)
data (cp/make-file-data file-id page-id)]
(t/testing "simple mod-obj" (t/testing "simple mod-obj"
(let [chg {:type :mod-obj (let [chg {:type :mod-obj
:page-id page-id :page-id page-id
@ -161,9 +164,10 @@
(t/deftest process-change-del-obj (t/deftest process-change-del-obj
(let [page-id (uuid/custom 1 1) (let [file-id (uuid/custom 2 2)
page-id (uuid/custom 1 1)
id (uuid/custom 2 1) id (uuid/custom 2 1)
data (cp/make-file-data page-id) data (cp/make-file-data file-id page-id)
data (-> data data (-> data
(assoc-in [:pages-index page-id :objects uuid/zero :shapes] [id]) (assoc-in [:pages-index page-id :objects uuid/zero :shapes] [id])
(assoc-in [:pages-index page-id :objects id] (assoc-in [:pages-index page-id :objects id]
@ -205,8 +209,9 @@
rect-d-id (uuid/custom 0 8) rect-d-id (uuid/custom 0 8)
rect-e-id (uuid/custom 0 9) rect-e-id (uuid/custom 0 9)
file-id (uuid/custom 2 2)
page-id (uuid/custom 1 1) page-id (uuid/custom 1 1)
data (cp/make-file-data page-id) data (cp/make-file-data file-id page-id)
data (update-in data [:pages-index page-id :objects] data (update-in data [:pages-index page-id :objects]
#(-> % #(-> %
@ -417,6 +422,7 @@
shape-2-id (uuid/custom 2 2) shape-2-id (uuid/custom 2 2)
shape-3-id (uuid/custom 2 3) shape-3-id (uuid/custom 2 3)
frame-id (uuid/custom 1 1) frame-id (uuid/custom 1 1)
file-id (uuid/custom 4 4)
page-id (uuid/custom 0 1) page-id (uuid/custom 0 1)
changes [{:type :add-obj changes [{:type :add-obj
@ -449,7 +455,7 @@
:obj {:type :rect :obj {:type :rect
:name "Shape 3"}} :name "Shape 3"}}
] ]
data (cp/make-file-data page-id) data (cp/make-file-data file-id page-id)
data (cp/process-changes data changes)] data (cp/process-changes data changes)]
(t/testing "preserve order on multiple shape mov 1" (t/testing "preserve order on multiple shape mov 1"
@ -513,6 +519,7 @@
shape-3-id (uuid/custom 1 3) shape-3-id (uuid/custom 1 3)
shape-4-id (uuid/custom 1 4) shape-4-id (uuid/custom 1 4)
group-1-id (uuid/custom 1 5) group-1-id (uuid/custom 1 5)
file-id (uuid/custom 1 6)
page-id (uuid/custom 0 1) page-id (uuid/custom 0 1)
changes [{:type :add-obj changes [{:type :add-obj
@ -555,7 +562,7 @@
:parent-id group-1-id :parent-id group-1-id
:shapes [shape-1-id shape-2-id]}] :shapes [shape-1-id shape-2-id]}]
data (cp/make-file-data page-id) data (cp/make-file-data file-id page-id)
data (cp/process-changes data changes)] data (cp/process-changes data changes)]
(t/testing "case 1" (t/testing "case 1"

View file

@ -80,3 +80,4 @@
(s/def ::recent-color ::spec/recent-color) (s/def ::recent-color ::spec/recent-color)
(s/def ::shape-attrs ::spec/shape-attrs) (s/def ::shape-attrs ::spec/shape-attrs)
(s/def ::typography ::spec/typography) (s/def ::typography ::spec/typography)

View file

@ -11,7 +11,7 @@
(:require (:require
[app.common.uuid :as uuid])) [app.common.uuid :as uuid]))
(def file-version 4) (def file-version 5)
(def default-color "#b1b2b5") ;; $color-gray-20 (def default-color "#b1b2b5") ;; $color-gray-20
(def root uuid/zero) (def root uuid/zero)

View file

@ -58,6 +58,7 @@
(defn get-container (defn get-container
[id type local-file] [id type local-file]
(assert (some? type))
(-> (if (= type :page) (-> (if (= type :page)
(get-in local-file [:pages-index id]) (get-in local-file [:pages-index id])
(get-in local-file [:components id])) (get-in local-file [:components id]))
@ -68,10 +69,11 @@
(get-in container [:objects shape-id])) (get-in container [:objects shape-id]))
(defn get-component (defn get-component
[component-id file-id local-library libraries] [component-id library-id local-library libraries]
(let [file (if (nil? file-id) (assert (some? (:id local-library)))
(let [file (if (= library-id (:id local-library))
local-library local-library
(get-in libraries [file-id :data]))] (get-in libraries [library-id :data]))]
(get-in file [:components component-id]))) (get-in file [:components component-id])))
(defn is-master-of (defn is-master-of

View file

@ -132,12 +132,13 @@
:height (:height selection-rect)}) :height (:height selection-rect)})
(defn make-file-data (defn make-file-data
([] (make-file-data (uuid/next))) ([file-id] (make-file-data file-id(uuid/next)))
([id] ([file-id page-id]
(let [ (let [
pd (assoc empty-page-data pd (assoc empty-page-data
:id id :id page-id
:name "Page-1")] :name "Page-1")]
(-> empty-file-data (-> empty-file-data
(update :pages conj id) (assoc :id file-id)
(update :pages-index assoc id pd))))) (update :pages conj page-id)
(update :pages-index assoc page-id pd)))))

View file

@ -35,7 +35,9 @@
(defn migrate-file (defn migrate-file
[file] [file]
(update file :data migrate-data)) (-> file
(update :data assoc :id (:id file))
(update :data migrate-data)))
;; Default handler, noop ;; Default handler, noop
(defmethod migrate :default [data] data) (defmethod migrate :default [data] data)
@ -120,3 +122,18 @@
;; We did rollback version 4 migration. ;; We did rollback version 4 migration.
;; Keep this in order to remember the next version to be 5 ;; Keep this in order to remember the next version to be 5
(defmethod migrate 4 [data] data) (defmethod migrate 4 [data] data)
;; Put the id of the local file in :component-file in instances of local components
(defmethod migrate 5
[data]
(letfn [(update-object [_ object]
(if (and (some? (:component-id object))
(nil? (:component-file object)))
(assoc object :component-file (:id data))
object))
(update-page [_ page]
(update page :objects #(d/mapm update-object %)))]
(update data :pages-index #(d/mapm update-page %))))

View file

@ -69,7 +69,7 @@
(def platform (parse-platform)) (def platform (parse-platform))
(js/console.log (js/console.log
(str/format "Welcome to pentpot! Version: '%s'" (:full version))) (str/format "Welcome to penpot! Version: '%s'" (:full version)))
;; --- Helper Functions ;; --- Helper Functions

View file

@ -119,11 +119,11 @@
text-ids (filter is-text? ids) text-ids (filter is-text? ids)
shape-ids (filter (comp not is-text?) ids) shape-ids (filter (comp not is-text?) ids)
attrs (cond-> {:fill-color (:color color) attrs {:fill-color (:color color)
:fill-color-ref-id (:id color) :fill-color-ref-id (:id color)
:fill-color-ref-file (:file-id color) :fill-color-ref-file (:file-id color)
:fill-color-gradient (:gradient color) :fill-color-gradient (:gradient color)
:fill-opacity (:opacity color)}) :fill-opacity (:opacity color)}
update-fn (fn [shape] (merge shape attrs)) update-fn (fn [shape] (merge shape attrs))
editors (get-in state [:workspace-local :editors]) editors (get-in state [:workspace-local :editors])

View file

@ -9,18 +9,22 @@
(ns app.main.data.workspace.common (ns app.main.data.workspace.common
(:require (:require
[beicon.core :as rx]
[cljs.spec.alpha :as s]
[clojure.set :as set]
[potok.core :as ptk]
[app.common.data :as d] [app.common.data :as d]
[app.common.geom.proportions :as gpr]
[app.common.geom.shapes :as gsh]
[app.common.pages :as cp] [app.common.pages :as cp]
[app.common.spec :as us] [app.common.spec :as us]
[app.common.uuid :as uuid] [app.common.uuid :as uuid]
[app.main.worker :as uw] [app.main.worker :as uw]
[app.util.logging :as log]
[app.util.timers :as ts] [app.util.timers :as ts]
[app.common.geom.proportions :as gpr] [beicon.core :as rx]
[app.common.geom.shapes :as gsh])) [cljs.spec.alpha :as s]
[clojure.set :as set]
[potok.core :as ptk]))
;; Change this to :info :debug or :trace to debug this module
(log/set-level! :warn)
(s/def ::shape-attrs ::cp/shape-attrs) (s/def ::shape-attrs ::cp/shape-attrs)
(s/def ::set-of-string (s/every string? :kind set?)) (s/def ::set-of-string (s/every string? :kind set?))
@ -66,6 +70,9 @@
:as opts}] :as opts}]
(us/verify ::cp/changes changes) (us/verify ::cp/changes changes)
;; (us/verify ::cp/changes undo-changes) ;; (us/verify ::cp/changes undo-changes)
(log/debug :msg "commit-changes"
:js/changes changes
:js/undo-changes undo-changes)
(let [error (volatile! nil)] (let [error (volatile! nil)]
(ptk/reify ::commit-changes (ptk/reify ::commit-changes

View file

@ -76,8 +76,9 @@
(assoc-in state [:workspace-local :color-for-rename] nil)))) (assoc-in state [:workspace-local :color-for-rename] nil))))
(defn update-color (defn update-color
[{:keys [id] :as color}] [{:keys [id] :as color} file-id]
(us/assert ::cp/color color) (us/assert ::cp/color color)
(us/assert ::us/uuid file-id)
(ptk/reify ::update-color (ptk/reify ::update-color
ptk/WatchEvent ptk/WatchEvent
(watch [_ state stream] (watch [_ state stream]
@ -87,7 +88,7 @@
uchg {:type :mod-color uchg {:type :mod-color
:color prev}] :color prev}]
(rx/of (dwc/commit-changes [rchg] [uchg] {:commit-local? true}) (rx/of (dwc/commit-changes [rchg] [uchg] {:commit-local? true})
(sync-file nil)))))) (sync-file file-id))))))
(defn delete-color (defn delete-color
[{:keys [id] :as params}] [{:keys [id] :as params}]
@ -164,9 +165,9 @@
(assoc-in [:workspace-local :rename-typography] (:id typography)))))))))) (assoc-in [:workspace-local :rename-typography] (:id typography))))))))))
(defn update-typography (defn update-typography
[typography] [typography file-id]
(us/assert ::cp/typography typography) (us/assert ::cp/typography typography)
(us/assert ::us/uuid file-id)
(ptk/reify ::update-typography (ptk/reify ::update-typography
ptk/WatchEvent ptk/WatchEvent
(watch [_ state stream] (watch [_ state stream]
@ -176,7 +177,7 @@
uchg {:type :mod-typography uchg {:type :mod-typography
:typography prev}] :typography prev}]
(rx/of (dwc/commit-changes [rchg] [uchg] {:commit-local? true}) (rx/of (dwc/commit-changes [rchg] [uchg] {:commit-local? true})
(sync-file nil)))))) (sync-file file-id))))))
(defn delete-typography (defn delete-typography
[id] [id]
@ -196,7 +197,8 @@
(ptk/reify ::add-component (ptk/reify ::add-component
ptk/WatchEvent ptk/WatchEvent
(watch [_ state stream] (watch [_ state stream]
(let [page-id (:current-page-id state) (let [file-id (:current-file-id state)
page-id (:current-page-id state)
objects (dwc/lookup-page-objects state page-id) objects (dwc/lookup-page-objects state page-id)
selected (get-in state [:workspace-local :selected]) selected (get-in state [:workspace-local :selected])
shapes (dwg/shapes-for-grouping objects selected)] shapes (dwg/shapes-for-grouping objects selected)]
@ -210,7 +212,7 @@
(dwg/prepare-create-group page-id shapes "Component-" true)) (dwg/prepare-create-group page-id shapes "Component-" true))
[new-shape new-shapes updated-shapes] [new-shape new-shapes updated-shapes]
(dwlh/make-component-shape group objects) (dwlh/make-component-shape group objects file-id)
rchanges (conj rchanges rchanges (conj rchanges
{:type :add-component {:type :add-component
@ -303,8 +305,8 @@
ptk/WatchEvent ptk/WatchEvent
(watch [_ state stream] (watch [_ state stream]
(let [component (cp/get-component id (let [component (cp/get-component id
nil (:current-file-id state)
(get state :workspace-data) (dwlh/get-local-library state)
nil) nil)
all-components (vals (get-in state [:workspace-data :components])) all-components (vals (get-in state [:workspace-data :components]))
unames (set (map :name all-components)) unames (set (map :name all-components))
@ -344,18 +346,18 @@
(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
in the given file library (if file-id is nil, take it from the current file library)." in the given file library / current file library."
[file-id component-id position] [file-id component-id position]
(us/assert (s/nilable ::us/uuid) file-id) (us/assert ::us/uuid file-id)
(us/assert ::us/uuid component-id) (us/assert ::us/uuid component-id)
(us/assert ::us/point position) (us/assert ::us/point position)
(ptk/reify ::instantiate-component (ptk/reify ::instantiate-component
ptk/WatchEvent ptk/WatchEvent
(watch [_ state stream] (watch [_ state stream]
(let [component (if (nil? file-id) (let [local-library (dwlh/get-local-library state)
(get-in state [:workspace-data :components component-id]) libraries (get state :workspace-libraries)
(get-in state [:workspace-libraries file-id :data :components component-id])) component (cp/get-component component-id file-id local-library libraries)
component-shape (get-in component [:objects (:id component)]) component-shape (cp/get-shape component component-id)
orig-pos (gpt/point (:x component-shape) (:y component-shape)) orig-pos (gpt/point (:x component-shape) (:y component-shape))
delta (gpt/subtract position orig-pos) delta (gpt/subtract position orig-pos)
@ -387,19 +389,9 @@
(nil? (:parent-id original-shape)) (nil? (:parent-id original-shape))
(assoc :component-id (:id original-shape) (assoc :component-id (:id original-shape)
:component-file file-id
:component-root? true) :component-root? true)
(and (nil? (:parent-id original-shape)) (some? file-id))
(assoc :component-file file-id)
(and (nil? (:parent-id original-shape)) (nil? file-id))
(dissoc :component-file)
(and (some? (:component-id original-shape))
(nil? (:component-file original-shape))
(some? file-id))
(assoc :component-file file-id)
(some? (:parent-id original-shape)) (some? (:parent-id original-shape))
(dissoc :component-root?)))) (dissoc :component-root?))))
@ -514,15 +506,15 @@
ptk/WatchEvent ptk/WatchEvent
(watch [_ state stream] (watch [_ state stream]
(log/info :msg "RESET-COMPONENT of shape" :id (str id)) (log/info :msg "RESET-COMPONENT of shape" :id (str id))
(let [local-file (get state :workspace-data) (let [local-library (dwlh/get-local-library state)
libraries (get state :workspace-libraries) libraries (dwlh/get-libraries state)
container (cp/get-container (get state :current-page-id) container (cp/get-container (get state :current-page-id)
:page :page
local-file) local-library)
[rchanges uchanges] [rchanges uchanges]
(dwlh/generate-sync-shape-direct container (dwlh/generate-sync-shape-direct container
id id
local-file local-library
libraries libraries
true)] true)]
(log/debug :msg "RESET-COMPONENT finished" :js/rchanges rchanges) (log/debug :msg "RESET-COMPONENT finished" :js/rchanges rchanges)
@ -539,16 +531,11 @@
ptk/WatchEvent ptk/WatchEvent
(watch [_ state stream] (watch [_ state stream]
(log/info :msg "UPDATE-COMPONENT of shape" :id (str id)) (log/info :msg "UPDATE-COMPONENT of shape" :id (str id))
(let [page-id (:current-page-id state) (let [[rchanges uchanges]
objects (dwc/lookup-page-objects state page-id)
shape (get objects id)
file-id (get shape :component-file)
[rchanges uchanges]
(dwlh/generate-sync-shape-inverse (get state :current-page-id) (dwlh/generate-sync-shape-inverse (get state :current-page-id)
id id
(get state :workspace-data) (dwlh/get-local-library state)
(get state :workspace-libraries))] (dwlh/get-libraries state))]
(log/debug :msg "UPDATE-COMPONENT finished" :js/rchanges rchanges) (log/debug :msg "UPDATE-COMPONENT finished" :js/rchanges rchanges)
@ -562,17 +549,17 @@
component of the library file, and copy the new values to the shapes. component of the library file, and copy the new values to the shapes.
Do it also for shapes inside components of the local file library." Do it also for shapes inside components of the local file library."
[file-id] [file-id]
(us/assert (s/nilable ::us/uuid) file-id) (us/assert ::us/uuid file-id)
(ptk/reify ::sync-file (ptk/reify ::sync-file
ptk/UpdateEvent ptk/UpdateEvent
(update [_ state] (update [_ state]
(if file-id (if (not= file-id (:current-file-id state))
(assoc-in state [:workspace-libraries file-id :synced-at] (dt/now)) (assoc-in state [:workspace-libraries file-id :synced-at] (dt/now))
state)) state))
ptk/WatchEvent ptk/WatchEvent
(watch [_ state stream] (watch [_ state stream]
(log/info :msg "SYNC-FILE" :file (str (or file-id "local"))) (log/info :msg "SYNC-FILE" :file (if (= file-id (:current-file-id state)) "local" (str file-id)))
(let [library-changes [(dwlh/generate-sync-library :components file-id state) (let [library-changes [(dwlh/generate-sync-library :components file-id state)
(dwlh/generate-sync-library :colors file-id state) (dwlh/generate-sync-library :colors file-id state)
(dwlh/generate-sync-library :typographies file-id state)] (dwlh/generate-sync-library :typographies file-id state)]
@ -590,7 +577,7 @@
(rx/of (dm/hide-tag :sync-dialog)) (rx/of (dm/hide-tag :sync-dialog))
(when rchanges (when rchanges
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true}))) (rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true})))
(when file-id (when (not= file-id (:current-file-id state))
(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}))
@ -607,12 +594,12 @@
;; implement updated-at at component level, to detect what components have ;; implement updated-at at component level, to detect what components have
;; not changed, and then not to apply sync and terminate the loop. ;; not changed, and then not to apply sync and terminate the loop.
[file-id] [file-id]
(us/assert (s/nilable ::us/uuid) file-id) (us/assert ::us/uuid file-id)
(ptk/reify ::sync-file-2nd-stage (ptk/reify ::sync-file-2nd-stage
ptk/WatchEvent ptk/WatchEvent
(watch [_ state stream] (watch [_ state stream]
(log/info :msg "SYNC-FILE (2nd stage)" :file (str (or file-id "local"))) (log/info :msg "SYNC-FILE (2nd stage)" :file (if (= file-id (:current-file-id state)) "local" (str file-id)))
(let [[rchanges1 uchanges1] (dwlh/generate-sync-file :components nil state) (let [[rchanges1 uchanges1] (dwlh/generate-sync-file :components file-id state)
[rchanges2 uchanges2] (dwlh/generate-sync-library :components file-id state) [rchanges2 uchanges2] (dwlh/generate-sync-library :components file-id state)
rchanges (d/concat rchanges1 rchanges2) rchanges (d/concat rchanges1 rchanges2)
uchanges (d/concat uchanges1 uchanges2)] uchanges (d/concat uchanges1 uchanges2)]
@ -621,7 +608,7 @@
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true}))))))) (rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true})))))))
(def ignore-sync (def ignore-sync
(ptk/reify ::sync-file (ptk/reify ::ignore-sync
ptk/UpdateEvent ptk/UpdateEvent
(update [_ state] (update [_ state]
(assoc-in state [:workspace-file :ignore-sync-until] (dt/now))) (assoc-in state [:workspace-file :ignore-sync-until] (dt/now)))

View file

@ -12,7 +12,6 @@
[cljs.spec.alpha :as s] [cljs.spec.alpha :as s]
[app.common.spec :as us] [app.common.spec :as us]
[app.common.data :as d] [app.common.data :as d]
[app.common.pages :as cph] ;; TODO: remove this namespace
[app.common.geom.point :as gpt] [app.common.geom.point :as gpt]
[app.common.geom.shapes :as geom] [app.common.geom.shapes :as geom]
[app.common.pages :as cp] [app.common.pages :as cp]
@ -44,7 +43,6 @@
(declare generate-sync-shape-inverse-recursive) (declare generate-sync-shape-inverse-recursive)
(declare compare-children) (declare compare-children)
(declare concat-changes)
(declare add-shape-to-instance) (declare add-shape-to-instance)
(declare add-shape-to-master) (declare add-shape-to-master)
(declare remove-shape) (declare remove-shape)
@ -53,6 +51,19 @@
(declare update-attrs) (declare update-attrs)
(declare reposition-shape) (declare reposition-shape)
(defn concat-changes
[[rchanges1 uchanges1] [rchanges2 uchanges2]]
[(d/concat rchanges1 rchanges2)
(d/concat uchanges1 uchanges2)])
(defn get-local-library
[state]
(get state :workspace-data))
(defn get-libraries
[state]
(get state :workspace-libraries))
;; ---- Create a new component ---- ;; ---- Create a new component ----
@ -60,7 +71,7 @@
"Clone the shape and all children. Generate new ids and detach "Clone the shape and all children. Generate new ids and detach
from parent and frame. Update the original shapes to have links from parent and frame. Update the original shapes to have links
to the new ones." to the new ones."
[shape objects] [shape objects file-id]
(assert (nil? (:component-id shape))) (assert (nil? (:component-id shape)))
(assert (nil? (:component-file shape))) (assert (nil? (:component-file shape)))
(assert (nil? (:shape-ref shape))) (assert (nil? (:shape-ref shape)))
@ -88,23 +99,23 @@
(nil? (:parent-id new-shape)) (nil? (:parent-id new-shape))
(assoc :component-id (:id new-shape) (assoc :component-id (:id new-shape)
:component-file nil :component-file file-id
:component-root? true) :component-root? true)
(some? (:parent-id new-shape)) (some? (:parent-id new-shape))
(dissoc :component-root?)))] (dissoc :component-root?)))]
(cph/clone-object shape nil objects update-new-shape update-original-shape))) (cp/clone-object shape nil objects update-new-shape update-original-shape)))
(defn duplicate-component (defn duplicate-component
"Clone the root shape of the component and all children. Generate new "Clone the root shape of the component and all children. Generate new
ids from all of them." ids from all of them."
[component] [component]
(let [component-root (cph/get-component-root component)] (let [component-root (cp/get-component-root component)]
(cph/clone-object component-root (cp/clone-object component-root
nil nil
(get component :objects) (get component :objects)
identity))) identity)))
;; ---- General library synchronization functions ---- ;; ---- General library synchronization functions ----
@ -114,14 +125,14 @@
with the given asset of the given library." 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)
(s/assert (s/nilable ::us/uuid) library-id) (s/assert ::us/uuid library-id)
(log/info :msg "Sync local file with library" (log/info :msg "Sync local file with library"
:asset-type asset-type :asset-type asset-type
:library (str (or library-id "local"))) :library (str (or library-id "local")))
(let [library-items (let [library-items
(if (nil? library-id) (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]))]
@ -136,7 +147,7 @@
(generate-sync-container asset-type (generate-sync-container asset-type
library-id library-id
state state
(cph/make-container page :page))] (cp/make-container page :page))]
(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)))
@ -149,10 +160,10 @@
(log/info :msg "Sync local components with library" (log/info :msg "Sync local components with library"
:asset-type asset-type :asset-type asset-type
:library (str (or library-id "local"))) :library (str library-id))
(let [library-items (let [library-items
(if (nil? library-id) (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]))]
(if (empty? library-items) (if (empty? library-items)
@ -166,8 +177,8 @@
(generate-sync-container asset-type (generate-sync-container asset-type
library-id library-id
state state
(cph/make-container local-component (cp/make-container local-component
:component))] :component))]
(recur (next local-components) (recur (next local-components)
(d/concat rchanges comp-rchanges) (d/concat rchanges comp-rchanges)
(d/concat uchanges comp-uchanges))) (d/concat uchanges comp-uchanges)))
@ -178,12 +189,12 @@
(a page or a component) that are linked to the given library." (a page or a component) that are linked to the given library."
[asset-type library-id state container] [asset-type library-id state container]
(if (cph/page? container) (if (cp/page? container)
(log/debug :msg "Sync page in local file" :page-id (:id container)) (log/debug :msg "Sync page in local file" :page-id (:id container))
(log/debug :msg "Sync component in local library" :component-id (:id container))) (log/debug :msg "Sync component in local library" :component-id (:id container)))
(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? container)] linked-shapes (cp/select-objects has-asset-reference? container)]
(loop [shapes (seq linked-shapes) (loop [shapes (seq linked-shapes)
rchanges [] rchanges []
uchanges []] uchanges []]
@ -209,22 +220,23 @@
(= (:component-file shape) library-id))) (= (:component-file shape) library-id)))
:colors :colors
(fn [shape] (if (= (:type shape) :text) (fn [shape]
(->> shape (if (= (:type shape) :text)
:content (->> shape
;; Check if any node in the content has a reference for the library :content
(ut/some-node ;; Check if any node in the content has a reference for the library
#(or (and (some? (:stroke-color-ref-id %)) (ut/some-node
(= library-id (:stroke-color-ref-file %))) #(or (and (some? (:stroke-color-ref-id %))
(and (some? (:fill-color-ref-id %)) (= library-id (:stroke-color-ref-file %)))
(= library-id (:fill-color-ref-file %)))))) (and (some? (:fill-color-ref-id %))
(some (= library-id (:fill-color-ref-file %))))))
#(let [attr (name %) (some
attr-ref-id (keyword (str attr "-ref-id")) #(let [attr (name %)
attr-ref-file (keyword (str attr "-ref-file"))] attr-ref-id (keyword (str attr "-ref-id"))
(and (get shape attr-ref-id) attr-ref-file (keyword (str attr "-ref-file"))]
(= library-id (get shape attr-ref-file)))) (and (get shape attr-ref-id)
(map #(nth % 2) color-sync-attrs)))) (= library-id (get shape attr-ref-file))))
(map #(nth % 2) color-sync-attrs))))
:typographies :typographies
(fn [shape] (fn [shape]
@ -233,8 +245,8 @@
:content :content
;; Check if any node in the content has a reference for the library ;; Check if any node in the content has a reference for the library
(ut/some-node (ut/some-node
#(and (some? (:typography-ref-id %)) #(and (some? (:typography-ref-id %))
(= 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, that use the given type
@ -245,11 +257,12 @@
[_ library-id state container shape] [_ library-id state container shape]
(generate-sync-shape-direct container (generate-sync-shape-direct container
(:id shape) (:id shape)
(get state :workspace-data) (get-local-library state)
(get state :workspace-libraries) (get-libraries state)
false)) false))
(defn- generate-sync-text-shape [shape container update-node] (defn- generate-sync-text-shape
[shape container update-node]
(let [old-content (:content shape) (let [old-content (:content shape)
new-content (ut/map-node update-node old-content) new-content (ut/map-node update-node old-content)
rchanges [(as-> {:type :mod-obj rchanges [(as-> {:type :mod-obj
@ -257,7 +270,7 @@
:operations [{:type :set :operations [{:type :set
:attr :content :attr :content
:val new-content}]} $ :val new-content}]} $
(if (cph/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))))]
uchanges [(as-> {:type :mod-obj uchanges [(as-> {:type :mod-obj
@ -265,7 +278,7 @@
:operations [{:type :set :operations [{:type :set
:attr :content :attr :content
:val old-content}]} $ :val old-content}]} $
(if (cph/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))))]]
@ -275,10 +288,11 @@
(defmethod generate-sync-shape :colors (defmethod generate-sync-shape :colors
[_ library-id state container shape] [_ library-id state container shape]
(log/debug :msg "Sync colors of shape" :shape (:name shape))
;; Synchronize a shape that uses some colors of the library. The value of the ;; Synchronize a shape that uses some colors of the library. The value of the
;; color in the library is copied to the shape. ;; color in the library is copied to the shape.
(let [colors (get-assets library-id :colors state)] (let [colors (get-assets library-id :colors state)]
(if (= :text (:type shape)) (if (= :text (:type shape))
(let [update-node (fn [node] (let [update-node (fn [node]
(if-let [color (get colors (:fill-color-ref-id node))] (if-let [color (get colors (:fill-color-ref-id node))]
@ -298,13 +312,13 @@
(let [rchanges [(as-> {:type :mod-obj (let [rchanges [(as-> {:type :mod-obj
:id (:id shape) :id (:id shape)
:operations roperations} $ :operations roperations} $
(if (cph/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))))]
uchanges [(as-> {:type :mod-obj uchanges [(as-> {:type :mod-obj
:id (:id shape) :id (:id shape)
:operations uoperations} $ :operations uoperations} $
(if (cph/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])) [rchanges uchanges]))
@ -327,6 +341,7 @@
(defmethod generate-sync-shape :typographies (defmethod generate-sync-shape :typographies
[_ library-id state container shape] [_ library-id state container shape]
(log/debug :msg "Sync typographies of shape" :shape (:name shape))
;; Synchronize a shape that uses some typographies of the library. The attributes ;; Synchronize a shape that uses some typographies of the library. The attributes
;; of the typography are copied to the shape." ;; of the typography are copied to the shape."
@ -342,7 +357,7 @@
(defn- get-assets (defn- get-assets
[library-id asset-type state] [library-id asset-type state]
(if (nil? library-id) (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])))
@ -354,17 +369,17 @@
be copied to this one. be copied to this one.
If reset? is true, all changed attributes will be copied and the 'touched' If reset? is true, all changed attributes will be copied and the 'touched'
flags in the instance shape will be cleared." flags in the instance shape will be cleared."
[container shape-id local-file 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 (cph/get-shape container shape-id) (let [shape-inst (cp/get-shape container shape-id)
component (cph/get-component (:component-id shape-inst) component (cp/get-component (:component-id shape-inst)
(:component-file shape-inst) (:component-file shape-inst)
local-file local-library
libraries) libraries)
shape-master (cph/get-shape component (:shape-ref shape-inst)) shape-master (cp/get-shape component (:shape-ref shape-inst))
root-inst shape-inst root-inst shape-inst
root-master (cph/get-component-root component)] root-master (cp/get-component-root component)]
(generate-sync-shape-direct-recursive container (generate-sync-shape-direct-recursive container
shape-inst shape-inst
@ -407,9 +422,9 @@
container container
options)) options))
children-inst (mapv #(cph/get-shape container %) children-inst (mapv #(cp/get-shape container %)
(:shapes shape-inst)) (:shapes shape-inst))
children-master (mapv #(cph/get-shape component %) children-master (mapv #(cp/get-shape component %)
(:shapes shape-master)) (:shapes shape-master))
only-inst (fn [shape-inst] only-inst (fn [shape-inst]
@ -468,18 +483,18 @@
shapes. shapes.
And if the component shapes are, in turn, instances of a second component, And if the component shapes are, in turn, instances of a second component,
their 'touched' flags will be set accordingly." their 'touched' flags will be set accordingly."
[page-id shape-id local-file 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 (cph/get-container page-id :page local-file) (let [container (cp/get-container page-id :page local-library)
shape-inst (cph/get-shape container shape-id) shape-inst (cp/get-shape container shape-id)
component (cph/get-component (:component-id shape-inst) component (cp/get-component (:component-id shape-inst)
(:component-file shape-inst) (:component-file shape-inst)
local-file local-library
libraries) libraries)
shape-master (cph/get-shape component (:shape-ref shape-inst)) shape-master (cp/get-shape component (:shape-ref shape-inst))
root-inst shape-inst root-inst shape-inst
root-master (cph/get-component-root component)] root-master (cp/get-component-root component)]
(generate-sync-shape-inverse-recursive container (generate-sync-shape-inverse-recursive container
shape-inst shape-inst
@ -509,7 +524,7 @@
shape-master shape-master
root-master) root-master)
component-container (cph/make-container component :component) component-container (cp/make-container component :component)
[rchanges uchanges] [rchanges uchanges]
(concat-changes (concat-changes
@ -528,9 +543,9 @@
(change-touched shape-inst nil container {:reset-touched? true}) (change-touched shape-inst nil container {:reset-touched? true})
empty-changes))) empty-changes)))
children-inst (mapv #(cph/get-shape container %) children-inst (mapv #(cp/get-shape container %)
(:shapes shape-inst)) (:shapes shape-inst))
children-master (mapv #(cph/get-shape component %) children-master (mapv #(cp/get-shape component %)
(:shapes shape-master)) (:shapes shape-master))
only-inst (fn [shape-inst] only-inst (fn [shape-inst]
@ -544,7 +559,7 @@
(remove-shape shape-master (remove-shape shape-master
component-container component-container
false)) false))
both (fn [shape-inst shape-master] both (fn [shape-inst shape-master]
(let [options (if-not (:component-id shape-inst) (let [options (if-not (:component-id shape-inst)
options options
@ -607,15 +622,15 @@
children-inst) children-inst)
:else :else
(if (cph/is-master-of child-master child-inst) (if (cp/is-master-of child-master child-inst)
(recur (next children-inst) (recur (next children-inst)
(next children-master) (next children-master)
(concat-changes [rchanges uchanges] (concat-changes [rchanges uchanges]
(both-cb child-inst child-master))) (both-cb child-inst child-master)))
(let [child-inst' (d/seek #(cph/is-master-of child-master %) (let [child-inst' (d/seek #(cp/is-master-of child-master %)
children-inst) children-inst)
child-master' (d/seek #(cph/is-master-of % child-inst) child-master' (d/seek #(cp/is-master-of % child-inst)
children-master)] children-master)]
(cond (cond
(nil? child-inst') (nil? child-inst')
@ -643,20 +658,15 @@
(concat-changes (both-cb child-inst child-master')) (concat-changes (both-cb child-inst child-master'))
(concat-changes (moved-cb child-inst' child-master)))))))))))) (concat-changes (moved-cb child-inst' child-master))))))))))))
(defn concat-changes
[[rchanges1 uchanges1] [rchanges2 uchanges2]]
[(d/concat rchanges1 rchanges2)
(d/concat uchanges1 uchanges2)])
(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?]
(log/info :msg (str "ADD [P] " (:name component-shape))) (log/info :msg (str "ADD [P] " (:name component-shape)))
(let [component-parent-shape (cph/get-shape component (:parent-id component-shape)) (let [component-parent-shape (cp/get-shape component (:parent-id component-shape))
parent-shape (d/seek #(cph/is-master-of component-parent-shape %) parent-shape (d/seek #(cp/is-master-of component-parent-shape %)
(cph/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)
(cph/get-parents parent-shape (:objects container)))) (cp/get-parents 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
@ -667,32 +677,17 @@
(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)))))
(some? (:shape-ref original-shape))
(assoc :shape-ref (:shape-ref original-shape))
(:component-id original-shape)
(assoc :component-id (:component-id original-shape))
(:component-file original-shape)
(assoc :component-file (:component-file original-shape))
(:component-root original-shape)
(assoc :component-root (:component-root original-shape))
(:touched original-shape)
(assoc :touched (:touched original-shape)))))
update-original-shape (fn [original-shape new-shape] update-original-shape (fn [original-shape new-shape]
original-shape) original-shape)
[new-shape new-shapes _] [new-shape new-shapes _]
(cph/clone-object component-shape (cp/clone-object component-shape
(:id parent-shape) (:id parent-shape)
(get component :objects) (get component :objects)
update-new-shape update-new-shape
update-original-shape) update-original-shape)
rchanges (d/concat rchanges (d/concat
(mapv (fn [shape'] (mapv (fn [shape']
@ -704,13 +699,13 @@
(cond-> $ (cond-> $
(:frame-id shape') (:frame-id shape')
(assoc :frame-id (:frame-id shape'))) (assoc :frame-id (:frame-id shape')))
(if (cph/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)))))
new-shapes) new-shapes)
[(as-> {:type :reg-objects [(as-> {:type :reg-objects
:shapes all-parents} $ :shapes all-parents} $
(if (cph/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))))])
@ -719,24 +714,24 @@
(as-> {:type :del-obj (as-> {:type :del-obj
:id (:id shape') :id (:id shape')
:ignore-touched true} $ :ignore-touched true} $
(if (cph/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)))))
new-shapes))] new-shapes))]
(if (and (cph/touched-group? parent-shape :shapes-group) omit-touched?) (if (and (cp/touched-group? parent-shape :shapes-group) omit-touched?)
empty-changes empty-changes
[rchanges uchanges]))) [rchanges uchanges])))
(defn- add-shape-to-master (defn- add-shape-to-master
[shape component page root-instance root-master] [shape component page root-instance root-master]
(log/info :msg (str "ADD [C] " (:name shape))) (log/info :msg (str "ADD [C] " (:name shape)))
(let [parent-shape (cph/get-shape page (:parent-id shape)) (let [parent-shape (cp/get-shape page (:parent-id shape))
component-parent-shape (d/seek #(cph/is-master-of % parent-shape) component-parent-shape (d/seek #(cp/is-master-of % parent-shape)
(cph/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)
(cph/get-parents component-parent-shape (:objects component)))) (cp/get-parents 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
@ -750,11 +745,11 @@
original-shape)) original-shape))
[new-shape new-shapes updated-shapes] [new-shape new-shapes updated-shapes]
(cph/clone-object shape (cp/clone-object shape
(:id component-parent-shape) (:id component-parent-shape)
(get page :objects) (get page :objects)
update-new-shape update-new-shape
update-original-shape) update-original-shape)
rchanges (d/concat rchanges (d/concat
(mapv (fn [shape'] (mapv (fn [shape']
@ -802,17 +797,17 @@
(defn- remove-shape (defn- remove-shape
[shape container omit-touched?] [shape container omit-touched?]
(log/info :msg (str "REMOVE-SHAPE " (log/info :msg (str "REMOVE-SHAPE "
(if (cph/page? container) "[P] " "[C] ") (if (cp/page? container) "[P] " "[C] ")
(:name shape))) (:name shape)))
(let [objects (get container :objects) (let [objects (get container :objects)
parents (cph/get-parents (:id shape) objects) parents (cp/get-parents (:id shape) objects)
parent (first parents) parent (first parents)
children (cph/get-children (:id shape) objects) children (cp/get-children (:id shape) objects)
rchanges [(as-> {:type :del-obj rchanges [(as-> {:type :del-obj
:id (:id shape) :id (:id shape)
:ignore-touched true} $ :ignore-touched true} $
(if (cph/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))))]
@ -820,14 +815,14 @@
(let [shape' (get objects id)] (let [shape' (get objects id)]
(as-> {:type :add-obj (as-> {:type :add-obj
:id id :id id
:index (cph/position-on-parent id objects) :index (cp/position-on-parent id objects)
:parent-id (:parent-id shape') :parent-id (:parent-id shape')
:ignore-touched true :ignore-touched true
:obj shape'} $ :obj shape'} $
(cond-> $ (cond-> $
(:frame-id shape') (:frame-id shape')
(assoc :frame-id (:frame-id shape'))) (assoc :frame-id (:frame-id shape')))
(if (cph/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))))))
@ -836,31 +831,31 @@
(map add-change children) (map add-change children)
[(as-> {:type :reg-objects [(as-> {:type :reg-objects
:shapes (vec parents)} $ :shapes (vec parents)} $
(if (cph/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))))])]
(if (and (cph/touched-group? parent :shapes-group) omit-touched?) (if (and (cp/touched-group? parent :shapes-group) omit-touched?)
empty-changes empty-changes
[rchanges uchanges]))) [rchanges uchanges])))
(defn- move-shape (defn- move-shape
[shape index-before index-after container omit-touched?] [shape index-before index-after container omit-touched?]
(log/info :msg (str "MOVE " (log/info :msg (str "MOVE "
(if (cph/page? container) "[P] " "[C] ") (if (cp/page? container) "[P] " "[C] ")
(:name shape) (:name shape)
" " " "
index-before index-before
" -> " " -> "
index-after)) index-after))
(let [parent (cph/get-shape container (:parent-id shape)) (let [parent (cp/get-shape container (:parent-id shape))
rchanges [(as-> {:type :mov-objects rchanges [(as-> {:type :mov-objects
:parent-id (:parent-id shape) :parent-id (:parent-id shape)
:shapes [(:id shape)] :shapes [(:id shape)]
:index index-after :index index-after
:ignore-touched true} $ :ignore-touched true} $
(if (cph/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))))]
uchanges [(as-> {:type :mov-objects uchanges [(as-> {:type :mov-objects
@ -868,11 +863,11 @@
:shapes [(:id shape)] :shapes [(:id shape)]
:index index-before :index index-before
:ignore-touched true} $ :ignore-touched true} $
(if (cph/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))))]]
(if (and (cph/touched-group? parent :shapes-group) omit-touched?) (if (and (cp/touched-group? parent :shapes-group) omit-touched?)
empty-changes empty-changes
[rchanges uchanges]))) [rchanges uchanges])))
@ -886,7 +881,7 @@
empty-changes empty-changes
(do (do
(log/info :msg (str "CHANGE-TOUCHED " (log/info :msg (str "CHANGE-TOUCHED "
(if (cph/page? container) "[P] " "[C] ") (if (cp/page? container) "[P] " "[C] ")
(:name dest-shape)) (:name dest-shape))
:options options) :options options)
(let [rchanges [(as-> {:type :mod-obj (let [rchanges [(as-> {:type :mod-obj
@ -898,7 +893,7 @@
nil nil
copy-touched? copy-touched?
(:touched orig-shape))}]} $ (:touched orig-shape))}]} $
(if (cph/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))))]
@ -907,7 +902,7 @@
:operations :operations
[{:type :set-touched [{:type :set-touched
:touched (:touched dest-shape)}]} $ :touched (:touched dest-shape)}]} $
(if (cph/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])))) [rchanges uchanges]))))
@ -918,16 +913,16 @@
empty-changes empty-changes
(do (do
(log/info :msg (str "SET-TOUCHED-SHAPES-GROUP " (log/info :msg (str "SET-TOUCHED-SHAPES-GROUP "
(if (cph/page? container) "[P] " "[C] ") (if (cp/page? container) "[P] " "[C] ")
(:name shape))) (:name shape)))
(let [rchanges [(as-> {:type :mod-obj (let [rchanges [(as-> {:type :mod-obj
:id (:id shape) :id (:id shape)
:operations :operations
[{:type :set-touched [{:type :set-touched
:touched (cph/set-touched-group :touched (cp/set-touched-group
(:touched shape) (:touched shape)
:shapes-group)}]} $ :shapes-group)}]} $
(if (cph/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))))]
@ -936,7 +931,7 @@
:operations :operations
[{:type :set-touched [{:type :set-touched
:touched (:touched shape)}]} $ :touched (:touched shape)}]} $
(if (cph/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])))) [rchanges uchanges]))))
@ -962,7 +957,7 @@
(log/info :msg (str "SYNC " (log/info :msg (str "SYNC "
(:name origin-shape) (:name origin-shape)
" -> " " -> "
(if (cph/page? container) "[P] " "[C] ") (if (cp/page? container) "[P] " "[C] ")
(:name dest-shape))) (:name dest-shape)))
(let [; To synchronize geometry attributes we need to make a prior (let [; To synchronize geometry attributes we need to make a prior
@ -1002,13 +997,13 @@
rchanges [(as-> {:type :mod-obj rchanges [(as-> {:type :mod-obj
:id (:id dest-shape) :id (:id dest-shape)
:operations roperations} $ :operations roperations} $
(if (cph/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))))]
uchanges [(as-> {:type :mod-obj uchanges [(as-> {:type :mod-obj
:id (:id dest-shape) :id (:id dest-shape)
:operations uoperations} $ :operations uoperations} $
(if (cph/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]) [rchanges uchanges])

View file

@ -112,7 +112,8 @@
(def workspace-local-library (def workspace-local-library
(l/derived (fn [state] (l/derived (fn [state]
(select-keys (get state :workspace-data) (select-keys (get state :workspace-data)
[:colors [:id
:colors
:media :media
:typographies :typographies
:components])) :components]))

View file

@ -122,7 +122,7 @@
(let [root-shape (cp/get-root-shape shape objects) (let [root-shape (cp/get-root-shape shape objects)
component-id (when root-shape (:component-id root-shape)) component-id (when root-shape (:component-id root-shape))
component-file-id (when root-shape (:component-file root-shape)) component-file-id (when root-shape (:component-file root-shape))
component-file (when component-file-id (get libraries component-file-id)) 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
(get-in component-file [:data :components component-id]) (get-in component-file [:data :components component-id])
@ -141,7 +141,7 @@
"" ""
(let [component-id (:component-id shape) (let [component-id (:component-id shape)
component-file-id (:component-file shape) component-file-id (:component-file shape)
component-file (when component-file-id (get libraries component-file-id)) component-file (when component-file-id (get libraries component-file-id nil))
component (if component-file component (if component-file
(get-in component-file [:data :components component-id]) (get-in component-file [:data :components component-id])
(get components component-id))] (get components component-id))]

View file

@ -36,7 +36,6 @@
(get-in state [:viewer-libraries file-id :data :typographies]))] (get-in state [:viewer-libraries file-id :data :typographies]))]
#(l/derived get-library st/state))) #(l/derived get-library st/state)))
(def properties [:fill-color (def properties [:fill-color
:fill-color-gradient :fill-color-gradient
:font-family :font-family

View file

@ -80,9 +80,6 @@
(-> (js->clj fill-color-gradient :keywordize-keys true) (-> (js->clj fill-color-gradient :keywordize-keys true)
(update :type keyword))) (update :type keyword)))
fill-color-ref-id (obj/get data "fill-color-ref-id")
fill-color-ref-file (obj/get data "fill-color-ref-file")
;; Uncomment this to allow to remove text colors. This could break the texts that already exist ;; Uncomment this to allow to remove text colors. This could break the texts that already exist
;;[r g b a] (if (nil? fill-color) ;;[r g b a] (if (nil? fill-color)
;; [0 0 0 0] ;; Transparent color ;; [0 0 0 0] ;; Transparent color

View file

@ -16,6 +16,7 @@
[app.main.refs :as refs] [app.main.refs :as refs]
[app.main.store :as st] [app.main.store :as st]
[app.main.streams :as ms] [app.main.streams :as ms]
[app.main.ui.context :as ctx]
[app.main.ui.components.dropdown :refer [dropdown]] [app.main.ui.components.dropdown :refer [dropdown]]
[app.main.ui.hooks :refer [use-rxsub]] [app.main.ui.hooks :refer [use-rxsub]]
[app.main.ui.icons :as i] [app.main.ui.icons :as i]
@ -50,6 +51,8 @@
{:keys [id] :as shape} (:shape mdata) {:keys [id] :as shape} (:shape mdata)
selected (:selected mdata) selected (:selected mdata)
current-file-id (mf/use-ctx ctx/current-file-id)
do-duplicate #(st/emit! dw/duplicate-selected) do-duplicate #(st/emit! dw/duplicate-selected)
do-delete #(st/emit! dw/delete-selected) do-delete #(st/emit! dw/delete-selected)
do-copy #(st/emit! dw/copy-selected) do-copy #(st/emit! dw/copy-selected)
@ -73,7 +76,7 @@
do-update-component #(do do-update-component #(do
(st/emit! (dwc/start-undo-transaction)) (st/emit! (dwc/start-undo-transaction))
(st/emit! (dwl/update-component id)) (st/emit! (dwl/update-component id))
(st/emit! (dwl/sync-file nil)) (st/emit! (dwl/sync-file current-file-id))
(st/emit! (dwc/commit-undo-transaction))) (st/emit! (dwc/commit-undo-transaction)))
do-show-component #(st/emit! (dw/go-to-layout :assets)) do-show-component #(st/emit! (dw/go-to-layout :assets))
do-navigate-component-file #(st/emit! (dwl/nav-to-component-file do-navigate-component-file #(st/emit! (dwl/nav-to-component-file
@ -154,7 +157,7 @@
;; WARNING: this menu is the same as the context menu at the sidebar. ;; WARNING: this menu is the same as the context menu at the sidebar.
;; If you change it, you must change equally the file ;; If you change it, you must change equally the file
;; app/main/ui/workspace/sidebar/options/component.cljs ;; app/main/ui/workspace/sidebar/options/component.cljs
(if (nil? (:component-file shape)) (if (= (:component-file shape) current-file-id)
[:* [:*
[:& menu-separator] [:& menu-separator]
[:& menu-entry {:title (t locale "workspace.shape.menu.detach-instance") [:& menu-entry {:title (t locale "workspace.shape.menu.detach-instance")

View file

@ -51,6 +51,7 @@
:top nil :top nil
:left nil :left nil
:component-id nil}) :component-id nil})
on-duplicate on-duplicate
(mf/use-callback (mf/use-callback
(mf/deps state) (mf/deps state)
@ -61,7 +62,7 @@
(mf/deps state) (mf/deps state)
(fn [] (fn []
(st/emit! (dwl/delete-component {:id (:component-id @state)})) (st/emit! (dwl/delete-component {:id (:component-id @state)}))
(st/emit! (dwl/sync-file nil)))) (st/emit! (dwl/sync-file file-id))))
on-rename on-rename
(mf/use-callback (mf/use-callback
@ -99,7 +100,7 @@
on-drag-start on-drag-start
(mf/use-callback (mf/use-callback
(fn [component event] (fn [component event]
(dnd/set-data! event "app/component" {:file-id (if local? nil file-id) (dnd/set-data! event "app/component" {:file-id file-id
:component component}) :component component})
(dnd/set-allowed-effect! event "move")))] (dnd/set-allowed-effect! event "move")))]
@ -260,7 +261,7 @@
[(tr "workspace.assets.delete") on-delete]]}])])])) [(tr "workspace.assets.delete") on-delete]]}])])]))
(mf/defc color-item (mf/defc color-item
[{:keys [color local? locale] :as props}] [{:keys [color local? file-id locale] :as props}]
(let [rename? (= (:color-for-rename @refs/workspace-local) (:id color)) (let [rename? (= (:color-for-rename @refs/workspace-local) (:id color))
id (:id color) id (:id color)
input-ref (mf/use-ref) input-ref (mf/use-ref)
@ -283,12 +284,12 @@
rename-color rename-color
(fn [name] (fn [name]
(st/emit! (dwl/update-color (assoc color :name name)))) (st/emit! (dwl/update-color (assoc color :name name) file-id)))
edit-color edit-color
(fn [new-color] (fn [new-color]
(let [updated-color (merge new-color (select-keys color [:id :file-id :name]))] (let [updated-color (merge new-color (select-keys color [:id :file-id :name]))]
(st/emit! (dwl/update-color updated-color)))) (st/emit! (dwl/update-color updated-color file-id))))
delete-color delete-color
(fn [] (fn []
@ -406,9 +407,10 @@
(let [color (cond-> color (let [color (cond-> color
(:value color) (assoc :color (:value color) :opacity 1) (:value color) (assoc :color (:value color) :opacity 1)
(:value color) (dissoc :value) (:value color) (dissoc :value)
true (assoc :file-id (when (not local?) file-id)))] true (assoc :file-id file-id))]
[:& color-item {:key (:id color) [:& color-item {:key (:id color)
:color color :color color
:file-id file-id
:local? local? :local? local?
:locale locale}]))])])) :locale locale}]))])]))
@ -433,12 +435,12 @@
(mf/use-callback (mf/use-callback
(mf/deps file-id) (mf/deps file-id)
(fn [typography changes] (fn [typography changes]
(st/emit! (dwl/update-typography (merge typography changes))))) (st/emit! (dwl/update-typography (merge typography changes) file-id))))
handle-typography-selection handle-typography-selection
(fn [typography] (fn [typography]
(let [attrs (merge (let [attrs (merge
{:typography-ref-file (when-not local? file-id) {:typography-ref-file file-id
:typography-ref-id (:id typography)} :typography-ref-id (:id typography)}
(d/without-keys typography [:id :name]))] (d/without-keys typography [:id :name]))]
(run! #(st/emit! (dwt/update-text-attrs {:id % :editor (get-in local [:editors %]) :attrs attrs})) (run! #(st/emit! (dwt/update-text-attrs {:id % :editor (get-in local [:editors %]) :attrs attrs}))

View file

@ -13,6 +13,7 @@
[app.common.pages :as cp] [app.common.pages :as cp]
[app.main.refs :as refs] [app.main.refs :as refs]
[app.main.store :as st] [app.main.store :as st]
[app.main.ui.context :as ctx]
[app.main.ui.icons :as i] [app.main.ui.icons :as i]
[app.main.ui.components.context-menu :refer [context-menu]] [app.main.ui.components.context-menu :refer [context-menu]]
[app.main.data.workspace :as dw] [app.main.data.workspace :as dw]
@ -25,7 +26,9 @@
(mf/defc component-menu (mf/defc component-menu
[{:keys [ids values] :as props}] [{:keys [ids values] :as props}]
(let [id (first ids) (let [current-file-id (mf/use-ctx ctx/current-file-id)
id (first ids)
locale (mf/deref i18n/locale) locale (mf/deref i18n/locale)
local (mf/use-state {:menu-open false}) local (mf/use-state {:menu-open false})
@ -51,7 +54,7 @@
do-update-component #(do do-update-component #(do
(st/emit! (dwc/start-undo-transaction)) (st/emit! (dwc/start-undo-transaction))
(st/emit! (dwl/update-component id)) (st/emit! (dwl/update-component id))
(st/emit! (dwl/sync-file nil)) (st/emit! (dwl/sync-file current-file-id))
(st/emit! (dwc/commit-undo-transaction))) (st/emit! (dwc/commit-undo-transaction)))
do-show-component #(st/emit! (dw/go-to-layout :assets)) do-show-component #(st/emit! (dw/go-to-layout :assets))
do-navigate-component-file #(st/emit! (dwl/nav-to-component-file do-navigate-component-file #(st/emit! (dwl/nav-to-component-file
@ -72,7 +75,7 @@
;; app/main/ui/workspace/context_menu.cljs ;; app/main/ui/workspace/context_menu.cljs
[:& context-menu {:on-close on-menu-close [:& context-menu {:on-close on-menu-close
:show (:menu-open @local) :show (:menu-open @local)
:options (if (nil? (:component-file values)) :options (if (= (:component-file values) current-file-id)
[[(t locale "workspace.shape.menu.detach-instance") do-detach-component] [[(t locale "workspace.shape.menu.detach-instance") do-detach-component]
[(t locale "workspace.shape.menu.reset-overrides") do-reset-component] [(t locale "workspace.shape.menu.reset-overrides") do-reset-component]
[(t locale "workspace.shape.menu.update-master") do-update-component] [(t locale "workspace.shape.menu.update-master") do-update-component]

View file

@ -21,6 +21,7 @@
[app.main.refs :as refs] [app.main.refs :as refs]
[app.main.data.modal :as modal] [app.main.data.modal :as modal]
[app.main.ui.hooks :as h] [app.main.ui.hooks :as h]
[app.main.ui.context :as ctx]
[app.main.ui.components.color-bullet :as cb] [app.main.ui.components.color-bullet :as cb]
[app.main.ui.components.numeric-input :refer [numeric-input]])) [app.main.ui.components.numeric-input :refer [numeric-input]]))
@ -64,11 +65,14 @@
(mf/defc color-row (mf/defc color-row
[{:keys [color disable-gradient disable-opacity on-change on-open on-close]}] [{:keys [color disable-gradient disable-opacity on-change on-open on-close]}]
(let [file-colors (mf/deref refs/workspace-file-colors) (let [current-file-id (mf/use-ctx ctx/current-file-id)
shared-libs (mf/deref refs/workspace-libraries) file-colors (mf/deref refs/workspace-file-colors)
shared-libs (mf/deref refs/workspace-libraries)
get-color-name (fn [{:keys [id file-id]}] get-color-name (fn [{:keys [id file-id]}]
(let [src-colors (if file-id (get-in shared-libs [file-id :data :colors]) file-colors)] (let [src-colors (if (= file-id current-file-id)
file-colors
(get-in shared-libs [file-id :data :colors]))]
(get-in src-colors [id :name]))) (get-in src-colors [id :name])))
parse-color (fn [color] parse-color (fn [color]

View file

@ -12,6 +12,7 @@
[rumext.alpha :as mf] [rumext.alpha :as mf]
[cuerdas.core :as str] [cuerdas.core :as str]
[okulary.core :as l] [okulary.core :as l]
[app.main.ui.context :as ctx]
[app.main.ui.icons :as i] [app.main.ui.icons :as i]
[app.common.data :as d] [app.common.data :as d]
[app.common.uuid :as uuid] [app.common.uuid :as uuid]
@ -177,6 +178,7 @@
shapes] :as props}] shapes] :as props}]
(let [locale (mf/deref i18n/locale) (let [locale (mf/deref i18n/locale)
current-file-id (mf/use-ctx ctx/current-file-id)
typographies (mf/deref refs/workspace-file-typography) typographies (mf/deref refs/workspace-file-typography)
shared-libs (mf/deref refs/workspace-libraries) shared-libs (mf/deref refs/workspace-libraries)
label (case type label (case type
@ -201,16 +203,16 @@
typography (cond typography (cond
(and (:typography-ref-id values) (and (:typography-ref-id values)
(not= (:typography-ref-id values) :multiple) (not= (:typography-ref-id values) :multiple)
(:typography-ref-file values)) (not= (:typography-ref-file values) current-file-id))
(-> shared-libs (-> shared-libs
(get-in [(:typography-ref-file values) :data :typographies (:typography-ref-id values)]) (get-in [(:typography-ref-file values) :data :typographies (:typography-ref-id values)])
(assoc :file-id (:typography-ref-file values))) (assoc :file-id (:typography-ref-file values)))
(and (:typography-ref-id values) (and (:typography-ref-id values)
(not= (:typography-ref-id values) :multiple)) (not= (:typography-ref-id values) :multiple)
(= (:typography-ref-file values) current-file-id))
(get typographies (:typography-ref-id values))) (get typographies (:typography-ref-id values)))
on-convert-to-typography on-convert-to-typography
(mf/use-callback (mf/use-callback
(mf/deps values) (mf/deps values)
@ -234,7 +236,7 @@
handle-change-typography handle-change-typography
(fn [changes] (fn [changes]
(st/emit! (dwl/update-typography (merge typography changes)))) (st/emit! (dwl/update-typography (merge typography changes) current-file-id)))
opts #js {:editor editor opts #js {:editor editor
:ids ids :ids ids
@ -253,7 +255,7 @@
(cond (cond
typography typography
[:& typography-entry {:typography typography [:& typography-entry {:typography typography
:read-only? (some? (:typography-ref-file values)) :read-only? (not= (:typography-ref-file values) current-file-id)
:file (get shared-libs (:typography-ref-file values)) :file (get shared-libs (:typography-ref-file values))
:on-deattach handle-deattach-typography :on-deattach handle-deattach-typography
:on-change handle-change-typography}] :on-change handle-change-typography}]

View file

@ -25,9 +25,7 @@
(defn format-fill-color [_ shape] (defn format-fill-color [_ shape]
(let [color {:color (:fill-color shape) (let [color {:color (:fill-color shape)
:opacity (:fill-opacity shape) :opacity (:fill-opacity shape)
:gradient (:fill-color-gradient shape) :gradient (:fill-color-gradient shape)}]
:id (:fill-ref-id shape)
:file-id (:fill-ref-file-id shape)}]
(uc/color->background color))) (uc/color->background color)))
(defn format-stroke [_ shape] (defn format-stroke [_ shape]
@ -35,9 +33,7 @@
style (name (:stroke-style shape)) style (name (:stroke-style shape))
color {:color (:stroke-color shape) color {:color (:stroke-color shape)
:opacity (:stroke-opacity shape) :opacity (:stroke-opacity shape)
:gradient (:stroke-color-gradient shape) :gradient (:stroke-color-gradient shape)}]
:id (:stroke-ref-id shape)
:file-id (:stroke-ref-file-id shape)}]
(str/format "%spx %s %s" width style (uc/color->background color)))) (str/format "%spx %s %s" width style (uc/color->background color))))
(def styles-data (def styles-data