diff --git a/backend/src/app/services/mutations/files.clj b/backend/src/app/services/mutations/files.clj index a1e3a3e19..e8cb8ef01 100644 --- a/backend/src/app/services/mutations/files.clj +++ b/backend/src/app/services/mutations/files.clj @@ -188,10 +188,6 @@ :library-file-id library-id})) - - - - ;; A generic, Changes based (granular) file update method. (s/def ::changes @@ -202,6 +198,13 @@ (s/def ::update-file (s/keys :req-un [::id ::session-id ::profile-id ::revn ::changes])) +;; File changes that affect to the library, and must be notified +;; to all clients using it. +(def library-changes + #{:add-color :mod-color :del-color + :add-media :mod-media :del-media + :add-component :mod-component :del-component}) + (declare update-file) (declare retrieve-lagged-changes) (declare insert-change) @@ -239,11 +242,27 @@ :file-id (:id file) :session-id sid :revn (:revn file) - :changes changes}] + :changes changes} + + library-changes (filter #(library-changes (:type %)) changes)] @(redis/run! :publish {:channel (str (:id file)) :message (t/encode-str msg)}) + (if (and (:is-shared file) (seq library-changes)) + (let [{:keys [team-id] :as project} + (db/get-by-id conn :project (:project-id file)) + + msg {:type :library-change + :profile-id (:profile-id params) + :file-id (:id file) + :session-id sid + :revn (:revn file) + :changes library-changes}] + + @(redis/run! :publish {:channel (str team-id) + :message (t/encode-str msg)}))) + (db/update! conn :file {:revn (:revn file) :data (:data file)} diff --git a/common/app/common/pages.cljc b/common/app/common/pages.cljc index 18f286ee4..6aa325c36 100644 --- a/common/app/common/pages.cljc +++ b/common/app/common/pages.cljc @@ -368,12 +368,12 @@ (defmethod change-spec :add-component [_] (s/keys :req-un [::id ::name :internal.changes.add-component/shapes])) +(defmethod change-spec :mod-component [_] + (s/keys :req-un [::id ::name :internal.changes.add-component/shapes])) + (defmethod change-spec :del-component [_] (s/keys :req-un [::id])) -(defmethod change-spec :update-component [_] - (s/keys :req-un [::id ::name :internal.changes.add-component/shapes])) - (s/def ::change (s/multi-spec change-spec :type)) (s/def ::changes (s/coll-of ::change)) @@ -782,17 +782,17 @@ :name name :objects (d/index-by :id shapes)})) -(defmethod process-change :del-component - [data {:keys [id]}] - (d/dissoc-in data [:components id])) - -(defmethod process-change :update-component +(defmethod process-change :mod-component [data {:keys [id name shapes]}] (update-in data [:components id] #(assoc % :name name :objects (d/index-by :id shapes)))) +(defmethod process-change :del-component + [data {:keys [id]}] + (d/dissoc-in data [:components id])) + (defmethod process-operation :set [shape op] (let [attr (:attr op) diff --git a/frontend/src/app/main/data/workspace/libraries.cljs b/frontend/src/app/main/data/workspace/libraries.cljs index 11d1df3c7..f0327d0b3 100644 --- a/frontend/src/app/main/data/workspace/libraries.cljs +++ b/frontend/src/app/main/data/workspace/libraries.cljs @@ -345,6 +345,16 @@ qparams {:page-id (first (get-in file [:data :pages]))}] (st/emit! (rt/nav-new-window :workspace pparams qparams)))))) +(defn ext-library-changed + [file-id changes] + (us/assert ::us/uuid file-id) + (us/assert ::cp/changes changes) + (ptk/reify ::ext-library-changed + ptk/UpdateEvent + (update [_ state] + (d/update-in-when state [:workspace-libraries file-id :data] + cp/process-changes changes)))) + (declare generate-sync-file) (declare generate-sync-page) (declare generate-sync-shape-and-children) @@ -407,12 +417,12 @@ [new-shape new-shapes _] (cph/clone-object root-shape nil objects update-new-shape) - rchanges [{:type :update-component + rchanges [{:type :mod-component :id component-id :name (:name new-shape) :shapes new-shapes}] - uchanges [{:type :update-component + uchanges [{:type :mod-component :id component-id :name (:name component-obj) :shapes (vals component-objs)}]] @@ -420,7 +430,7 @@ (rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true})))))) (defn sync-file - [{:keys [file-id] :as params}] + [file-id] (us/assert (s/nilable ::us/uuid) file-id) (ptk/reify ::sync-file ptk/WatchEvent @@ -434,22 +444,25 @@ (if (nil? file-id) (get-in state [:workspace-data :components]) (get-in state [:workspace-libraries file-id :data :components]))] - (loop [pages (seq (vals (get-in state [:workspace-data :pages-index]))) - rchanges [] - uchanges []] - (let [page (first pages)] - (if (nil? page) - [rchanges uchanges] - (let [[page-rchanges page-uchanges] - (generate-sync-page page components)] - (recur (next pages) - (concat rchanges page-rchanges) - (concat uchanges page-uchanges)))))))) + (when (some? components) + (loop [pages (seq (vals (get-in state [:workspace-data :pages-index]))) + rchanges [] + uchanges []] + (let [page (first pages)] + (if (nil? page) + [rchanges uchanges] + (let [[page-rchanges page-uchanges] + (generate-sync-page file-id page components)] + (recur (next pages) + (concat rchanges page-rchanges) + (concat uchanges page-uchanges))))))))) (defn- generate-sync-page - [page components] + [file-id page components] (let [linked-shapes - (cph/select-objects #(some? (:component-id %)) page)] + (cph/select-objects #(and (some? (:component-id %)) + (= (:component-file %) file-id)) + page)] (loop [shapes (seq linked-shapes) rchanges [] uchanges []] diff --git a/frontend/src/app/main/data/workspace/notifications.cljs b/frontend/src/app/main/data/workspace/notifications.cljs index 38ab4f189..20b851491 100644 --- a/frontend/src/app/main/data/workspace/notifications.cljs +++ b/frontend/src/app/main/data/workspace/notifications.cljs @@ -15,6 +15,7 @@ [app.common.spec :as us] [app.main.data.workspace.common :as dwc] [app.main.data.workspace.persistence :as dwp] + [app.main.data.workspace.libraries :as dwl] [app.main.repo :as rp] [app.main.store :as st] [app.main.streams :as ms] @@ -31,6 +32,7 @@ (declare handle-presence) (declare handle-pointer-update) (declare handle-file-change) +(declare handle-library-change) (declare handle-pointer-send) (declare send-keepalive) @@ -73,6 +75,7 @@ :presence (handle-presence msg) :pointer-update (handle-pointer-update msg) :file-change (handle-file-change msg) + :library-change (handle-library-change msg) ::unknown)) (defn- send-keepalive @@ -197,3 +200,17 @@ (rx/of (dwp/shapes-changes-persisted file-id msg)) (when (seq page-ids) (rx/from (map dwc/update-indices page-ids)))))))) + +(s/def ::library-change-event + (s/keys :req-un [::type ::profile-id ::file-id ::session-id ::revn ::changes])) + +(defn handle-library-change + [{:keys [file-id changes] :as msg}] + (us/assert ::library-change-event msg) + (ptk/reify ::handle-library-change + ptk/WatchEvent + (watch [_ state stream] + (when (contains? (:workspace-libraries state) file-id) + (rx/of (dwl/ext-library-changed file-id changes) + (dwl/sync-file file-id)))))) + diff --git a/frontend/src/app/main/ui/workspace/context_menu.cljs b/frontend/src/app/main/ui/workspace/context_menu.cljs index 487708bee..a7330c3cb 100644 --- a/frontend/src/app/main/ui/workspace/context_menu.cljs +++ b/frontend/src/app/main/ui/workspace/context_menu.cljs @@ -67,7 +67,7 @@ do-reset-component #(st/emit! (dwl/reset-component id)) do-update-component #(do (st/emit! (dwl/update-component id)) - (st/emit! (dwl/sync-file {:file-id nil}))) + (st/emit! (dwl/sync-file nil))) do-navigate-component-file #(st/emit! (dwl/nav-to-component-file (:component-file root-shape)))] [:* diff --git a/frontend/src/app/main/ui/workspace/sidebar/assets.cljs b/frontend/src/app/main/ui/workspace/sidebar/assets.cljs index 79a51e1da..ea76d9870 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/assets.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/assets.cljs @@ -51,7 +51,7 @@ (mf/deps state) (fn [] (st/emit! (dwl/delete-component {:id (:component-id @state)})) - (st/emit! (dwl/sync-file {:file-id nil})))) + (st/emit! (dwl/sync-file nil)))) on-context-menu (mf/use-callback