0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-03-11 23:31:21 -05:00

Merge pull request #3189 from penpot/hiru-sync-notifications

 Notify library updates when really needed
This commit is contained in:
Pablo Alba 2023-05-17 15:35:06 +02:00 committed by GitHub
commit 44a3f651c2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
32 changed files with 373 additions and 195 deletions

View file

@ -253,7 +253,6 @@
:code :feature-mismatch
:feature "components/v2"
:hint "file has 'components/v2' feature enabled but frontend didn't specifies it"))
(cond-> file
(and (contains? client-features "components/v2")
(not (contains? features "components/v2")))
@ -263,7 +262,6 @@
(not (contains? client-features "storage/pointer-map")))
(process-pointers deref)))
;; --- COMMAND QUERY: get-file (by id)
(defn get-file

View file

@ -78,8 +78,7 @@
(defn- library-change?
[{:keys [type] :as change}]
(or (contains? library-change-types type)
(and (contains? file-change-types type)
(some? (:component-id change)))))
(contains? file-change-types type)))
(def ^:private sql:get-file
"SELECT f.*, p.team_id

View file

@ -6,7 +6,9 @@
(ns app.util.time
(:require
[app.common.data.macros :as dm]
[app.common.exceptions :as ex]
[app.common.time :as common-time]
[clojure.spec.alpha :as s]
[cuerdas.core :as str]
[fipp.ednize :as fez])
@ -186,9 +188,7 @@
:else
(throw (UnsupportedOperationException. "unsupported type")))))
(defn now
[]
(Instant/now))
(dm/export common-time/now)
(defn in-future
[v]

View file

@ -4,7 +4,7 @@
"main": "index.js",
"license": "MPL-2.0",
"dependencies": {
"luxon": "^3.1.1"
"luxon": "^3.3.0"
},
"scripts": {
"compile-and-watch-test": "clojure -M:dev:shadow-cljs watch test",

View file

@ -16,6 +16,7 @@
[app.common.pages.helpers :as cph]
[app.common.spec :as us]
[app.common.pages.changes-spec :as pcs]
[app.common.types.component :as ctk]
[app.common.types.components-list :as ctkl]
[app.common.types.container :as ctn]
[app.common.types.colors-list :as ctcl]
@ -53,7 +54,7 @@
(run! validate-shape!)))))
(defmulti process-change (fn [_ change] (:type change)))
(defmulti process-operation (fn [_ op] (:type op)))
(defmulti process-operation (fn [_ _ op] (:type op)))
(defn process-changes
([data items]
@ -91,14 +92,34 @@
(defmethod process-change :mod-obj
[data {:keys [id page-id component-id operations]}]
(let [update-fn (fn [objects]
(let [objects (if page-id
(-> data :pages-index (get page-id) :objects)
(-> data :components (get component-id) :objects))
modified-component-ids (atom #{})
on-touched (fn [shape]
;; When a shape is modified, if it belongs to a main component instance,
;; the component needs to be marked as modified.
(let [component-root (ctn/get-component-shape objects shape {:allow-main? true})]
(when (ctk/main-instance? component-root)
(swap! modified-component-ids conj (:component-id component-root)))))
update-fn (fn [objects]
(if-let [obj (get objects id)]
(let [result (reduce process-operation obj operations)]
(let [result (reduce (partial process-operation on-touched) obj operations)]
(assoc objects id result))
objects))]
(if page-id
(d/update-in-when data [:pages-index page-id :objects] update-fn)
(d/update-in-when data [:components component-id :objects] update-fn))))
objects))
modify-components (fn [data]
(reduce ctkl/set-component-modified
data @modified-component-ids))]
(as-> data $
(if page-id
(d/update-in-when $ [:pages-index page-id :objects] update-fn)
(d/update-in-when $ [:components component-id :objects] update-fn))
(modify-components $))))
(defmethod process-change :del-obj
[data {:keys [page-id component-id id ignore-touched]}]
@ -223,8 +244,6 @@
(not= :frame (:type obj))
(as-> $$ (reduce (partial assign-frame-id frame-id) $$ (:shapes obj))))))
(move-objects [objects]
(let [valid? (every? (partial is-valid-move? objects) shapes)
parent (get objects parent-id)
@ -284,7 +303,7 @@
(defmethod process-change :mod-color
[data {:keys [color]}]
(d/assoc-in-when data [:colors (:id color)] color))
(ctcl/set-color data color))
(defmethod process-change :del-color
[data {:keys [id]}]
@ -343,7 +362,7 @@
(defmethod process-change :mod-typography
[data {:keys [typography]}]
(d/update-in-when data [:typographies (:id typography)] merge typography))
(ctyl/update-typography data (:id typography) merge typography))
(defmethod process-change :del-typography
[data {:keys [id]}]
@ -352,7 +371,7 @@
;; === Operations
(defmethod process-operation :set
[shape op]
[on-touched shape op]
(let [attr (:attr op)
group (get component-sync-attrs attr)
val (:val op)
@ -367,7 +386,7 @@
;; after the check added in data/workspace/modifiers/check-delta
;; function. Better check it and test toroughly when activating
;; components-v2 mode.
shape-ref (:shape-ref shape)
in-copy? (ctk/in-component-copy? shape)
root-name? (and (= group :name-group)
(:component-root? shape))
@ -379,17 +398,23 @@
(gsh/close-attrs? attr val shape-val 1)
(gsh/close-attrs? attr val shape-val))]
(when (and group (not ignore) (not equal?)
(not root-name?)
(not (and ignore-geometry is-geometry?)))
;; Notify touched even if it's not copy, because it may be a main instance
(on-touched shape))
(cond-> shape
;; Depending on the origin of the attribute change, we need or not to
;; set the "touched" flag for the group the attribute belongs to.
;; In some cases we need to ignore touched only if the attribute is
;; geometric (position, width or transformation).
(and shape-ref group (not ignore) (not equal?)
(and in-copy? group (not ignore) (not equal?)
(not root-name?)
(not (and ignore-geometry is-geometry?)))
(->
(update :touched cph/set-touched-group group)
(dissoc :remote-synced?))
(update :touched cph/set-touched-group group)
(dissoc :remote-synced?))
(nil? val)
(dissoc attr)
@ -398,23 +423,23 @@
(assoc attr val))))
(defmethod process-operation :set-touched
[shape op]
[_ shape op]
(let [touched (:touched op)
shape-ref (:shape-ref shape)]
(if (or (nil? shape-ref) (nil? touched) (empty? touched))
in-copy? (ctk/in-component-copy? shape)]
(if (or (not in-copy?) (nil? touched) (empty? touched))
(dissoc shape :touched)
(assoc shape :touched touched))))
(defmethod process-operation :set-remote-synced
[shape op]
[_ shape op]
(let [remote-synced? (:remote-synced? op)
shape-ref (:shape-ref shape)]
(if (or (nil? shape-ref) (not remote-synced?))
in-copy? (ctk/in-component-copy? shape)]
(if (or (not in-copy?) (not remote-synced?))
(dissoc shape :remote-synced?)
(assoc shape :remote-synced? true))))
(defmethod process-operation :default
[_ op]
[_ _ op]
(ex/raise :type :not-implemented
:code :operation-not-implemented
:context {:type (:type op)}))

View file

@ -272,17 +272,6 @@
[shape group]
((or (:touched shape) #{}) group))
(defn get-root-shape
"Get the root shape linked to a component for this shape, if any."
[objects shape]
(cond
(some? (:component-root? shape))
shape
(some? (:shape-ref shape))
(recur objects (get objects (:parent-id shape)))))
(defn make-container
[page-or-component type]
(assoc page-or-component :type type))

View file

@ -235,7 +235,7 @@
;; --- SPECS WITHOUT CONFORMER
(s/def ::inst inst?)
(s/def ::inst inst?) ;; A clojure instant (date and time)
(s/def ::string
(s/with-gen string?

View file

@ -0,0 +1,27 @@
;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; Copyright (c) KALEIDOS INC
;; Here we put the time functions that are common between frontend and backend.
;; In the future we may create an unified API for both.
(ns app.common.time
#?(:cljs
(:require
["luxon" :as lxn])
:clj
(:import
java.time.Instant)))
#?(:cljs
(def DateTime lxn/DateTime))
#?(:cljs
(def Duration lxn/Duration))
(defn now
[]
#?(:clj (Instant/now)
:cljs (.local ^js DateTime)))

View file

@ -58,6 +58,7 @@
(s/def ::color-generic/gradient (s/nilable ::gradient))
(s/def ::color-generic/ref-id uuid?)
(s/def ::color-generic/ref-file uuid?)
(s/def ::color-generic/modified-at ::us/inst)
(s/def ::shape-color
(s/keys :req-un [:us/color
@ -73,7 +74,8 @@
::color-generic/value
::color-generic/color
::color-generic/opacity
::color-generic/gradient]))
::color-generic/gradient
::color-generic/modified-at]))
(s/def ::recent-color
(s/and

View file

@ -4,25 +4,51 @@
;;
;; Copyright (c) KALEIDOS INC
(ns app.common.types.colors-list)
(ns app.common.types.colors-list
(:require
[app.common.data :as d]
[app.common.time :as dt]
[app.common.types.color :as ctc]))
(defn colors-seq
[file-data]
(vals (:colors file-data)))
(defn- touch
[color]
(assoc color :modified-at (dt/now)))
(defn add-color
[file-data color]
(update file-data :colors assoc (:id color) color))
(update file-data :colors assoc (:id color) (touch color)))
(defn get-color
[file-data color-id]
(get-in file-data [:colors color-id]))
(defn get-ref-color
[library-data color]
(when (= (:ref-file color) (:id library-data))
(get-color library-data (:ref-id color))))
(defn set-color
[file-data color]
(d/assoc-in-when file-data [:colors (:id color)] (touch color)))
(defn update-color
[file-data color-id f]
(update-in file-data [:colors color-id] f))
[file-data color-id f & args]
(d/update-in-when file-data [:colors color-id] #(-> (apply f % args)
(touch))))
(defn delete-color
[file-data color-id]
(update file-data :colors dissoc color-id))
(defn used-colors-changed-since
"Find all usages of any color in the library by the given shape, of colors
that have ben modified after the date."
[shape library since-date]
(->> (ctc/get-all-colors shape)
(keep #(get-ref-color (:data library) %))
(remove #(< (:modified-at %) since-date)) ;; Note that :modified-at may be nil
(map #(vector (:id shape) (:id %) :color))))

View file

@ -7,6 +7,12 @@
(ns app.common.types.component)
(defn instance-root?
"Check if this shape is the head of a top instance."
[shape]
(some? (:component-root? shape)))
(defn instance-head?
"Check if this shape is the head of a top instance or a subinstance."
[shape]
(some? (:component-id shape)))
@ -46,12 +52,12 @@
(and (some? (:component-id shape))
(= (:component-file shape) library-id)))
(defn in-component-instance?
(defn in-component-copy?
"Check if the shape is inside a component non-main instance."
[shape]
(some? (:shape-ref shape)))
(defn in-component-instance-not-root?
(defn in-component-copy-not-root?
"Check if the shape is inside a component non-main instance and
is not the root shape."
[shape]

View file

@ -8,7 +8,9 @@
(:require
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.files.features :as feat]))
[app.common.files.features :as feat]
[app.common.time :as dt]
[app.common.types.component :as ctk]))
(defn components
[file-data]
@ -23,6 +25,10 @@
[file-data]
(filter :deleted (vals (:components file-data))))
(defn- touch
[component]
(assoc component :modified-at (dt/now)))
(defn add-component
[file-data {:keys [id name path main-instance-id main-instance-page shapes]}]
(let [components-v2 (dm/get-in file-data [:options :components-v2])
@ -30,9 +36,9 @@
(cond-> file-data
:always
(assoc-in [:components id]
{:id id
:name name
:path path})
(touch {:id id
:name name
:path path}))
(not components-v2)
(assoc-in [:components id :objects]
@ -47,24 +53,27 @@
(defn mod-component
[file-data {:keys [id name path objects annotation]}]
(let [wrap-objects-fn feat/*wrap-with-objects-map-fn*]
(update-in file-data [:components id]
(fn [component]
(let [objects (some-> objects wrap-objects-fn)]
(cond-> component
(some? name)
(assoc :name name)
(d/update-in-when file-data [:components id]
(fn [component]
(let [objects (some-> objects wrap-objects-fn)]
(cond-> component
(some? name)
(assoc :name name)
(some? path)
(assoc :path path)
(some? path)
(assoc :path path)
(some? objects)
(assoc :objects objects)
(some? objects)
(assoc :objects objects)
(some? annotation)
(assoc :annotation annotation)
(some? annotation)
(assoc :annotation annotation)
(nil? annotation)
(dissoc :annotation)))))))
(nil? annotation)
(dissoc :annotation)
:always
(touch)))))))
(defn get-component
([file-data component-id]
@ -83,8 +92,13 @@
component)))
(defn update-component
[file-data component-id f]
(update-in file-data [:components component-id] f))
[file-data component-id f & args]
(d/update-in-when file-data [:components component-id] #(-> (apply f % args)
(touch))))
(defn set-component-modified
[file-data component-id]
(update-component file-data component-id identity))
(defn delete-component
[file-data component-id]
@ -92,8 +106,19 @@
(defn mark-component-deleted
[file-data component-id]
(assoc-in file-data [:components component-id :deleted] true))
(d/update-in-when file-data [:components component-id] assoc :deleted true))
(defn mark-component-undeleted
[file-data component-id]
(d/dissoc-in file-data [:components component-id :deleted]))
(defn used-components-changed-since
"Check if the shape is an instance of any component in the library, and
the component has been modified after the date."
[shape library since-date]
(if (ctk/uses-library-components? shape (:id library))
(let [component (get-component (:data library) (:component-id shape))]
(if (< (:modified-at component) since-date) ;; Note that :modified-at may be nil
[]
[[(:id shape) (:component-id shape) :component]]))
[]))

View file

@ -10,6 +10,7 @@
[app.common.geom.shapes :as gsh]
[app.common.pages.common :as common]
[app.common.spec :as us]
[app.common.types.component :as ctk]
[app.common.types.components-list :as ctkl]
[app.common.types.pages-list :as ctpl]
[app.common.types.shape-tree :as ctst]
@ -20,10 +21,11 @@
(s/def ::id uuid?)
(s/def ::name ::us/string)
(s/def ::path (s/nilable ::us/string))
(s/def ::modified-at ::us/inst)
(s/def ::container
(s/keys :req-un [::id ::name]
:opt-un [::type ::path ::ctst/objects]))
:opt-un [::type ::path ::modified-at ::ctst/objects]))
(defn make-container
[page-or-component type]
@ -70,14 +72,20 @@
(defn get-component-shape
"Get the parent shape linked to a component for this shape, if any"
[objects shape]
(if-not (:shape-ref shape)
([objects shape] (get-component-shape objects shape nil))
([objects shape {:keys [allow-main?] :or {allow-main? false} :as options}]
(cond
(nil? shape)
nil
(if (:component-id shape)
shape
(if-let [parent-id (:parent-id shape)]
(get-component-shape objects (get objects parent-id))
nil))))
(and (not (ctk/in-component-copy? shape)) (not allow-main?))
nil
(ctk/instance-root? shape)
shape
:else
(get-component-shape objects (get objects (:parent-id shape)) options))))
(defn make-component-shape
"Clone the shape and all children. Generate new ids and detach

View file

@ -281,6 +281,22 @@
(some used-in-container? (containers-seq file-data))))
(defn used-assets-changed-since
"Get a lazy sequence of all assets in the library that are in use by the file and have
been modified after the given date."
[file-data library since-date]
(letfn [(used-assets-shape [shape]
(concat
(ctkl/used-components-changed-since shape library since-date)
(ctcl/used-colors-changed-since shape library since-date)
(ctyl/used-typographies-changed-since shape library since-date)))
(used-assets-container [container]
(->> (mapcat used-assets-shape (ctn/shapes-seq container))
(map #(cons (:id container) %))))]
(mapcat used-assets-container (containers-seq file-data))))
(defn get-or-add-library-page
"If exists a page named 'Library backup', get the id and calculate the position to start
adding new components. If not, create it and start at (0, 0)."
@ -370,7 +386,7 @@
root-to-board
(fn [shape]
(cond-> shape
(and (ctk/instance-root? shape)
(and (ctk/instance-head? shape)
(not= (:type shape) :frame))
(assoc :type :frame
:fills []
@ -548,75 +564,81 @@
(defn dump-tree
([file-data page-id libraries]
(dump-tree file-data page-id libraries false false))
(dump-tree file-data page-id libraries false false false))
([file-data page-id libraries show-ids]
(dump-tree file-data page-id libraries show-ids false))
(dump-tree file-data page-id libraries show-ids false false))
([file-data page-id libraries show-ids show-touched]
(dump-tree file-data page-id libraries show-ids show-touched false))
([file-data page-id libraries show-ids show-touched show-modified]
(let [page (ctpl/get-page file-data page-id)
objects (:objects page)
components (ctkl/components file-data)
root (d/seek #(nil? (:parent-id %)) (vals objects))]
(letfn [(show-shape [shape-id level objects]
(let [shape (get objects shape-id)]
(println (str/pad (str (str/repeat " " level)
(when (:main-instance? shape) "{")
(:name shape)
(when (:main-instance? shape) "}")
(when (seq (:touched shape)) "*")
(when show-ids (str/format " <%s>" (:id shape))))
{:length 20
:type :right})
(show-component-info shape objects))
(when show-touched
(when (seq (:touched shape))
(println (str (str/repeat " " level)
" "
(str (:touched shape)))))
(when (:remote-synced? shape)
(println (str (str/repeat " " level)
" (remote-synced)"))))
(when (:shapes shape)
(dorun (for [shape-id (:shapes shape)]
(show-shape shape-id (inc level) objects))))))
(let [shape (get objects shape-id)]
(println (str/pad (str (str/repeat " " level)
(when (:main-instance? shape) "{")
(:name shape)
(when (:main-instance? shape) "}")
(when (seq (:touched shape)) "*")
(when show-ids (str/format " <%s>" (:id shape))))
{:length 20
:type :right})
(show-component-info shape objects))
(when show-touched
(when (seq (:touched shape))
(println (str (str/repeat " " level)
" "
(str (:touched shape)))))
(when (:remote-synced? shape)
(println (str (str/repeat " " level)
" (remote-synced)"))))
(when (:shapes shape)
(dorun (for [shape-id (:shapes shape)]
(show-shape shape-id (inc level) objects))))))
(show-component-info [shape objects]
(if (nil? (:shape-ref shape))
(if (:component-root? shape) " #" "")
(let [root-shape (ctn/get-component-shape objects shape)
component-id (when root-shape (:component-id root-shape))
component-file-id (when root-shape (:component-file root-shape))
component-file (when component-file-id (get libraries component-file-id nil))
component (when component-id
(if component-file
(ctkl/get-component (:data component-file) component-id)
(get components component-id)))
component-shape (when component
(if component-file
(get-ref-shape (:data component-file) component shape)
(get-ref-shape file-data component shape)))]
(if (nil? (:shape-ref shape))
(if (:component-root? shape) " #" "")
(let [root-shape (ctn/get-component-shape objects shape)
component-id (when root-shape (:component-id root-shape))
component-file-id (when root-shape (:component-file root-shape))
component-file (when component-file-id (get libraries component-file-id nil))
component (when component-id
(if component-file
(ctkl/get-component (:data component-file) component-id)
(get components component-id)))
component-shape (when component
(if component-file
(get-ref-shape (:data component-file) component shape)
(get-ref-shape file-data component shape)))]
(str/format " %s--> %s%s%s"
(cond (:component-root? shape) "#"
(:component-id shape) "@"
:else "-")
(when component-file (str/format "<%s> " (:name component-file)))
(or (:name component-shape) "?")
(if (or (:component-root? shape)
(nil? (:component-id shape))
true)
""
(let [component-id (:component-id shape)
component-file-id (:component-file shape)
component-file (when component-file-id (get libraries component-file-id nil))
component (if component-file
(ctkl/get-component (:data component-file) component-id)
(get components component-id))]
(str/format " (%s%s)"
(when component-file (str/format "<%s> " (:name component-file)))
(:name component))))))))
(str/format " %s--> %s%s%s"
(cond (:component-root? shape) "#"
(:component-id shape) "@"
:else "-")
(when component-file (str/format "<%s> " (:name component-file)))
(or (:name component-shape) "?")
(if (or (:component-root? shape)
(nil? (:component-id shape))
true)
""
(let [component-id (:component-id shape)
component-file-id (:component-file shape)
component-file (when component-file-id (get libraries component-file-id nil))
component (if component-file
(ctkl/get-component (:data component-file) component-id)
(get components component-id))]
(str/format " (%s%s)"
(when component-file (str/format "<%s> " (:name component-file)))
(:name component))))))))
(show-component-instance [component]
(let [page (get-component-page file-data component)
@ -633,7 +655,10 @@
(dorun (for [component (vals components)]
(do
(println)
(println (str/format "[%s]" (:name component)))
(println (str/format "[%s]%s%s"
(:name component)
(when show-ids (str " " (:id component)))
(when show-modified (str " " (:modified-at component)))))
(when (:objects component)
(show-shape (:id component) 0 (:objects component)))
(when (:main-instance-page component)

View file

@ -118,8 +118,8 @@
(filter cph/frame-shape?))]
(->> (keys objects)
(into [] xform))))
(remove #(or (and skip-components? (ctk/instance-root? %))
(and skip-copies? (and (ctk/instance-root? %) (not (ctk/main-instance? %)))))))))
(remove #(or (and skip-components? (ctk/instance-head? %))
(and skip-copies? (and (ctk/instance-head? %) (not (ctk/main-instance? %)))))))))
(defn get-frames-ids
"Retrieves all frame ids as vector"

View file

@ -4,25 +4,53 @@
;;
;; Copyright (c) KALEIDOS INC
(ns app.common.types.typographies-list)
(ns app.common.types.typographies-list
(:require
[app.common.data :as d]
[app.common.text :as txt]
[app.common.time :as dt]))
(defn typographies-seq
[file-data]
(vals (:typographies file-data)))
(defn- touch
[typography]
(assoc typography :modified-at (dt/now)))
(defn add-typography
[file-data typography]
(update file-data :typographies assoc (:id typography) typography))
(update file-data :typographies assoc (:id typography) (touch typography)))
(defn get-typography
[file-data typography-id]
(get-in file-data [:typographies typography-id]))
(defn get-ref-typography
[library-data typography]
(when (= (:typography-ref-file typography) (:id library-data))
(get-typography library-data (:typography-ref-id typography))))
(defn set-typography
[file-data typography]
(d/assoc-in-when file-data [:typographies (:id typography)] (touch typography)))
(defn update-typography
[file-data typography-id f]
(update-in file-data [:typographies typography-id] f))
[file-data typography-id f & args]
(d/update-in-when file-data [:typographies typography-id] #(-> (apply f % args)
(touch))))
(defn delete-typography
[file-data typography-id]
(update file-data :typographies dissoc typography-id))
(defn used-typographies-changed-since
"Find all usages of any typography in the library by the given shape, of
typographies that have ben modified after the date.."
[shape library since-date]
(->> shape
:content
txt/node-seq
(keep #(get-ref-typography (:data library) %))
(remove #(< (:modified-at %) since-date)) ;; Note that :modified-at may be nil
(map #(vector (:id shape) (:id %) :typography))))

View file

@ -6,6 +6,7 @@
(ns app.common.types.typography
(:require
[app.common.spec :as us]
[app.common.text :as txt]
[clojure.spec.alpha :as s]))
@ -21,6 +22,7 @@
(s/def ::line-height string?)
(s/def ::letter-spacing string?)
(s/def ::text-transform string?)
(s/def ::modified-at ::us/inst)
(s/def ::typography
(s/keys :req-un [::id
@ -34,7 +36,8 @@
::line-height
::letter-spacing
::text-transform]
:opt-un [::path]))
:opt-un [::path
::modified-at]))
(defn uses-library-typographies?
"Check if the shape uses any typography in the given library."

View file

@ -305,10 +305,10 @@ isexe@^2.0.0:
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=
luxon@^3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/luxon/-/luxon-3.1.1.tgz#b492c645b2474fb86f3bd3283213846b99c32c1e"
integrity sha512-Ah6DloGmvseB/pX1cAmjbFvyU/pKuwQMQqz7d0yvuDlVYLTs2WeDHQMpC8tGjm1da+BriHROW/OEIT/KfYg6xw==
luxon@^3.3.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/luxon/-/luxon-3.3.0.tgz#d73ab5b5d2b49a461c47cedbc7e73309b4805b48"
integrity sha512-An0UCfG/rSiqtAIiBPO0Y9/zAnHUZxAMiCpTd5h2smgsj7GGmcenvrvww2cqNA8/4A5ZrD1gJpHN2mIHZQF+Mg==
md5.js@^1.3.4:
version "1.3.5"

View file

@ -59,7 +59,7 @@
"highlight.js": "^11.7.0",
"js-beautify": "^1.14.7",
"jszip": "^3.10.1",
"luxon": "^3.1.1",
"luxon": "^3.3.0",
"mousetrap": "^1.6.5",
"opentype.js": "^1.3.4",
"postcss-modules": "^6.0.0",

View file

@ -260,12 +260,11 @@
ptk/WatchEvent
(watch [_ state _]
(let [ignore-until (-> state :workspace-file :ignore-sync-until)
file-id (-> state :workspace-file :id)
needs-update? (some #(and (> (:modified-at %) (:synced-at %))
(or (not ignore-until)
(> (:modified-at %) ignore-until)))
libraries)]
(let [file-data (:workspace-data state)
ignore-until (dm/get-in state [:workspace-file :ignore-sync-until])
file-id (dm/get-in state [:workspace-file :id])
needs-update? (seq (filter #(dwl/assets-need-sync % file-data ignore-until)
libraries))]
(when needs-update?
(rx/of (dwl/notify-sync-file file-id)))))))
@ -1633,7 +1632,7 @@
;; Check if the shape is an instance whose master is defined in a
;; library that is not linked to the current file
(foreign-instance? [shape paste-objects state]
(let [root (cph/get-root-shape paste-objects shape)
(let [root (ctn/get-component-shape paste-objects shape)
root-file-id (:component-file root)]
(and (some? root)
(not= root-file-id (:current-file-id state))

View file

@ -91,7 +91,7 @@
;; Shapes that are in a component, but are not root, must be detached,
;; because they will be now children of a non instance group.
shapes-to-detach (filter ctk/in-component-instance-not-root? shapes)
shapes-to-detach (filter ctk/in-component-copy-not-root? shapes)
;; Look at the `get-empty-groups-after-group-creation`
;; docstring to understand the real purpose of this code
@ -124,7 +124,7 @@
;; Shapes that are in a component (including root) must be detached,
;; because cannot be easyly synchronized back to the main component.
shapes-to-detach (filter ctk/in-component-instance?
shapes-to-detach (filter ctk/in-component-copy?
(cph/get-children-with-self objects (:id group)))]
(-> (pcb/empty-changes it page-id)

View file

@ -605,7 +605,7 @@
container (cph/get-container local-file :page page-id)
shape (ctn/get-shape container id)]
(when (ctk/in-component-instance? shape)
(when (ctk/instance-head? shape)
(let [libraries (wsh/get-libraries state)
changes
@ -801,6 +801,8 @@
(rx/of (dch/commit-changes (assoc changes :file-id file-id))))))))
(def ignore-sync
"Mark the file as ignore syncs. All library changes before this moment will not
ber notified to sync."
(ptk/reify ::ignore-sync
ptk/UpdateEvent
(update [_ state]
@ -812,13 +814,26 @@
{:file-id (get-in state [:workspace-file :id])
:date (dt/now)}))))
(defn assets-need-sync
"Get a lazy sequence of all the assets of each type in the library that have
been modified after the last sync of the library. The sync date may be
overriden by providing a ignore-until parameter.
The sequence items are tuples of (page-id shape-id asset-id asset-type)."
([library file-data] (assets-need-sync library file-data nil))
([library file-data ignore-until]
(let [sync-date (max (:synced-at library) (or ignore-until 0))]
(when (> (:modified-at library) sync-date)
(ctf/used-assets-changed-since file-data library sync-date)))))
(defn notify-sync-file
[file-id]
(us/assert ::us/uuid file-id)
(ptk/reify ::notify-sync-file
ptk/WatchEvent
(watch [_ state _]
(let [libraries-need-sync (filter #(> (:modified-at %) (:synced-at %))
(let [file-data (:workspace-data state)
libraries-need-sync (filter #(seq (assets-need-sync % file-data))
(vals (get state :workspace-libraries)))
do-update #(do (apply st/emit! (map (fn [library]
(sync-file (:current-file-id state)
@ -828,14 +843,15 @@
do-dismiss #(do (st/emit! ignore-sync)
(st/emit! dm/hide))]
(rx/of (dm/info-dialog
(tr "workspace.updates.there-are-updates")
:inline-actions
[{:label (tr "workspace.updates.update")
:callback do-update}
{:label (tr "workspace.updates.dismiss")
:callback do-dismiss}]
:sync-dialog))))))
(when (seq libraries-need-sync)
(rx/of (dm/info-dialog
(tr "workspace.updates.there-are-updates")
:inline-actions
[{:label (tr "workspace.updates.update")
:callback do-update}
{:label (tr "workspace.updates.dismiss")
:callback do-dismiss}]
:sync-dialog)))))))
(defn watch-component-changes
"Watch the state for changes that affect to any main instance. If a change is detected will throw

View file

@ -99,7 +99,7 @@
(if (and (= (count shapes) 1)
(or (and (= (:type (first shapes)) :group) (not components-v2))
(= (:type (first shapes)) :frame))
(not (ctk/instance-root? (first shapes))))
(not (ctk/instance-head? (first shapes))))
[(first shapes) (-> (pcb/empty-changes it page-id)
(pcb/with-objects objects))]
(let [root-name (if (= 1 (count shapes))
@ -111,7 +111,7 @@
page-id
shapes
root-name
(not (ctk/instance-root? (first shapes))))
(not (ctk/instance-head? (first shapes))))
(prepare-create-board changes
(uuid/next)
(:parent-id (first shapes))
@ -203,7 +203,7 @@
(defn- generate-detach-recursive
[changes container shape-id first]
(let [shape (ctn/get-shape container shape-id)]
(if (and (ctk/instance-root? shape) (not first))
(if (and (ctk/instance-head? shape) (not first))
;; Subinstances are not detached, but converted in top instances
(pcb/update-shapes changes [(:id shape)] #(assoc % :component-root? true))
;; Otherwise, detach the shape and all children
@ -530,7 +530,7 @@
[changes libraries container shape-id reset? components-v2]
(log/debug :msg "Sync shape direct" :shape (str shape-id) :reset? reset?)
(let [shape-inst (ctn/get-shape container shape-id)]
(if (ctk/in-component-instance? shape-inst)
(if (ctk/in-component-copy? shape-inst)
(let [library (dm/get-in libraries [(:component-file shape-inst) :data])
component (or (ctkl/get-component library (:component-id shape-inst))
(and reset?

View file

@ -15,6 +15,7 @@
[app.common.pages.common :as cpc]
[app.common.pages.helpers :as cph]
[app.common.spec :as us]
[app.common.types.container :as ctn]
[app.common.types.modifiers :as ctm]
[app.common.types.shape.layout :as ctl]
[app.main.constants :refer [zoom-half-pixel-precision]]
@ -54,7 +55,7 @@
shape
(nil? root)
(cph/get-root-shape objects shape)
(ctn/get-component-shape objects shape {:allow-main? true})
:else root)
@ -64,7 +65,7 @@
transformed-shape
(nil? transformed-root)
(as-> (cph/get-root-shape objects transformed-shape) $
(as-> (ctn/get-component-shape objects transformed-shape {:allow-main? true}) $
(gsh/transform-shape (merge $ (get modif-tree (:id $)))))
:else transformed-root)

View file

@ -189,7 +189,7 @@
;; but hidden (to be able to recover them more easily).
(let [shape (get objects shape-id)
component-shape (ctn/get-component-shape objects shape)]
(and (ctk/in-component-instance? shape)
(and (ctk/in-component-copy? shape)
(not= shape component-shape)
(not (ctk/main-instance? component-shape)))))

View file

@ -14,7 +14,7 @@
(mf/defc element-icon
[{:keys [shape main-instance?] :as props}]
(if (ctk/instance-root? shape)
(if (ctk/instance-head? shape)
(if main-instance?
i/component
i/component-copy)

View file

@ -14,7 +14,7 @@
(mf/defc element-icon-refactor
[{:keys [shape main-instance?] :as props}]
(if (ctk/instance-root? shape)
(if (ctk/instance-head? shape)
(if main-instance?
i/component-refactor
i/copy-refactor)

View file

@ -438,7 +438,7 @@
has-component? (some true? (map #(contains? % :component-id) shapes))
is-component? (and single? (-> shapes first :component-id some?))
is-non-root? (and single? (ctk/in-component-instance-not-root? (first shapes)))
is-non-root? (and single? (ctk/in-component-copy-not-root? (first shapes)))
first-shape (first shapes)
{:keys [shape-id component-id component-file main-instance?]} first-shape

View file

@ -224,7 +224,7 @@
#(as-> (get objects %) obj
(and (cph/root-frame? obj)
(d/not-empty? (:shapes obj))
(not (ctk/instance-root? obj))
(not (ctk/instance-head? obj))
(not (ctk/main-instance? obj))))
;; Set with the elements to remove from the hover list

View file

@ -22,12 +22,13 @@
["date-fns/locale/ru" :default dateFnsLocalesRu]
["date-fns/locale/tr" :default dateFnsLocalesTr]
["date-fns/locale/zh-CN" :default dateFnsLocalesZhCn]
["luxon" :as lxn]
[app.common.data.macros :as dm]
[app.common.time :as common-time]
[app.util.object :as obj]
[cuerdas.core :as str]))
(def DateTime lxn/DateTime)
(def Duration lxn/Duration)
(dm/export common-time/DateTime)
(dm/export common-time/Duration)
(defprotocol ITimeMath
(plus [_ o])
@ -89,9 +90,7 @@
:rfc2822 (.fromRFC2822 ^js DateTime s #js {:zone zone :setZone force-zone})
:http (.fromHTTP ^js DateTime s #js {:zone zone :setZone force-zone})))))
(defn now
[]
(.local ^js DateTime))
(dm/export common-time/now)
(defn utc-now
[]

View file

@ -295,18 +295,20 @@
nil))
(defn dump-tree'
([state] (dump-tree' state false false))
([state show-ids] (dump-tree' state show-ids false))
([state show-ids show-touched]
([state] (dump-tree' state false false false))
([state show-ids] (dump-tree' state show-ids false false))
([state show-ids show-touched] (dump-tree' state show-ids show-touched false))
([state show-ids show-touched show-modified]
(let [page-id (get state :current-page-id)
file-data (get state :workspace-data)
libraries (get state :workspace-libraries)]
(ctf/dump-tree file-data page-id libraries show-ids show-touched))))
(ctf/dump-tree file-data page-id libraries show-ids show-touched show-modified))))
(defn ^:export dump-tree
([] (dump-tree' @st/state))
([show-ids] (dump-tree' @st/state show-ids))
([show-ids show-touched] (dump-tree' @st/state show-ids show-touched)))
([show-ids] (dump-tree' @st/state show-ids false false))
([show-ids show-touched] (dump-tree' @st/state show-ids show-touched false))
([show-ids show-touched show-modified] (dump-tree' @st/state show-ids show-touched show-modified)))
(when *assert*
(defonce debug-subscription

View file

@ -3342,10 +3342,10 @@ lru-queue@^0.1.0:
dependencies:
es5-ext "~0.10.2"
luxon@^3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/luxon/-/luxon-3.1.1.tgz#b492c645b2474fb86f3bd3283213846b99c32c1e"
integrity sha512-Ah6DloGmvseB/pX1cAmjbFvyU/pKuwQMQqz7d0yvuDlVYLTs2WeDHQMpC8tGjm1da+BriHROW/OEIT/KfYg6xw==
luxon@^3.3.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/luxon/-/luxon-3.3.0.tgz#d73ab5b5d2b49a461c47cedbc7e73309b4805b48"
integrity sha512-An0UCfG/rSiqtAIiBPO0Y9/zAnHUZxAMiCpTd5h2smgsj7GGmcenvrvww2cqNA8/4A5ZrD1gJpHN2mIHZQF+Mg==
make-iterator@^1.0.0:
version "1.0.1"