mirror of
https://github.com/penpot/penpot.git
synced 2026-01-15 09:50:23 -05:00
Compare commits
124 Commits
piweek-web
...
pre-lazy-l
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5621c2c394 | ||
|
|
a506be2897 | ||
|
|
74447442b8 | ||
|
|
62b1dc2a4b | ||
|
|
88779dd50b | ||
|
|
ae4f14ece2 | ||
|
|
ad185c4215 | ||
|
|
1a1e9b4ecd | ||
|
|
63b264b494 | ||
|
|
fca33f8451 | ||
|
|
d7fded19aa | ||
|
|
7f47131499 | ||
|
|
85829e53af | ||
|
|
16b37230cc | ||
|
|
c2f48e4075 | ||
|
|
6e5d5cfc50 | ||
|
|
32439a52db | ||
|
|
ce8c17e589 | ||
|
|
407e7186a4 | ||
|
|
a589d79043 | ||
|
|
5ce362df8e | ||
|
|
2120b40abe | ||
|
|
705a1c8b10 | ||
|
|
a9cafdfc9d | ||
|
|
0cb80febf0 | ||
|
|
804fe018ef | ||
|
|
026c32fe00 | ||
|
|
96d9786f83 | ||
|
|
9a5c220c87 | ||
|
|
4a2fb6facd | ||
|
|
eb575e9daf | ||
|
|
a7fc53f325 | ||
|
|
24bb49d0bf | ||
|
|
67d3a7f9c5 | ||
|
|
1efc40b6c4 | ||
|
|
304f6ea96e | ||
|
|
2509ab3a5d | ||
|
|
163ce9f3b7 | ||
|
|
0643ba03a1 | ||
|
|
49d719fb45 | ||
|
|
4fc892a856 | ||
|
|
88c7ac379b | ||
|
|
ccf063b8ef | ||
|
|
96f5a33f5f | ||
|
|
ecee15af5b | ||
|
|
607c3c4517 | ||
|
|
7648836725 | ||
|
|
6b12645bfb | ||
|
|
92934c6cdd | ||
|
|
52c849ce4b | ||
|
|
f8dd86da34 | ||
|
|
15f81e557c | ||
|
|
60fc1a48a5 | ||
|
|
51b3556b45 | ||
|
|
89c14b25ab | ||
|
|
051c0dce78 | ||
|
|
a9dd55b8d2 | ||
|
|
ca50486639 | ||
|
|
0ad2e8a0f2 | ||
|
|
ac20451ae7 | ||
|
|
47baa21d53 | ||
|
|
eee28a5793 | ||
|
|
da15924de0 | ||
|
|
eabed4325a | ||
|
|
f01cad9ce7 | ||
|
|
78260fbc42 | ||
|
|
7b36a7df8b | ||
|
|
74d985db13 | ||
|
|
6b042be65c | ||
|
|
86a4833c4a | ||
|
|
e4d86cbb39 | ||
|
|
611594a392 | ||
|
|
bdb1742d59 | ||
|
|
ba01f314dd | ||
|
|
517c913af9 | ||
|
|
08b9178a65 | ||
|
|
b19a6321de | ||
|
|
2dbe7bca07 | ||
|
|
fd5fd87360 | ||
|
|
a4919e3b53 | ||
|
|
419cc2e848 | ||
|
|
db713c2d61 | ||
|
|
b5296613de | ||
|
|
05614a345d | ||
|
|
4832b718da | ||
|
|
625f99c933 | ||
|
|
6ed1d223bf | ||
|
|
39856c8f6a | ||
|
|
e2446fcc62 | ||
|
|
9834195f0e | ||
|
|
ffa37d26fc | ||
|
|
db3d7af0b8 | ||
|
|
85c301c26b | ||
|
|
18d954faba | ||
|
|
7d98833e4e | ||
|
|
aa3fe1cd2b | ||
|
|
e884cba002 | ||
|
|
030ff398ed | ||
|
|
5522430afe | ||
|
|
6969f8be03 | ||
|
|
9ac8e72b23 | ||
|
|
02986f81bd | ||
|
|
22f4ee82bb | ||
|
|
600b5ecdb7 | ||
|
|
527d7afb00 | ||
|
|
d9fa8bbb06 | ||
|
|
780edaac3b | ||
|
|
0b6633dc44 | ||
|
|
ed2461c3ec | ||
|
|
ae535b8ea1 | ||
|
|
7c2fa2392f | ||
|
|
736a26a46a | ||
|
|
23853345cc | ||
|
|
69cffe43f3 | ||
|
|
93df5354e5 | ||
|
|
161e8b01a5 | ||
|
|
958b442b2e | ||
|
|
d3404bd359 | ||
|
|
b49ba9572e | ||
|
|
6a397eb262 | ||
|
|
c09ca021e9 | ||
|
|
85fbc0352c | ||
|
|
271384718d | ||
|
|
ff8b6fbd8c |
@@ -15,7 +15,7 @@
|
||||
:hooks
|
||||
{:analyze-call
|
||||
{app.common.data.macros/export hooks.export/export
|
||||
potok.core/reify hooks.export/potok-reify
|
||||
potok.v2.core/reify hooks.export/potok-reify
|
||||
app.util.services/defmethod hooks.export/service-defmethod
|
||||
app.common.record/defrecord hooks.export/penpot-defrecord
|
||||
app.db/with-atomic hooks.export/penpot-with-atomic
|
||||
|
||||
12
CHANGES.md
12
CHANGES.md
@@ -1,5 +1,17 @@
|
||||
# CHANGELOG
|
||||
|
||||
## :rocket: Next
|
||||
|
||||
### :boom: Breaking changes & Deprecations
|
||||
|
||||
### :sparkles: New features
|
||||
|
||||
### :bug: Bugs fixed
|
||||
|
||||
### :arrow_up: Deps updates
|
||||
|
||||
### :heart: Community contributions by (Thank you!)
|
||||
|
||||
## 1.20.0
|
||||
|
||||
### :boom: Breaking changes & Deprecations
|
||||
|
||||
@@ -30,7 +30,6 @@
|
||||
<Logger name="app.redis" level="info" />
|
||||
<Logger name="app.rpc.rlimit" level="info" />
|
||||
<Logger name="app.rpc.climit" level="info" />
|
||||
<Logger name="app.rpc.mutations.files" level="info" />
|
||||
<Logger name="app.common.files.migrations" level="info" />
|
||||
|
||||
<Logger name="app.loggers" level="debug" additivity="false">
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.exceptions :as ex]
|
||||
[app.common.features :as cfeat]
|
||||
[app.common.files.changes :as cp]
|
||||
[app.common.files.changes-builder :as fcb]
|
||||
[app.common.files.helpers :as cfh]
|
||||
@@ -769,58 +768,58 @@
|
||||
fdata (migrate-graphics fdata)]
|
||||
(update fdata :options assoc :components-v2 true)))))
|
||||
|
||||
(defn- process-fdata
|
||||
[fdata id]
|
||||
(-> fdata
|
||||
(assoc :id id)
|
||||
(fdata/process-pointers deref)
|
||||
(fmg/migrate-data)))
|
||||
(defn- get-file
|
||||
[system id]
|
||||
(binding [pmap/*load-fn* (partial fdata/load-pointer system id)]
|
||||
(-> (files/get-file system id :migrate? false)
|
||||
(update :data assoc :id id)
|
||||
(update :data fdata/process-pointers deref)
|
||||
(fmg/migrate-file))))
|
||||
|
||||
(defn- validate-file!
|
||||
[file libs throw-on-validate?]
|
||||
(try
|
||||
(cfv/validate-file! file libs)
|
||||
(cfv/validate-file-schema! file)
|
||||
(catch Throwable cause
|
||||
(if throw-on-validate?
|
||||
(throw cause)
|
||||
(l/wrn :hint "migrate:file:validation-error" :cause cause)))))
|
||||
|
||||
(defn- process-file
|
||||
[{:keys [::db/conn] :as system} id & {:keys [validate? throw-on-validate?]}]
|
||||
(binding [pmap/*tracked* (pmap/create-tracked)
|
||||
pmap/*load-fn* (partial fdata/load-pointer *system* id)]
|
||||
(let [file (get-file system id)
|
||||
|
||||
(let [file (binding [cfeat/*new* (atom #{})]
|
||||
(-> (files/get-file system id :migrate? false)
|
||||
(update :data process-fdata id)
|
||||
(update :features into (deref cfeat/*new*))
|
||||
(update :features cfeat/migrate-legacy-features)))
|
||||
libs (->> (files/get-file-libraries conn id)
|
||||
(into [file] (comp (map :id) (map (partial get-file system))))
|
||||
(d/index-by :id))
|
||||
|
||||
libs (->> (files/get-file-libraries conn id)
|
||||
(into [file] (map (fn [{:keys [id]}]
|
||||
(binding [pmap/*load-fn* (partial fdata/load-pointer system id)]
|
||||
(-> (files/get-file system id :migrate? false)
|
||||
(update :data process-fdata id))))))
|
||||
(d/index-by :id))
|
||||
file (-> file
|
||||
(update :data migrate-fdata libs)
|
||||
(update :features conj "components/v2"))
|
||||
|
||||
pmap? (contains? (:features file) "fdata/pointer-map")
|
||||
_ (when validate?
|
||||
(validate-file! file libs throw-on-validate?))
|
||||
|
||||
file (-> file
|
||||
(update :data migrate-fdata libs)
|
||||
(update :features conj "components/v2")
|
||||
(cond-> pmap? (fdata/enable-pointer-map)))
|
||||
]
|
||||
file (if (contains? (:features file) "fdata/objects-map")
|
||||
(fdata/enable-objects-map file)
|
||||
file)
|
||||
|
||||
(when validate?
|
||||
(if throw-on-validate?
|
||||
(cfv/validate-file! file libs)
|
||||
(doseq [error (cfv/validate-file file libs)]
|
||||
(l/wrn :hint "migrate:file:validation-error"
|
||||
:file-id (str (:id file))
|
||||
:file-name (:name file)
|
||||
:error error))))
|
||||
file (if (contains? (:features file) "fdata/pointer-map")
|
||||
(binding [pmap/*tracked* (pmap/create-tracked)]
|
||||
(let [file (fdata/enable-pointer-map file)]
|
||||
(fdata/persist-pointers! system id)
|
||||
file))
|
||||
file)]
|
||||
|
||||
(db/update! conn :file
|
||||
{:data (blob/encode (:data file))
|
||||
:features (db/create-array conn "text" (:features file))
|
||||
:revn (:revn file)}
|
||||
{:id (:id file)})
|
||||
(db/update! conn :file
|
||||
{:data (blob/encode (:data file))
|
||||
:features (db/create-array conn "text" (:features file))
|
||||
:revn (:revn file)}
|
||||
{:id (:id file)}
|
||||
{::db/return-keys? false})
|
||||
|
||||
(when pmap?
|
||||
(fdata/persist-pointers! system id))
|
||||
|
||||
(dissoc file :data))))
|
||||
(dissoc file :data)))
|
||||
|
||||
(defn migrate-file!
|
||||
[system file-id & {:keys [validate? throw-on-validate?]}]
|
||||
|
||||
@@ -81,6 +81,7 @@
|
||||
(cond
|
||||
(or (= code :spec-validation)
|
||||
(= code :params-validation)
|
||||
(= code :schema-validation)
|
||||
(= code :data-validation))
|
||||
(let [explain (ex/explain data)]
|
||||
{::rres/status 400
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
[app.common.exceptions :as ex]
|
||||
[app.common.features :as cfeat]
|
||||
[app.common.files.defaults :as cfd]
|
||||
[app.common.files.migrations :as pmg]
|
||||
[app.common.files.migrations :as fmg]
|
||||
[app.common.files.validate :as fval]
|
||||
[app.common.fressian :as fres]
|
||||
[app.common.logging :as l]
|
||||
@@ -693,17 +693,6 @@
|
||||
(vswap! *state* update :index update-index files)
|
||||
(vswap! *state* assoc :version version :files files)))
|
||||
|
||||
(defn- postprocess-file
|
||||
[file]
|
||||
(cond-> file
|
||||
(and (contains? cfeat/*current* "fdata/objects-map")
|
||||
(not (contains? cfeat/*previous* "fdata/objects-map")))
|
||||
(feat.fdata/enable-objects-map)
|
||||
|
||||
(and (contains? cfeat/*current* "fdata/pointer-map")
|
||||
(not (contains? cfeat/*previous* "fdata/pointer-map")))
|
||||
(feat.fdata/enable-pointer-map)))
|
||||
|
||||
(defn- get-remaped-thumbnails
|
||||
[thumbnails file-id]
|
||||
(mapv (fn [thumbnail]
|
||||
@@ -712,6 +701,31 @@
|
||||
(update :object-id #(str/replace-first % #"^(.*?)/" (str file-id "/")))))
|
||||
thumbnails))
|
||||
|
||||
(defn- process-file
|
||||
[{:keys [id] :as file}]
|
||||
(-> file
|
||||
(update :data (fn [fdata]
|
||||
(-> fdata
|
||||
(assoc :id id)
|
||||
(dissoc :recent-colors)
|
||||
(cond-> (> (:version fdata) cfd/version)
|
||||
(assoc :version cfd/version))
|
||||
;; FIXME: We're temporarily activating all
|
||||
;; migrations because a problem in the
|
||||
;; environments messed up with the version
|
||||
;; numbers When this problem is fixed delete
|
||||
;; the following line
|
||||
(cond-> (> (:version fdata) 22)
|
||||
(assoc :version 22)))))
|
||||
(fmg/migrate-file)
|
||||
(update :data (fn [fdata]
|
||||
(-> fdata
|
||||
(update :pages-index relink-shapes)
|
||||
(update :components relink-shapes)
|
||||
(update :media relink-media)
|
||||
(d/without-nils))))))
|
||||
|
||||
|
||||
(defmethod read-section :v1/files
|
||||
[{:keys [::db/conn ::input ::project-id ::enabled-features ::timestamp ::overwrite?] :as system}]
|
||||
|
||||
@@ -722,19 +736,7 @@
|
||||
file-id (:id file)
|
||||
file-id' (lookup-index file-id)
|
||||
|
||||
thumbnails (:thumbnails file)
|
||||
file (update file :features cfeat/migrate-legacy-features)
|
||||
|
||||
features (-> enabled-features
|
||||
(set/difference cfeat/frontend-only-features)
|
||||
(set/union (cfeat/check-supported-features! (:features file))))]
|
||||
|
||||
;; All features that are enabled and requires explicit migration
|
||||
;; are added to the state for a posterior migration step
|
||||
(doseq [feature (-> enabled-features
|
||||
(set/difference cfeat/no-migration-features)
|
||||
(set/difference (:features file)))]
|
||||
(vswap! *state* update :pending-to-migrate (fnil conj []) [feature file-id']))
|
||||
thumbnails (:thumbnails file)]
|
||||
|
||||
(when (not= file-id expected-file-id)
|
||||
(ex/raise :type :validation
|
||||
@@ -765,63 +767,62 @@
|
||||
(l/dbg :hint "update media references" ::l/sync? true)
|
||||
(vswap! *state* update :media into (map #(update % :id lookup-index)) media))
|
||||
|
||||
(binding [cfeat/*current* features
|
||||
cfeat/*previous* (:features file)
|
||||
pmap/*tracked* (atom {})]
|
||||
(let [file (-> file
|
||||
(assoc :id file-id')
|
||||
(process-file))
|
||||
|
||||
(let [params (-> file
|
||||
(assoc :id file-id')
|
||||
(assoc :features features)
|
||||
(assoc :project-id project-id)
|
||||
(assoc :created-at timestamp)
|
||||
(assoc :modified-at timestamp)
|
||||
(dissoc :thumbnails)
|
||||
(update :data (fn [data]
|
||||
(-> data
|
||||
(dissoc :recent-colors)
|
||||
(assoc :id file-id')
|
||||
(cond-> (> (:version data) cfd/version)
|
||||
(assoc :version cfd/version))
|
||||
;; All features that are enabled and requires explicit migration are
|
||||
;; added to the state for a posterior migration step.
|
||||
_ (doseq [feature (-> enabled-features
|
||||
(set/difference cfeat/no-migration-features)
|
||||
(set/difference (:features file)))]
|
||||
(vswap! *state* update :pending-to-migrate (fnil conj []) [feature file-id']))
|
||||
|
||||
;; FIXME: We're temporarily activating all
|
||||
;; migrations because a problem in the
|
||||
;; environments messed up with the version
|
||||
;; numbers When this problem is fixed delete
|
||||
;; the following line
|
||||
(assoc :version 22)
|
||||
(update :pages-index relink-shapes)
|
||||
(update :components relink-shapes)
|
||||
(update :media relink-media)
|
||||
(pmg/migrate-data)
|
||||
(d/without-nils)))))
|
||||
file (-> file
|
||||
(assoc :project-id project-id)
|
||||
(assoc :created-at timestamp)
|
||||
(assoc :modified-at timestamp)
|
||||
(dissoc :thumbnails)
|
||||
(update :features
|
||||
(fn [features]
|
||||
(let [features (cfeat/check-supported-features! features)]
|
||||
(-> enabled-features
|
||||
(set/difference cfeat/frontend-only-features)
|
||||
(set/union features))))))
|
||||
|
||||
params (if (contains? cf/flags :file-schema-validation)
|
||||
(fval/validate-file-schema! params)
|
||||
params)
|
||||
_ (when (contains? cf/flags :file-schema-validation)
|
||||
(fval/validate-file-schema! file))
|
||||
|
||||
_ (when (contains? cf/flags :soft-file-schema-validation)
|
||||
(try
|
||||
(fval/validate-file-schema! params)
|
||||
(catch Throwable cause
|
||||
(l/error :hint "file schema validation error" :cause cause))))
|
||||
_ (when (contains? cf/flags :soft-file-schema-validation)
|
||||
(let [result (ex/try! (fval/validate-file-schema! file))]
|
||||
(when (ex/exception? result)
|
||||
(l/error :hint "file schema validation error" :cause result))))
|
||||
|
||||
params (-> params
|
||||
(postprocess-file)
|
||||
(update :features #(db/create-array conn "text" %))
|
||||
(update :data blob/encode))]
|
||||
file (if (contains? (:features file) "fdata/objects-map")
|
||||
(feat.fdata/enable-objects-map file)
|
||||
file)
|
||||
|
||||
file (if (contains? (:features file) "fdata/pointer-map")
|
||||
(binding [pmap/*tracked* (pmap/create-tracked)]
|
||||
(let [file (feat.fdata/enable-pointer-map file)]
|
||||
(feat.fdata/persist-pointers! system file-id')
|
||||
file))
|
||||
file)
|
||||
|
||||
file (-> file
|
||||
(update :features #(db/create-array conn "text" %))
|
||||
(update :data blob/encode))]
|
||||
|
||||
(l/dbg :hint "create file" :id (str file-id') ::l/sync? true)
|
||||
|
||||
(if overwrite?
|
||||
(create-or-update-file! conn params)
|
||||
(db/insert! conn :file params))
|
||||
|
||||
(feat.fdata/persist-pointers! system file-id')
|
||||
(create-or-update-file! conn file)
|
||||
(db/insert! conn :file file))
|
||||
|
||||
(when overwrite?
|
||||
(db/delete! conn :file-thumbnail {:file-id file-id'}))
|
||||
|
||||
file-id')))))
|
||||
file-id'))))
|
||||
|
||||
(defmethod read-section :v1/rels
|
||||
[{:keys [::db/conn ::input ::timestamp]}]
|
||||
|
||||
@@ -34,7 +34,6 @@
|
||||
[app.util.pointer-map :as pmap]
|
||||
[app.util.services :as sv]
|
||||
[app.util.time :as dt]
|
||||
[clojure.set :as set]
|
||||
[clojure.spec.alpha :as s]
|
||||
[cuerdas.core :as str]))
|
||||
|
||||
@@ -225,9 +224,9 @@
|
||||
(defn- migrate-file
|
||||
[{:keys [::db/conn] :as cfg} {:keys [id] :as file}]
|
||||
(binding [pmap/*load-fn* (partial feat.fdata/load-pointer cfg id)
|
||||
pmap/*tracked* (pmap/create-tracked)
|
||||
cfeat/*new* (atom #{})]
|
||||
pmap/*tracked* (pmap/create-tracked)]
|
||||
(let [file (fmg/migrate-file file)]
|
||||
|
||||
;; NOTE: when file is migrated, we break the rule of no perform
|
||||
;; mutations on get operations and update the file with all
|
||||
;; migrations applied
|
||||
@@ -235,23 +234,26 @@
|
||||
;; NOTE: the following code will not work on read-only mode, it
|
||||
;; is a known issue; we keep is not implemented until we really
|
||||
;; need this
|
||||
(if (fmg/migrated? file)
|
||||
(let [file (update file :features cfeat/migrate-legacy-features)
|
||||
features (set/union (deref cfeat/*new*) (:features file))]
|
||||
(db/update! conn :file
|
||||
{:data (blob/encode (:data file))
|
||||
:features (db/create-array conn "text" features)}
|
||||
{:id id})
|
||||
(feat.fdata/persist-pointers! cfg id)
|
||||
(assoc file :features features))
|
||||
file))))
|
||||
(when (fmg/migrated? file)
|
||||
(db/update! conn :file
|
||||
{:data (blob/encode (:data file))
|
||||
:features (db/create-array conn "text" (:features file))}
|
||||
{:id id}
|
||||
{::db/return-keys? false})
|
||||
|
||||
(when (contains? (:features file) "fdata/pointer-map")
|
||||
(feat.fdata/persist-pointers! cfg id)))
|
||||
|
||||
file)))
|
||||
|
||||
(defn get-file
|
||||
[{:keys [::db/conn] :as cfg} id & {:keys [project-id migrate?
|
||||
[{:keys [::db/conn] :as cfg} id & {:keys [project-id
|
||||
migrate?
|
||||
include-deleted?
|
||||
lock-for-update?]
|
||||
:or {include-deleted? false
|
||||
lock-for-update? false}}]
|
||||
lock-for-update? false
|
||||
migrate? true}}]
|
||||
(dm/assert!
|
||||
"expected cfg with valid connection"
|
||||
(db/connection-map? cfg))
|
||||
@@ -706,11 +708,12 @@
|
||||
(cfeat/check-client-features! (:features params))
|
||||
(cfeat/check-file-features! (:features file) (:features params)))
|
||||
|
||||
{:name (:name file)
|
||||
:components-count (count (ctkl/components-seq (:data file)))
|
||||
:graphics-count (count (get-in file [:data :media] []))
|
||||
:colors-count (count (get-in file [:data :colors] []))
|
||||
:typography-count (count (get-in file [:data :typographies] []))}))
|
||||
(binding [pmap/*load-fn* (partial feat.fdata/load-pointer cfg id)]
|
||||
{:name (:name file)
|
||||
:components-count (count (ctkl/components-seq (:data file)))
|
||||
:graphics-count (count (get-in file [:data :media] []))
|
||||
:colors-count (count (get-in file [:data :colors] []))
|
||||
:typography-count (count (get-in file [:data :typographies] []))})))
|
||||
|
||||
(sv/defmethod ::get-file-summary
|
||||
"Retrieve a file summary by its ID. Only authenticated users."
|
||||
@@ -856,8 +859,10 @@
|
||||
(true? (:is-shared params)))
|
||||
(let [file (assoc file :is-shared true)]
|
||||
(db/update! conn :file
|
||||
{:is-shared false}
|
||||
{:id id})
|
||||
{:is-shared true}
|
||||
{:id id}
|
||||
::db/return-keys? false)
|
||||
|
||||
file)
|
||||
|
||||
:else
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.features :as cfeat]
|
||||
[app.common.files.helpers :as cfh]
|
||||
[app.common.files.migrations :as fmg]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.common.schema :as sm]
|
||||
[app.common.thumbnails :as thc]
|
||||
@@ -105,24 +106,12 @@
|
||||
(letfn [;; function responsible on finding the frame marked to be
|
||||
;; used as thumbnail; the returned frame always have
|
||||
;; the :page-id set to the page that it belongs.
|
||||
|
||||
(get-thumbnail-frame [file]
|
||||
;; NOTE: this is a hack for avoid perform blocking
|
||||
;; operation inside the for loop, clojure lazy-seq uses
|
||||
;; synchronized blocks that does not plays well with
|
||||
;; virtual threads where all rpc methods calls are
|
||||
;; dispatched, so we need to perform the load operation
|
||||
;; first. This operation forces all pointer maps load into
|
||||
;; the memory.
|
||||
;;
|
||||
;; FIXME: this is no longer true with clojure>=1.12
|
||||
(let [{:keys [data]} (update file :data feat.fdata/process-pointers pmap/load!)]
|
||||
;; Then proceed to find the frame set for thumbnail
|
||||
(d/seek #(or (:use-for-thumbnail %)
|
||||
(:use-for-thumbnail? %)) ; NOTE: backward comp (remove on v1.21)
|
||||
(for [page (-> data :pages-index vals)
|
||||
frame (-> page :objects ctt/get-frames)]
|
||||
(assoc frame :page-id (:id page))))))
|
||||
(get-thumbnail-frame [{:keys [data]}]
|
||||
(d/seek #(or (:use-for-thumbnail %)
|
||||
(:use-for-thumbnail? %)) ; NOTE: backward comp (remove on v1.21)
|
||||
(for [page (-> data :pages-index vals)
|
||||
frame (-> page :objects ctt/get-frames)]
|
||||
(assoc frame :page-id (:id page)))))
|
||||
|
||||
;; function responsible to filter objects data structure of
|
||||
;; all unneeded shapes if a concrete frame is provided. If no
|
||||
@@ -166,30 +155,29 @@
|
||||
|
||||
objects)))]
|
||||
|
||||
(binding [pmap/*load-fn* (partial feat.fdata/load-pointer cfg id)]
|
||||
(let [frame (get-thumbnail-frame file)
|
||||
frame-id (:id frame)
|
||||
page-id (or (:page-id frame)
|
||||
(-> data :pages first))
|
||||
(let [frame (get-thumbnail-frame file)
|
||||
frame-id (:id frame)
|
||||
page-id (or (:page-id frame)
|
||||
(-> data :pages first))
|
||||
|
||||
page (dm/get-in data [:pages-index page-id])
|
||||
page (cond-> page (pmap/pointer-map? page) deref)
|
||||
frame-ids (if (some? frame) (list frame-id) (map :id (ctt/get-frames (:objects page))))
|
||||
page (dm/get-in data [:pages-index page-id])
|
||||
page (cond-> page (pmap/pointer-map? page) deref)
|
||||
frame-ids (if (some? frame) (list frame-id) (map :id (ctt/get-frames (:objects page))))
|
||||
|
||||
obj-ids (map #(thc/fmt-object-id (:id file) page-id % "frame") frame-ids)
|
||||
thumbs (get-object-thumbnails conn id obj-ids)]
|
||||
obj-ids (map #(thc/fmt-object-id (:id file) page-id % "frame") frame-ids)
|
||||
thumbs (get-object-thumbnails conn id obj-ids)]
|
||||
|
||||
(cond-> page
|
||||
;; If we have frame, we need to specify it on the page level
|
||||
;; and remove the all other unrelated objects.
|
||||
(some? frame-id)
|
||||
(-> (assoc :thumbnail-frame-id frame-id)
|
||||
(update :objects filter-objects frame-id))
|
||||
(cond-> page
|
||||
;; If we have frame, we need to specify it on the page level
|
||||
;; and remove the all other unrelated objects.
|
||||
(some? frame-id)
|
||||
(-> (assoc :thumbnail-frame-id frame-id)
|
||||
(update :objects filter-objects frame-id))
|
||||
|
||||
;; Assoc the available thumbnails and prune not visible shapes
|
||||
;; for avoid transfer unnecessary data.
|
||||
:always
|
||||
(update :objects assoc-thumbnails page-id thumbs))))))
|
||||
;; Assoc the available thumbnails and prune not visible shapes
|
||||
;; for avoid transfer unnecessary data.
|
||||
:always
|
||||
(update :objects assoc-thumbnails page-id thumbs)))))
|
||||
|
||||
(def ^:private
|
||||
schema:get-file-data-for-thumbnail
|
||||
@@ -221,7 +209,10 @@
|
||||
:profile-id profile-id
|
||||
:file-id file-id)
|
||||
|
||||
file (files/get-file cfg file-id)]
|
||||
file (binding [pmap/*load-fn* (partial feat.fdata/load-pointer cfg file-id)]
|
||||
(-> (files/get-file cfg file-id :migrate? false)
|
||||
(update :data feat.fdata/process-pointers deref)
|
||||
(fmg/migrate-file)))]
|
||||
|
||||
(-> (cfeat/get-team-enabled-features cf/flags team)
|
||||
(cfeat/check-client-features! (:features params))
|
||||
|
||||
@@ -182,39 +182,39 @@
|
||||
(defn update-file
|
||||
[{:keys [::db/conn ::mtx/metrics] :as cfg}
|
||||
{:keys [id file features changes changes-with-metadata] :as params}]
|
||||
(binding [cfeat/*current* features
|
||||
cfeat/*previous* (:features file)]
|
||||
(let [update-fn (cond-> update-file*
|
||||
(contains? features "fdata/pointer-map")
|
||||
(wrap-with-pointer-map-context)
|
||||
(let [features (-> features
|
||||
(set/difference cfeat/frontend-only-features)
|
||||
(set/union (:features file)))
|
||||
|
||||
(contains? features "fdata/objects-map")
|
||||
(wrap-with-objects-map-context))
|
||||
update-fn (cond-> update-file*
|
||||
(contains? features "fdata/pointer-map")
|
||||
(wrap-with-pointer-map-context)
|
||||
|
||||
changes (if changes-with-metadata
|
||||
(->> changes-with-metadata (mapcat :changes) vec)
|
||||
(vec changes))
|
||||
(contains? features "fdata/objects-map")
|
||||
(wrap-with-objects-map-context))
|
||||
|
||||
features (-> features
|
||||
(set/difference cfeat/frontend-only-features)
|
||||
(set/union (:features file)))]
|
||||
changes (if changes-with-metadata
|
||||
(->> changes-with-metadata (mapcat :changes) vec)
|
||||
(vec changes))]
|
||||
|
||||
(when (> (:revn params)
|
||||
(:revn file))
|
||||
(ex/raise :type :validation
|
||||
:code :revn-conflict
|
||||
:hint "The incoming revision number is greater that stored version."
|
||||
:context {:incoming-revn (:revn params)
|
||||
:stored-revn (:revn file)}))
|
||||
(when (> (:revn params)
|
||||
(:revn file))
|
||||
(ex/raise :type :validation
|
||||
:code :revn-conflict
|
||||
:hint "The incoming revision number is greater that stored version."
|
||||
:context {:incoming-revn (:revn params)
|
||||
:stored-revn (:revn file)}))
|
||||
|
||||
(mtx/run! metrics {:id :update-file-changes :inc (count changes)})
|
||||
(mtx/run! metrics {:id :update-file-changes :inc (count changes)})
|
||||
|
||||
(when (not= features (:features file))
|
||||
(let [features (db/create-array conn "text" features)]
|
||||
(db/update! conn :file
|
||||
{:features features}
|
||||
{:id id})))
|
||||
(when (not= features (:features file))
|
||||
(let [features (db/create-array conn "text" features)]
|
||||
(db/update! conn :file
|
||||
{:features features}
|
||||
{:id id})))
|
||||
|
||||
(binding [cfeat/*current* features
|
||||
cfeat/*previous* (:features file)]
|
||||
(let [file (assoc file :features features)
|
||||
params (-> params
|
||||
(assoc :file file)
|
||||
@@ -276,9 +276,7 @@
|
||||
(try
|
||||
(val/validate-file-schema! file)
|
||||
(catch Throwable cause
|
||||
(l/error :hint "file schema validation error" :cause cause)))
|
||||
|
||||
file)
|
||||
(l/error :hint "file schema validation error" :cause cause))))
|
||||
|
||||
(defn- soft-validate-file!
|
||||
[file libs]
|
||||
@@ -286,8 +284,7 @@
|
||||
(val/validate-file! file libs)
|
||||
(catch Throwable cause
|
||||
(l/error :hint "file validation error"
|
||||
:cause cause)))
|
||||
file)
|
||||
:cause cause))))
|
||||
|
||||
(defn- update-file-data
|
||||
[{:keys [::db/conn] :as cfg} file changes skip-validate]
|
||||
@@ -300,7 +297,8 @@
|
||||
|
||||
;; WARNING: this ruins performance; maybe we need to find
|
||||
;; some other way to do general validation
|
||||
libs (when (and (contains? cf/flags :file-validation)
|
||||
libs (when (and (or (contains? cf/flags :file-validation)
|
||||
(contains? cf/flags :soft-file-validation))
|
||||
(not skip-validate))
|
||||
(->> (files/get-file-libraries conn (:id file))
|
||||
(into [file] (map (fn [{:keys [id]}]
|
||||
@@ -309,37 +307,37 @@
|
||||
(-> (files/get-file cfg id :migrate? false)
|
||||
(feat.fdata/process-pointers deref) ; ensure all pointers resolved
|
||||
(fmg/migrate-file))))))
|
||||
(d/index-by :id)))]
|
||||
(d/index-by :id)))
|
||||
|
||||
(-> (files/check-version! file)
|
||||
(update :revn inc)
|
||||
(update :data cpc/process-changes changes)
|
||||
file (-> (files/check-version! file)
|
||||
(update :revn inc)
|
||||
(update :data cpc/process-changes changes))]
|
||||
|
||||
;; If `libs` is defined, then full validation is performed
|
||||
(cond-> (contains? cf/flags :soft-file-validation)
|
||||
(soft-validate-file! libs))
|
||||
(when (contains? cf/flags :soft-file-validation)
|
||||
(soft-validate-file! file libs))
|
||||
|
||||
(cond-> (contains? cf/flags :soft-file-schema-validation)
|
||||
(soft-validate-file-schema!))
|
||||
(when (contains? cf/flags :soft-file-schema-validation)
|
||||
(soft-validate-file-schema! file))
|
||||
|
||||
(cond-> (and (contains? cf/flags :file-validation)
|
||||
(not skip-validate))
|
||||
(val/validate-file! libs))
|
||||
(when (and (contains? cf/flags :file-validation)
|
||||
(not skip-validate))
|
||||
(val/validate-file! file libs))
|
||||
|
||||
(cond-> (and (contains? cf/flags :file-schema-validation)
|
||||
(not skip-validate))
|
||||
(val/validate-file-schema!))
|
||||
(when (and (contains? cf/flags :file-schema-validation)
|
||||
(not skip-validate))
|
||||
(val/validate-file-schema! file))
|
||||
|
||||
(cond-> (and (contains? cfeat/*current* "fdata/objects-map")
|
||||
(not (contains? cfeat/*previous* "fdata/objects-map")))
|
||||
(feat.fdata/enable-objects-map))
|
||||
(cond-> file
|
||||
(and (contains? cfeat/*current* "fdata/objects-map")
|
||||
(not (contains? cfeat/*previous* "fdata/objects-map")))
|
||||
(feat.fdata/enable-objects-map)
|
||||
|
||||
(cond-> (and (contains? cfeat/*current* "fdata/pointer-map")
|
||||
(not (contains? cfeat/*previous* "fdata/pointer-map")))
|
||||
(feat.fdata/enable-pointer-map))
|
||||
|
||||
(update :data blob/encode))))
|
||||
(and (contains? cfeat/*current* "fdata/pointer-map")
|
||||
(not (contains? cfeat/*previous* "fdata/pointer-map")))
|
||||
(feat.fdata/enable-pointer-map)
|
||||
|
||||
:always
|
||||
(update :data blob/encode))))
|
||||
|
||||
(defn- take-snapshot?
|
||||
"Defines the rule when file `data` snapshot should be saved."
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
[app.common.files.migrations :as pmg]
|
||||
[app.common.schema :as sm]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.config :as cf]
|
||||
[app.db :as db]
|
||||
[app.features.fdata :as feat.fdata]
|
||||
[app.http.sse :as sse]
|
||||
@@ -105,27 +106,34 @@
|
||||
media
|
||||
media))
|
||||
|
||||
(update-fdata [fdata new-id]
|
||||
(-> fdata
|
||||
(assoc :id new-id)
|
||||
(pmg/migrate-data)
|
||||
(update :pages-index relink-shapes)
|
||||
(update :components relink-shapes)
|
||||
(update :media relink-media)
|
||||
(d/without-nils)
|
||||
(feat.fdata/process-pointers pmap/clone)))]
|
||||
(process-file [{:keys [id] :as file}]
|
||||
(-> file
|
||||
(update :data assoc :id id)
|
||||
(update :data feat.fdata/process-pointers deref)
|
||||
(pmg/migrate-file)
|
||||
(update :data (fn [data]
|
||||
(-> data
|
||||
(update :pages-index relink-shapes)
|
||||
(update :components relink-shapes)
|
||||
(update :media relink-media)
|
||||
(d/without-nils))))))]
|
||||
|
||||
(binding [pmap/*load-fn* (partial feat.fdata/load-pointer cfg id)
|
||||
pmap/*tracked* (pmap/create-tracked)
|
||||
cfeat/*new* (atom #{})]
|
||||
(let [new-id (get index id)
|
||||
file (-> file
|
||||
(assoc :id new-id)
|
||||
(update :data update-fdata new-id)
|
||||
(update :features into (deref cfeat/*new*))
|
||||
(update :features cfeat/migrate-legacy-features))]
|
||||
(feat.fdata/persist-pointers! cfg new-id)
|
||||
file))))
|
||||
(let [new-id (get index id)
|
||||
file (binding [pmap/*load-fn* (partial feat.fdata/load-pointer cfg id)]
|
||||
(-> (assoc file :id new-id)
|
||||
(process-file)))
|
||||
|
||||
file (if (contains? (:features file) "fdata/objects-map")
|
||||
(feat.fdata/enable-objects-map file)
|
||||
file)
|
||||
|
||||
file (if (contains? (:features file) "fdata/pointer-map")
|
||||
(binding [pmap/*tracked* (pmap/create-tracked)]
|
||||
(let [file (feat.fdata/enable-pointer-map file)]
|
||||
(feat.fdata/persist-pointers! cfg (:id file))
|
||||
file))
|
||||
file)]
|
||||
file)))
|
||||
|
||||
(def sql:get-used-libraries
|
||||
"select flr.*
|
||||
@@ -190,20 +198,22 @@
|
||||
(db/insert! conn :file
|
||||
(-> file
|
||||
(update :features #(db/create-array conn "text" %))
|
||||
(update :data blob/encode)))
|
||||
(update :data blob/encode))
|
||||
{::db/return-keys? false})
|
||||
|
||||
(db/insert! conn :file-profile-rel
|
||||
{:file-id (:id file)
|
||||
:profile-id profile-id
|
||||
:is-owner true
|
||||
:is-admin true
|
||||
:can-edit true})
|
||||
:can-edit true}
|
||||
{::db/return-keys? false})
|
||||
|
||||
(doseq [params flibs]
|
||||
(db/insert! conn :file-library-rel params))
|
||||
(db/insert! conn :file-library-rel params ::db/return-keys? false))
|
||||
|
||||
(doseq [params fmeds]
|
||||
(db/insert! conn :file-media-object params))
|
||||
(db/insert! conn :file-media-object params ::db/return-keys? false))
|
||||
|
||||
file))
|
||||
|
||||
@@ -283,7 +293,7 @@
|
||||
;; --- COMMAND: Move file
|
||||
|
||||
(def sql:get-files
|
||||
"select id, project_id from file where id = ANY(?)")
|
||||
"select id, features, project_id from file where id = ANY(?)")
|
||||
|
||||
(def sql:move-files
|
||||
"update file set project_id = ? where id = ANY(?)")
|
||||
@@ -307,7 +317,8 @@
|
||||
[{:keys [::db/conn] :as cfg} {:keys [profile-id ids project-id] :as params}]
|
||||
|
||||
(let [fids (db/create-array conn "uuid" ids)
|
||||
files (db/exec! conn [sql:get-files fids])
|
||||
files (->> (db/exec! conn [sql:get-files fids])
|
||||
(map files/decode-row))
|
||||
source (into #{} (map :project-id) files)
|
||||
pids (->> (conj source project-id)
|
||||
(db/create-array conn "uuid"))]
|
||||
@@ -327,7 +338,12 @@
|
||||
;; Check the team compatibility
|
||||
(let [orig-team (teams/get-team conn :profile-id profile-id :project-id (first source))
|
||||
dest-team (teams/get-team conn :profile-id profile-id :project-id project-id)]
|
||||
(cfeat/check-teams-compatibility! orig-team dest-team))
|
||||
(cfeat/check-teams-compatibility! orig-team dest-team)
|
||||
|
||||
;; Check if all pending to move files are compaib
|
||||
(let [features (cfeat/get-team-enabled-features cf/flags dest-team)]
|
||||
(doseq [file files]
|
||||
(cfeat/check-file-features! features (:features file)))))
|
||||
|
||||
;; move all files to the project
|
||||
(db/exec-one! conn [sql:move-files project-id fids])
|
||||
@@ -384,7 +400,15 @@
|
||||
;; Check the teams compatibility
|
||||
(let [orig-team (teams/get-team conn :profile-id profile-id :team-id (:team-id project))
|
||||
dest-team (teams/get-team conn :profile-id profile-id :team-id team-id)]
|
||||
(cfeat/check-teams-compatibility! orig-team dest-team))
|
||||
(cfeat/check-teams-compatibility! orig-team dest-team)
|
||||
|
||||
;; Check if all pending to move files are compaib
|
||||
(let [features (cfeat/get-team-enabled-features cf/flags dest-team)]
|
||||
(doseq [file (->> (db/query conn :file
|
||||
{:project-id project-id}
|
||||
{:columns [:features]})
|
||||
(map files/decode-row))]
|
||||
(cfeat/check-file-features! features (:features file)))))
|
||||
|
||||
;; move project to the destination team
|
||||
(db/update! conn :project
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
[app.common.spec :as us]
|
||||
[app.db :as db]
|
||||
[app.rpc :as-alias rpc]
|
||||
[app.rpc.commands.files :refer [resolve-public-uri]]
|
||||
[app.rpc.doc :as-alias doc]
|
||||
[app.util.services :as sv]
|
||||
[clojure.spec.alpha :as s]))
|
||||
@@ -37,12 +38,15 @@
|
||||
)
|
||||
select distinct
|
||||
f.id,
|
||||
f.revn,
|
||||
f.project_id,
|
||||
f.created_at,
|
||||
f.modified_at,
|
||||
f.name,
|
||||
f.is_shared
|
||||
f.is_shared,
|
||||
ft.media_id
|
||||
from file as f
|
||||
left join file_thumbnail as ft on (ft.file_id = f.id and ft.revn = f.revn)
|
||||
inner join projects as pr on (f.project_id = pr.id)
|
||||
where f.name ilike ('%' || ? || '%')
|
||||
and (f.deleted_at is null or f.deleted_at > now())
|
||||
@@ -50,10 +54,16 @@
|
||||
|
||||
(defn search-files
|
||||
[conn profile-id team-id search-term]
|
||||
(db/exec! conn [sql:search-files
|
||||
profile-id team-id
|
||||
profile-id team-id
|
||||
search-term]))
|
||||
(->> (db/exec! conn [sql:search-files
|
||||
profile-id team-id
|
||||
profile-id team-id
|
||||
search-term])
|
||||
(mapv (fn [row]
|
||||
(if-let [media-id (:media-id row)]
|
||||
(-> row
|
||||
(dissoc :media-id)
|
||||
(assoc :thumbnail-uri (resolve-public-uri media-id)))
|
||||
(dissoc row :media-id))))))
|
||||
|
||||
(s/def ::team-id ::us/uuid)
|
||||
(s/def ::search-files ::us/string)
|
||||
|
||||
@@ -105,7 +105,7 @@
|
||||
(pmg/migrate-file))))))
|
||||
(d/index-by :id))
|
||||
errors (validate/validate-file file libs)
|
||||
changes (-> (repair/repair-file (:data file) libs errors) :redo-changes)
|
||||
changes (repair/repair-file file libs errors)
|
||||
|
||||
file (-> file
|
||||
(update :revn inc)
|
||||
|
||||
@@ -103,7 +103,7 @@
|
||||
|
||||
{:on-open
|
||||
(fn on-open [channel]
|
||||
(l/trace :fn "on-open" :conn-id id :channel channel)
|
||||
(l/dbg :fn "on-open" :conn-id (str id))
|
||||
(let [options (-> options
|
||||
(assoc ::channel channel)
|
||||
(on-connect))
|
||||
@@ -114,10 +114,10 @@
|
||||
|
||||
:on-close
|
||||
(fn on-close [_channel code reason]
|
||||
(l/info :fn "on-ws-terminate"
|
||||
:conn-id id
|
||||
:code code
|
||||
:reason reason)
|
||||
(l/dbg :fn "on-close"
|
||||
:conn-id (str id)
|
||||
:code code
|
||||
:reason reason)
|
||||
(sp/close! close-ch))
|
||||
|
||||
:on-error
|
||||
@@ -132,18 +132,19 @@
|
||||
|
||||
:on-pong
|
||||
(fn on-pong [_channel data]
|
||||
(l/trace :fn "on-pong" :data data)
|
||||
(sp/put! hbeat-ch data))}))
|
||||
|
||||
(defn- handle-ping!
|
||||
[{:keys [::id ::beats ::channel] :as wsp} beat-id]
|
||||
(l/trace :hint "send ping" :beat beat-id :conn-id id)
|
||||
(l/trc :hint "send ping" :beat beat-id :conn-id (str id))
|
||||
(rws/ping channel (encode-beat beat-id))
|
||||
(let [issued (swap! beats conj (long beat-id))]
|
||||
(not (>= (count issued) max-missed-heartbeats))))
|
||||
|
||||
(defn- start-io-loop!
|
||||
[{:keys [::id ::close-ch ::input-ch ::output-ch ::heartbeat-ch ::channel ::handler ::beats ::on-rcv-message ::on-snd-message] :as wsp}]
|
||||
[{:keys [::id ::close-ch ::input-ch ::output-ch ::heartbeat-ch
|
||||
::channel ::handler ::beats ::on-rcv-message ::on-snd-message]
|
||||
:as wsp}]
|
||||
(try
|
||||
(handler wsp {:type :open})
|
||||
(loop [i 0]
|
||||
@@ -154,14 +155,16 @@
|
||||
(identical? p ping-ch)
|
||||
(if (handle-ping! wsp i)
|
||||
(recur (inc i))
|
||||
(rws/close channel 8802 "missing to many pings"))
|
||||
(do
|
||||
(l/trc :hint "closing" :reason "missing to many pings")
|
||||
(rws/close channel 8802 "missing to many pings")))
|
||||
|
||||
(or (identical? p close-ch) (nil? msg))
|
||||
(do :nothing)
|
||||
|
||||
(identical? p heartbeat-ch)
|
||||
(let [beat (decode-beat msg)]
|
||||
;; (l/trace :hint "pong" :beat beat :conn-id id)
|
||||
(l/trc :hint "pong received" :beat beat :conn-id (str id))
|
||||
(swap! beats disj beat)
|
||||
(recur i))
|
||||
|
||||
@@ -179,7 +182,6 @@
|
||||
(identical? p output-ch)
|
||||
(let [message (on-snd-message msg)
|
||||
message (t/encode-str message {:type :json-verbose})]
|
||||
;; (l/trace :hint "writing message to output" :message msg)
|
||||
(rws/send channel message)
|
||||
(recur i))))))
|
||||
|
||||
@@ -188,12 +190,12 @@
|
||||
(catch java.io.IOException _)
|
||||
|
||||
(catch InterruptedException _cause
|
||||
(l/debug :hint "websocket thread interrumpted" :conn-id id))
|
||||
(l/dbg :hint "websocket thread interrumpted" :conn-id id))
|
||||
|
||||
(catch Throwable cause
|
||||
(l/error :hint "unhandled exception on websocket thread"
|
||||
:conn-id id
|
||||
:cause cause))
|
||||
(l/err :hint "unhandled exception on websocket thread"
|
||||
:conn-id id
|
||||
:cause cause))
|
||||
(finally
|
||||
(try
|
||||
(handler wsp {:type :close})
|
||||
@@ -212,4 +214,4 @@
|
||||
(catch Throwable cause
|
||||
(throw cause)))
|
||||
|
||||
(l/trace :hint "websocket thread terminated" :conn-id id))))
|
||||
(l/trc :hint "websocket thread terminated" :conn-id id))))
|
||||
|
||||
@@ -69,12 +69,12 @@
|
||||
(t/is (not= (:id file1) (:id result)))
|
||||
|
||||
;; Check that the new file has a correct file library relation
|
||||
(let [[item :as rows] (db/query th/*pool* :file-library-rel {:file-id (:id result)})]
|
||||
(let [[item :as rows] (th/db-query :file-library-rel {:file-id (:id result)})]
|
||||
(t/is (= 1 (count rows)))
|
||||
(t/is (= (:id file2) (:library-file-id item))))
|
||||
|
||||
;; Check that the new file has a correct file media objects
|
||||
(let [[item :as rows] (db/query th/*pool* :file-media-object {:file-id (:id result)})]
|
||||
(let [[item :as rows] (th/db-query :file-media-object {:file-id (:id result)})]
|
||||
(t/is (= 1 (count rows)))
|
||||
|
||||
;; Check that both items have different ids
|
||||
@@ -91,7 +91,7 @@
|
||||
(t/is (not (contains? (get-in result [:data :media]) (:id mobj)))))
|
||||
|
||||
;; Check the total number of files
|
||||
(let [rows (db/query th/*pool* :file {:project-id (:id project)})]
|
||||
(let [rows (th/db-query :file {:project-id (:id project)})]
|
||||
(t/is (= 3 (count rows))))))))
|
||||
|
||||
(t/deftest duplicate-file-with-deleted-relations
|
||||
@@ -139,15 +139,15 @@
|
||||
(t/is (not= (:id file1) (:id result)))
|
||||
|
||||
;; Check that there are no relation to a deleted library
|
||||
(let [[item :as rows] (db/query th/*pool* :file-library-rel {:file-id (:id result)})]
|
||||
(let [[item :as rows] (th/db-query :file-library-rel {:file-id (:id result)})]
|
||||
(t/is (= 0 (count rows))))
|
||||
|
||||
;; Check that the new file has no media objects
|
||||
(let [[item :as rows] (db/query th/*pool* :file-media-object {:file-id (:id result)})]
|
||||
(let [[item :as rows] (th/db-query :file-media-object {:file-id (:id result)})]
|
||||
(t/is (= 0 (count rows))))
|
||||
|
||||
;; Check the total number of files
|
||||
(let [rows (db/query th/*pool* :file {:project-id (:id project)})]
|
||||
(let [rows (th/db-query :file {:project-id (:id project)})]
|
||||
(t/is (= 3 (count rows))))))))
|
||||
|
||||
(t/deftest duplicate-project
|
||||
@@ -196,16 +196,16 @@
|
||||
(t/is (not= (:id project) (:id result)))
|
||||
|
||||
;; Check the total number of projects (previously is 2, now is 3)
|
||||
(let [rows (db/query th/*pool* :project {:team-id (:default-team-id profile)})]
|
||||
(let [rows (th/db-query :project {:team-id (:default-team-id profile)})]
|
||||
(t/is (= 3 (count rows))))
|
||||
|
||||
;; Check that the new project has the same files
|
||||
(let [p1-files (db/query th/*pool* :file
|
||||
{:project-id (:id project)}
|
||||
{:order-by [:name]})
|
||||
p2-files (db/query th/*pool* :file
|
||||
{:project-id (:id result)}
|
||||
{:order-by [:name]})]
|
||||
(let [p1-files (th/db-query :file
|
||||
{:project-id (:id project)}
|
||||
{:order-by [:name]})
|
||||
p2-files (th/db-query :file
|
||||
{:project-id (:id result)}
|
||||
{:order-by [:name]})]
|
||||
(t/is (= (count p1-files)
|
||||
(count p2-files)))
|
||||
|
||||
@@ -260,16 +260,16 @@
|
||||
(t/is (not= (:id project) (:id result)))
|
||||
|
||||
;; Check the total number of projects (previously is 2, now is 3)
|
||||
(let [rows (db/query th/*pool* :project {:team-id (:default-team-id profile)})]
|
||||
(let [rows (th/db-query :project {:team-id (:default-team-id profile)})]
|
||||
(t/is (= 3 (count rows))))
|
||||
|
||||
;; Check that the new project has only the second file
|
||||
(let [p1-files (db/query th/*pool* :file
|
||||
{:project-id (:id project)}
|
||||
{:order-by [:name]})
|
||||
p2-files (db/query th/*pool* :file
|
||||
{:project-id (:id result)}
|
||||
{:order-by [:name]})]
|
||||
(let [p1-files (th/db-query :file
|
||||
{:project-id (:id project)}
|
||||
{:order-by [:name]})
|
||||
p2-files (th/db-query :file
|
||||
{:project-id (:id result)}
|
||||
{:order-by [:name]})]
|
||||
(t/is (= (count (rest p1-files))
|
||||
(count p2-files)))
|
||||
|
||||
@@ -318,11 +318,11 @@
|
||||
(t/is (th/ex-of-code? error :cant-move-to-same-project)))
|
||||
|
||||
;; initially project1 should have 2 files
|
||||
(let [rows (db/query th/*pool* :file {:project-id (:id project1)})]
|
||||
(let [rows (th/db-query :file {:project-id (:id project1)})]
|
||||
(t/is (= 2 (count rows))))
|
||||
|
||||
;; initially project2 should be empty
|
||||
(let [rows (db/query th/*pool* :file {:project-id (:id project2)})]
|
||||
(let [rows (th/db-query :file {:project-id (:id project2)})]
|
||||
(t/is (= 0 (count rows))))
|
||||
|
||||
;; move a file1 to project2 (in the same team)
|
||||
@@ -337,21 +337,21 @@
|
||||
(t/is (nil? (:result out)))
|
||||
|
||||
;; project1 now should contain 1 file
|
||||
(let [rows (db/query th/*pool* :file {:project-id (:id project1)})]
|
||||
(let [rows (th/db-query :file {:project-id (:id project1)})]
|
||||
(t/is (= 1 (count rows))))
|
||||
|
||||
;; project2 now should contain 1 file
|
||||
(let [rows (db/query th/*pool* :file {:project-id (:id project2)})]
|
||||
(let [rows (th/db-query :file {:project-id (:id project2)})]
|
||||
(t/is (= 1 (count rows))))
|
||||
|
||||
;; file1 should be still linked to file2
|
||||
(let [[item :as rows] (db/query th/*pool* :file-library-rel {:file-id (:id file1)})]
|
||||
(let [[item :as rows] (th/db-query :file-library-rel {:file-id (:id file1)})]
|
||||
(t/is (= 1 (count rows)))
|
||||
(t/is (= (:file-id item) (:id file1)))
|
||||
(t/is (= (:library-file-id item) (:id file2))))
|
||||
|
||||
;; should be no libraries on file2
|
||||
(let [rows (db/query th/*pool* :file-library-rel {:file-id (:id file2)})]
|
||||
(let [rows (th/db-query :file-library-rel {:file-id (:id file2)})]
|
||||
(t/is (= 0 (count rows)))))))
|
||||
|
||||
|
||||
@@ -384,27 +384,27 @@
|
||||
;; --- initial data checks
|
||||
|
||||
;; the project1 should have 3 files
|
||||
(let [rows (db/query th/*pool* :file {:project-id (:id project1)})]
|
||||
(let [rows (th/db-query :file {:project-id (:id project1)})]
|
||||
(t/is (= 3 (count rows))))
|
||||
|
||||
;; should be no files on project2
|
||||
(let [rows (db/query th/*pool* :file {:project-id (:id project2)})]
|
||||
(let [rows (th/db-query :file {:project-id (:id project2)})]
|
||||
(t/is (= 0 (count rows))))
|
||||
|
||||
;; the file1 should be linked to file2
|
||||
(let [[item :as rows] (db/query th/*pool* :file-library-rel {:file-id (:id file1)})]
|
||||
(let [[item :as rows] (th/db-query :file-library-rel {:file-id (:id file1)})]
|
||||
(t/is (= 1 (count rows)))
|
||||
(t/is (= (:file-id item) (:id file1)))
|
||||
(t/is (= (:library-file-id item) (:id file2))))
|
||||
|
||||
;; the file2 should be linked to file3
|
||||
(let [[item :as rows] (db/query th/*pool* :file-library-rel {:file-id (:id file2)})]
|
||||
(let [[item :as rows] (th/db-query :file-library-rel {:file-id (:id file2)})]
|
||||
(t/is (= 1 (count rows)))
|
||||
(t/is (= (:file-id item) (:id file2)))
|
||||
(t/is (= (:library-file-id item) (:id file3))))
|
||||
|
||||
;; should be no libraries on file3
|
||||
(let [rows (db/query th/*pool* :file-library-rel {:file-id (:id file3)})]
|
||||
(let [rows (th/db-query :file-library-rel {:file-id (:id file3)})]
|
||||
(t/is (= 0 (count rows))))
|
||||
|
||||
;; move to other project in other team
|
||||
@@ -418,23 +418,23 @@
|
||||
(t/is (nil? (:result out)))
|
||||
|
||||
;; project1 now should have 2 file
|
||||
(let [[item1 item2 :as rows] (db/query th/*pool* :file {:project-id (:id project1)}
|
||||
{:order-by [:created-at]})]
|
||||
(let [[item1 item2 :as rows] (th/db-query :file {:project-id (:id project1)}
|
||||
{:order-by [:created-at]})]
|
||||
;; (clojure.pprint/pprint rows)
|
||||
(t/is (= 2 (count rows)))
|
||||
(t/is (= (:id item1) (:id file2))))
|
||||
|
||||
;; project2 now should have 1 file
|
||||
(let [[item :as rows] (db/query th/*pool* :file {:project-id (:id project2)})]
|
||||
(let [[item :as rows] (th/db-query :file {:project-id (:id project2)})]
|
||||
(t/is (= 1 (count rows)))
|
||||
(t/is (= (:id item) (:id file1))))
|
||||
|
||||
;; the moved file1 should not have any link to libraries
|
||||
(let [rows (db/query th/*pool* :file-library-rel {:file-id (:id file1)})]
|
||||
(let [rows (th/db-query :file-library-rel {:file-id (:id file1)})]
|
||||
(t/is (zero? (count rows))))
|
||||
|
||||
;; the file2 should still be linked to file3
|
||||
(let [[item :as rows] (db/query th/*pool* :file-library-rel {:file-id (:id file2)})]
|
||||
(let [[item :as rows] (th/db-query :file-library-rel {:file-id (:id file2)})]
|
||||
(t/is (= 1 (count rows)))
|
||||
(t/is (= (:file-id item) (:id file2)))
|
||||
(t/is (= (:library-file-id item) (:id file3)))))))
|
||||
@@ -462,21 +462,21 @@
|
||||
;; --- initial data checks
|
||||
|
||||
;; the project1 should have 2 files
|
||||
(let [rows (db/query th/*pool* :file {:project-id (:id project1)})]
|
||||
(let [rows (th/db-query :file {:project-id (:id project1)})]
|
||||
(t/is (= 2 (count rows))))
|
||||
|
||||
;; should be no files on project2
|
||||
(let [rows (db/query th/*pool* :file {:project-id (:id project2)})]
|
||||
(let [rows (th/db-query :file {:project-id (:id project2)})]
|
||||
(t/is (= 0 (count rows))))
|
||||
|
||||
;; the file1 should be linked to file2
|
||||
(let [[item :as rows] (db/query th/*pool* :file-library-rel {:file-id (:id file1)})]
|
||||
(let [[item :as rows] (th/db-query :file-library-rel {:file-id (:id file1)})]
|
||||
(t/is (= 1 (count rows)))
|
||||
(t/is (= (:file-id item) (:id file1)))
|
||||
(t/is (= (:library-file-id item) (:id file2))))
|
||||
|
||||
;; should be no libraries on file2
|
||||
(let [rows (db/query th/*pool* :file-library-rel {:file-id (:id file2)})]
|
||||
(let [rows (th/db-query :file-library-rel {:file-id (:id file2)})]
|
||||
(t/is (= 0 (count rows))))
|
||||
|
||||
;; move the library to other project
|
||||
@@ -490,22 +490,22 @@
|
||||
(t/is (nil? (:result out)))
|
||||
|
||||
;; project1 now should have 1 file
|
||||
(let [[item :as rows] (db/query th/*pool* :file {:project-id (:id project1)}
|
||||
{:order-by [:created-at]})]
|
||||
(let [[item :as rows] (th/db-query :file {:project-id (:id project1)}
|
||||
{:order-by [:created-at]})]
|
||||
(t/is (= 1 (count rows)))
|
||||
(t/is (= (:id item) (:id file1))))
|
||||
|
||||
;; project2 now should have 1 file
|
||||
(let [[item :as rows] (db/query th/*pool* :file {:project-id (:id project2)})]
|
||||
(let [[item :as rows] (th/db-query :file {:project-id (:id project2)})]
|
||||
(t/is (= 1 (count rows)))
|
||||
(t/is (= (:id item) (:id file2))))
|
||||
|
||||
;; the file1 should not have any link to libraries
|
||||
(let [rows (db/query th/*pool* :file-library-rel {:file-id (:id file1)})]
|
||||
(let [rows (th/db-query :file-library-rel {:file-id (:id file1)})]
|
||||
(t/is (zero? (count rows))))
|
||||
|
||||
;; the file2 should not have any link to libraries
|
||||
(let [rows (db/query th/*pool* :file-library-rel {:file-id (:id file2)})]
|
||||
(let [rows (th/db-query :file-library-rel {:file-id (:id file2)})]
|
||||
(t/is (zero? (count rows)))))))
|
||||
|
||||
(t/deftest move-project
|
||||
@@ -538,16 +538,17 @@
|
||||
;; --- initial data checks
|
||||
|
||||
;; the project1 should have 2 files
|
||||
(let [rows (db/query th/*pool* :file {:project-id (:id project1)})]
|
||||
(let [rows (th/db-query :file {:project-id (:id project1)})]
|
||||
(t/is (= 2 (count rows))))
|
||||
|
||||
;; the project2 should have 1 file
|
||||
(let [rows (db/query th/*pool* :file {:project-id (:id project2)})]
|
||||
(let [rows (th/db-query :file {:project-id (:id project2)})]
|
||||
(t/is (= 1 (count rows))))
|
||||
|
||||
;; the file1 should be linked to file2 and file3
|
||||
(let [[item1 item2 :as rows] (db/query th/*pool* :file-library-rel {:file-id (:id file1)}
|
||||
{:order-by [:created-at]})]
|
||||
(let [[item1 item2 :as rows] (th/db-query :file-library-rel
|
||||
{:file-id (:id file1)}
|
||||
{:order-by [:created-at]})]
|
||||
(t/is (= 2 (count rows)))
|
||||
(t/is (= (:file-id item1) (:id file1)))
|
||||
(t/is (= (:library-file-id item1) (:id file2)))
|
||||
@@ -555,15 +556,14 @@
|
||||
(t/is (= (:library-file-id item2) (:id file3))))
|
||||
|
||||
;; the file2 should not be linked to any file
|
||||
(let [[rows] (db/query th/*pool* :file-library-rel {:file-id (:id file2)})]
|
||||
(let [[rows] (th/db-query :file-library-rel {:file-id (:id file2)})]
|
||||
(t/is (= 0 (count rows))))
|
||||
|
||||
;; the file3 should not be linked to any file
|
||||
(let [[rows] (db/query th/*pool* :file-library-rel {:file-id (:id file3)})]
|
||||
(let [[rows] (th/db-query :file-library-rel {:file-id (:id file3)})]
|
||||
(t/is (= 0 (count rows))))
|
||||
|
||||
;; move project1 to other team
|
||||
;; TODO: correct team change of project
|
||||
(let [data {::th/type :move-project
|
||||
::rpc/profile-id (:id profile)
|
||||
:project-id (:id project1)
|
||||
@@ -574,21 +574,25 @@
|
||||
(t/is (nil? (:result out)))
|
||||
|
||||
;; project1 now should still have 2 files
|
||||
(let [[item1 item2 :as rows] (db/query th/*pool* :file {:project-id (:id project1)}
|
||||
{:order-by [:created-at]})]
|
||||
(let [[item1 item2 :as rows] (th/db-query :file
|
||||
{:project-id (:id project1)}
|
||||
{:order-by [:created-at]})]
|
||||
;; (clojure.pprint/pprint rows)
|
||||
(t/is (= 2 (count rows)))
|
||||
(t/is (= (:id item1) (:id file1)))
|
||||
(t/is (= (:id item2) (:id file2))))
|
||||
|
||||
;; project2 now should still have 1 file
|
||||
(let [[item :as rows] (db/query th/*pool* :file {:project-id (:id project2)})]
|
||||
(let [[item :as rows] (th/db-query :file {:project-id (:id project2)})]
|
||||
;; (pp/pprint rows)
|
||||
(t/is (= 1 (count rows)))
|
||||
(t/is (= (:id item) (:id file3))))
|
||||
|
||||
;; the file1 should be linked to file2 but not file3
|
||||
(let [[item1 :as rows] (db/query th/*pool* :file-library-rel {:file-id (:id file1)}
|
||||
{:order-by [:created-at]})]
|
||||
(let [[item1 :as rows] (th/db-query :file-library-rel
|
||||
{:file-id (:id file1)}
|
||||
{:order-by [:created-at]})]
|
||||
|
||||
(t/is (= 1 (count rows)))
|
||||
(t/is (= (:file-id item1) (:id file1)))
|
||||
(t/is (= (:library-file-id item1) (:id file2)))))))
|
||||
|
||||
@@ -51,7 +51,10 @@
|
||||
"layout/grid"})
|
||||
|
||||
;; A set of features enabled by default for each file, they are
|
||||
;; implicit and are enabled by default and can't be disabled
|
||||
;; implicit and are enabled by default and can't be disabled. The
|
||||
;; features listed in this set are mainly freatures addedby file
|
||||
;; migrations process, so all features referenced in migrations should
|
||||
;; be here.
|
||||
(def default-enabled-features
|
||||
#{"fdata/shape-data-type"})
|
||||
|
||||
@@ -190,7 +193,11 @@
|
||||
([enabled-features file-features]
|
||||
(check-file-features! enabled-features file-features #{}))
|
||||
([enabled-features file-features client-features]
|
||||
(let [file-features (into #{} xf-remove-ephimeral file-features)]
|
||||
(let [file-features (into #{} xf-remove-ephimeral file-features)
|
||||
;; We should ignore all features that does not match with the
|
||||
;; `no-migration-features` set because we can't enable them
|
||||
;; as-is, because they probably need migrations
|
||||
client-features (set/intersection client-features no-migration-features)]
|
||||
(let [not-supported (-> enabled-features
|
||||
(set/union client-features)
|
||||
(set/difference file-features)
|
||||
@@ -208,15 +215,11 @@
|
||||
|
||||
(check-supported-features! file-features)
|
||||
|
||||
(let [;; We should ignore all features that does not match with
|
||||
;; the `no-migration-features` set because we can't enable
|
||||
;; them as-is, because they probably need migrations
|
||||
client-features (set/intersection client-features no-migration-features)
|
||||
not-supported (-> file-features
|
||||
(set/difference enabled-features)
|
||||
(set/difference client-features)
|
||||
(set/difference backend-only-features)
|
||||
(set/difference frontend-only-features))]
|
||||
(let [not-supported (-> file-features
|
||||
(set/difference enabled-features)
|
||||
(set/difference client-features)
|
||||
(set/difference backend-only-features)
|
||||
(set/difference frontend-only-features))]
|
||||
|
||||
(when (seq not-supported)
|
||||
(ex/raise :type :restriction
|
||||
|
||||
@@ -358,13 +358,13 @@
|
||||
|
||||
(defn changed-attrs
|
||||
"Returns the list of attributes that will change when `update-fn` is applied"
|
||||
[object update-fn {:keys [attrs]}]
|
||||
[object objects update-fn {:keys [attrs]}]
|
||||
(let [changed?
|
||||
(fn [old new attr]
|
||||
(let [old-val (get old attr)
|
||||
new-val (get new attr)]
|
||||
(not= old-val new-val)))
|
||||
new-obj (update-fn object)]
|
||||
new-obj (update-fn object objects)]
|
||||
(when-not (= object new-obj)
|
||||
(let [attrs (or attrs (d/concat-set (keys object) (keys new-obj)))]
|
||||
(filter (partial changed? object new-obj) attrs)))))
|
||||
@@ -412,7 +412,7 @@
|
||||
update-shape
|
||||
(fn [changes id]
|
||||
(let [old-obj (get objects id)
|
||||
new-obj (update-fn old-obj)]
|
||||
new-obj (update-fn old-obj objects)]
|
||||
(if (= old-obj new-obj)
|
||||
changes
|
||||
(let [[rops uops] (-> (or attrs (d/concat-set (keys old-obj) (keys new-obj)))
|
||||
|
||||
@@ -42,18 +42,21 @@
|
||||
(reduce migrate-fn data (range (:version data 0) to-version))))))
|
||||
|
||||
(defn migrate-file
|
||||
[{:keys [id data] :as file}]
|
||||
(let [data (assoc data :id id)]
|
||||
(-> file
|
||||
(assoc ::orig-version (:version data))
|
||||
(assoc :data (migrate-data data)))))
|
||||
[{:keys [id data features] :as file}]
|
||||
(binding [cfeat/*new* (atom #{})]
|
||||
(let [file (-> file
|
||||
(update :data assoc :id id)
|
||||
(update :data migrate-data)
|
||||
(update :features (fnil into #{}) (deref cfeat/*new*))
|
||||
(update :features cfeat/migrate-legacy-features))]
|
||||
(if (or (not= (:version data) (:version (:data file)))
|
||||
(not= features (:features file)))
|
||||
(vary-meta file assoc ::migrated true)
|
||||
file))))
|
||||
|
||||
(defn migrated?
|
||||
[{:keys [data] :as file}]
|
||||
(or (::migrated file)
|
||||
(let [version (:version data)]
|
||||
(> version
|
||||
(::orig-version file version)))))
|
||||
[file]
|
||||
(true? (-> file meta ::migrated)))
|
||||
|
||||
;; Default handler, noop
|
||||
(defmethod migrate :default [data] data)
|
||||
|
||||
@@ -28,14 +28,14 @@
|
||||
(let [repair-shape
|
||||
(fn [shape]
|
||||
; Reset geometry to minimal
|
||||
(log/debug :hint " -> Reset geometry")
|
||||
(log/debug :hint " -> reset geometry")
|
||||
(-> shape
|
||||
(assoc :x 0)
|
||||
(assoc :y 0)
|
||||
(assoc :width 0.01)
|
||||
(assoc :height 0.01)
|
||||
(cts/setup-rect)))]
|
||||
(log/info :hint "Repairing shape :invalid-geometry" :id (:id shape) :name (:name shape) :page-id page-id)
|
||||
(log/dbg :hint "repairing shape :invalid-geometry" :id (:id shape) :name (:name shape) :page-id page-id)
|
||||
(-> (pcb/empty-changes nil page-id)
|
||||
(pcb/with-file-data file-data)
|
||||
(pcb/update-shapes [(:id shape)] repair-shape))))
|
||||
@@ -45,10 +45,10 @@
|
||||
(let [repair-shape
|
||||
(fn [shape]
|
||||
; Set parent to root frame.
|
||||
(log/debug :hint " -> Set to " :parent-id uuid/zero)
|
||||
(log/debug :hint " -> set to " :parent-id uuid/zero)
|
||||
(assoc shape :parent-id uuid/zero))]
|
||||
|
||||
(log/info :hint "Repairing shape :parent-not-found" :id (:id shape) :name (:name shape) :page-id page-id)
|
||||
(log/dbg :hint "repairing shape :parent-not-found" :id (:id shape) :name (:name shape) :page-id page-id)
|
||||
(-> (pcb/empty-changes nil page-id)
|
||||
(pcb/with-file-data file-data)
|
||||
(pcb/update-shapes [(:id shape)] repair-shape))))
|
||||
@@ -58,10 +58,10 @@
|
||||
(let [repair-shape
|
||||
(fn [parent-shape]
|
||||
; Add shape to parent's children list
|
||||
(log/debug :hint " -> Add children to" :parent-id (:id parent-shape))
|
||||
(log/debug :hint " -> add children to" :parent-id (:id parent-shape))
|
||||
(update parent-shape :shapes conj (:id shape)))]
|
||||
|
||||
(log/info :hint "Repairing shape :child-not-in-parent" :id (:id shape) :name (:name shape) :page-id page-id)
|
||||
(log/dbg :hint "repairing shape :child-not-in-parent" :id (:id shape) :name (:name shape) :page-id page-id)
|
||||
(-> (pcb/empty-changes nil page-id)
|
||||
(pcb/with-file-data file-data)
|
||||
(pcb/update-shapes [(:parent-id shape)] repair-shape))))
|
||||
@@ -70,17 +70,17 @@
|
||||
[_ {:keys [shape page-id args] :as error} file-data _]
|
||||
(let [repair-shape
|
||||
(fn [parent-shape]
|
||||
(log/debug :hint " -> Remove child" :child-id (:child-id args))
|
||||
(log/debug :hint " -> remove child" :child-id (:child-id args))
|
||||
(update parent-shape :shapes (fn [shapes]
|
||||
(d/removev #(= (:child-id args) %) shapes))))]
|
||||
(log/info :hint "Repairing shape :child-not-found" :id (:id shape) :name (:name shape) :page-id page-id)
|
||||
(log/dbg :hint "repairing shape :child-not-found" :id (:id shape) :name (:name shape) :page-id page-id)
|
||||
(-> (pcb/empty-changes nil page-id)
|
||||
(pcb/with-file-data file-data)
|
||||
(pcb/update-shapes [(:id shape)] repair-shape))))
|
||||
|
||||
(defmethod repair-error :invalid-parent
|
||||
[_ {:keys [shape page-id args] :as error} file-data _]
|
||||
(log/info :hint "Repairing shape :invalid-parent" :id (:id shape) :name (:name shape) :page-id page-id)
|
||||
(log/dbg :hint "repairing shape :invalid-parent" :id (:id shape) :name (:name shape) :page-id page-id)
|
||||
(-> (pcb/empty-changes nil page-id)
|
||||
(pcb/with-file-data file-data)
|
||||
(pcb/change-parent (:parent-id args) [shape] nil {:component-swap true})))
|
||||
@@ -93,10 +93,10 @@
|
||||
(let [page (ctpl/get-page file-data page-id)
|
||||
frame (cfh/get-frame (:objects page) (:parent-id shape))
|
||||
frame-id (or (:id frame) uuid/zero)]
|
||||
(log/debug :hint " -> Set to " :frame-id frame-id)
|
||||
(log/debug :hint " -> set to " :frame-id frame-id)
|
||||
(assoc shape :frame-id frame-id)))]
|
||||
|
||||
(log/info :hint "Repairing shape :frame-not-found" :id (:id shape) :name (:name shape) :page-id page-id)
|
||||
(log/dbg :hint "repairing shape :frame-not-found" :id (:id shape) :name (:name shape) :page-id page-id)
|
||||
(-> (pcb/empty-changes nil page-id)
|
||||
(pcb/with-file-data file-data)
|
||||
(pcb/update-shapes [(:id shape)] repair-shape))))
|
||||
@@ -109,10 +109,10 @@
|
||||
(let [page (ctpl/get-page file-data page-id)
|
||||
frame (cfh/get-frame (:objects page) (:parent-id shape))
|
||||
frame-id (or (:id frame) uuid/zero)]
|
||||
(log/debug :hint " -> Set to " :frame-id frame-id)
|
||||
(log/debug :hint " -> set to " :frame-id frame-id)
|
||||
(assoc shape :frame-id frame-id)))]
|
||||
|
||||
(log/info :hint "Repairing shape :invalid-frame" :id (:id shape) :name (:name shape) :page-id page-id)
|
||||
(log/dbg :hint "repairing shape :invalid-frame" :id (:id shape) :name (:name shape) :page-id page-id)
|
||||
(-> (pcb/empty-changes nil page-id)
|
||||
(pcb/with-file-data file-data)
|
||||
(pcb/update-shapes [(:id shape)] repair-shape))))
|
||||
@@ -122,10 +122,10 @@
|
||||
(let [repair-shape
|
||||
(fn [shape]
|
||||
; Set the :shape as main instance root
|
||||
(log/debug :hint " -> Set :main-instance")
|
||||
(log/debug :hint " -> set :main-instance")
|
||||
(assoc shape :main-instance true))]
|
||||
|
||||
(log/info :hint "Repairing shape :component-not-main" :id (:id shape) :name (:name shape) :page-id page-id)
|
||||
(log/dbg :hint "repairing shape :component-not-main" :id (:id shape) :name (:name shape) :page-id page-id)
|
||||
(-> (pcb/empty-changes nil page-id)
|
||||
(pcb/with-file-data file-data)
|
||||
(pcb/update-shapes [(:id shape)] repair-shape))))
|
||||
@@ -135,13 +135,13 @@
|
||||
(let [repair-shape
|
||||
(fn [shape]
|
||||
; Set :component-file to local file
|
||||
(log/debug :hint " -> Set :component-file to local file")
|
||||
(log/debug :hint " -> set :component-file to local file")
|
||||
(assoc shape :component-file (:id file-data)))]
|
||||
; There is no solution that may recover it with confidence
|
||||
;; (log/warn :hint " -> CANNOT REPAIR THIS AUTOMATICALLY.")
|
||||
;; shape)]
|
||||
|
||||
(log/info :hint "Repairing shape :component-main-external" :id (:id shape) :name (:name shape) :page-id page-id)
|
||||
(log/dbg :hint "repairing shape :component-main-external" :id (:id shape) :name (:name shape) :page-id page-id)
|
||||
(-> (pcb/empty-changes nil page-id)
|
||||
(pcb/with-file-data file-data)
|
||||
(pcb/update-shapes [(:id shape)] repair-shape))))
|
||||
@@ -154,13 +154,13 @@
|
||||
repair-shape
|
||||
(fn [shape]
|
||||
; Detach the shape and convert it to non instance.
|
||||
(log/debug :hint " -> Detach shape" :shape-id (:id shape))
|
||||
(log/debug :hint " -> detach shape" :shape-id (:id shape))
|
||||
(ctk/detach-shape shape))]
|
||||
; There is no solution that may recover it with confidence
|
||||
;; (log/warn :hint " -> CANNOT REPAIR THIS AUTOMATICALLY.")
|
||||
;; shape)]
|
||||
|
||||
(log/info :hint "Repairing shape :component-not-found" :id (:id shape) :name (:name shape) :page-id page-id)
|
||||
(log/dbg :hint "repairing shape :component-not-found" :id (:id shape) :name (:name shape) :page-id page-id)
|
||||
(-> (pcb/empty-changes nil page-id)
|
||||
(pcb/with-file-data file-data)
|
||||
(pcb/update-shapes shape-ids repair-shape))))
|
||||
@@ -172,15 +172,15 @@
|
||||
repair-component
|
||||
(fn [component]
|
||||
; Assign main instance in the component to current shape
|
||||
(log/debug :hint " -> Assign main-instance-id" :component-id (:id component))
|
||||
(log/debug :hint " -> assign main-instance-id" :component-id (:id component))
|
||||
(assoc component :main-instance-id (:id shape)))
|
||||
|
||||
detach-shape
|
||||
(fn [shape]
|
||||
(log/debug :hint " -> Detach shape" :shape-id (:id shape))
|
||||
(log/debug :hint " -> detach shape" :shape-id (:id shape))
|
||||
(ctk/detach-shape shape))]
|
||||
|
||||
(log/info :hint "Repairing shape :invalid-main-instance-id" :id (:id shape) :name (:name shape) :page-id page-id)
|
||||
(log/dbg :hint "repairing shape :invalid-main-instance-id" :id (:id shape) :name (:name shape) :page-id page-id)
|
||||
(if (and (some? component) (not (:deleted component)))
|
||||
(-> (pcb/empty-changes nil page-id)
|
||||
(pcb/with-library-data file-data)
|
||||
@@ -195,9 +195,9 @@
|
||||
(let [repair-component
|
||||
(fn [component]
|
||||
; Assign main instance in the component to current shape
|
||||
(log/debug :hint " -> Assign main-instance-page" :component-id (:id component))
|
||||
(log/debug :hint " -> assign main-instance-page" :component-id (:id component))
|
||||
(assoc component :main-instance-page page-id))]
|
||||
(log/info :hint "Repairing shape :invalid-main-instance-page" :id (:id shape) :name (:name shape) :page-id page-id)
|
||||
(log/dbg :hint "repairing shape :invalid-main-instance-page" :id (:id shape) :name (:name shape) :page-id page-id)
|
||||
(-> (pcb/empty-changes nil page-id)
|
||||
(pcb/with-library-data file-data)
|
||||
(pcb/update-component (:component-id shape) repair-component))))
|
||||
@@ -210,7 +210,7 @@
|
||||
(log/warn :hint " -> CANNOT REPAIR THIS AUTOMATICALLY.")
|
||||
shape)]
|
||||
|
||||
(log/info :hint "Repairing shape :invalid-main-instance" :id (:id shape) :name (:name shape) :page-id page-id)
|
||||
(log/dbg :hint "repairing shape :invalid-main-instance" :id (:id shape) :name (:name shape) :page-id page-id)
|
||||
(-> (pcb/empty-changes nil page-id)
|
||||
(pcb/with-file-data file-data)
|
||||
(pcb/update-shapes [(:id shape)] repair-shape))))
|
||||
@@ -220,10 +220,10 @@
|
||||
(let [repair-shape
|
||||
(fn [shape]
|
||||
; Unset the :shape as main instance root
|
||||
(log/debug :hint " -> Unset :main-instance")
|
||||
(log/debug :hint " -> unset :main-instance")
|
||||
(dissoc shape :main-instance))]
|
||||
|
||||
(log/info :hint "Repairing shape :component-main" :id (:id shape) :name (:name shape) :page-id page-id)
|
||||
(log/dbg :hint "repairing shape :component-main" :id (:id shape) :name (:name shape) :page-id page-id)
|
||||
(-> (pcb/empty-changes nil page-id)
|
||||
(pcb/with-file-data file-data)
|
||||
(pcb/update-shapes [(:id shape)] repair-shape))))
|
||||
@@ -233,10 +233,10 @@
|
||||
(let [repair-shape
|
||||
(fn [shape]
|
||||
; Convert the shape in a top copy root.
|
||||
(log/debug :hint " -> Set :component-root")
|
||||
(log/debug :hint " -> set :component-root")
|
||||
(assoc shape :component-root true))]
|
||||
|
||||
(log/info :hint "Repairing shape :should-be-component-root" :id (:id shape) :name (:name shape) :page-id page-id)
|
||||
(log/dbg :hint "repairing shape :should-be-component-root" :id (:id shape) :name (:name shape) :page-id page-id)
|
||||
(-> (pcb/empty-changes nil page-id)
|
||||
(pcb/with-file-data file-data)
|
||||
(pcb/update-shapes [(:id shape)] repair-shape))))
|
||||
@@ -246,10 +246,10 @@
|
||||
(let [repair-shape
|
||||
(fn [shape]
|
||||
; Convert the shape in a nested copy root.
|
||||
(log/debug :hint " -> Unset :component-root")
|
||||
(log/debug :hint " -> unset :component-root")
|
||||
(dissoc shape :component-root))]
|
||||
|
||||
(log/info :hint "Repairing shape :should-not-be-component-root" :id (:id shape) :name (:name shape) :page-id page-id)
|
||||
(log/dbg :hint "repairing shape :should-not-be-component-root" :id (:id shape) :name (:name shape) :page-id page-id)
|
||||
(-> (pcb/empty-changes nil page-id)
|
||||
(pcb/with-file-data file-data)
|
||||
(pcb/update-shapes [(:id shape)] repair-shape))))
|
||||
@@ -268,17 +268,17 @@
|
||||
|
||||
reassign-shape
|
||||
(fn [shape]
|
||||
(log/debug :hint " -> Reassign shape-ref to" :shape-ref (:id matching-shape))
|
||||
(log/debug :hint " -> reassign shape-ref to" :shape-ref (:id matching-shape))
|
||||
(assoc shape :shape-ref (:id matching-shape)))
|
||||
|
||||
detach-shape
|
||||
(fn [shape]
|
||||
(log/debug :hint " -> Detach shape" :shape-id (:id shape))
|
||||
(log/debug :hint " -> detach shape" :shape-id (:id shape))
|
||||
(ctk/detach-shape shape))]
|
||||
|
||||
; If the shape still refers to the remote component, try to find the corresponding near one
|
||||
; and link to it. If not, detach the shape.
|
||||
(log/info :hint "Repairing shape :ref-shape-not-found" :id (:id shape) :name (:name shape) :page-id page-id)
|
||||
(log/dbg :hint "repairing shape :ref-shape-not-found" :id (:id shape) :name (:name shape) :page-id page-id)
|
||||
(if (some? matching-shape)
|
||||
(-> (pcb/empty-changes nil page-id)
|
||||
(pcb/with-file-data file-data)
|
||||
@@ -294,10 +294,10 @@
|
||||
(let [repair-shape
|
||||
(fn [shape]
|
||||
; Remove shape-ref
|
||||
(log/debug :hint " -> Unset :shape-ref")
|
||||
(log/debug :hint " -> unset :shape-ref")
|
||||
(dissoc shape :shape-ref))]
|
||||
|
||||
(log/info :hint "Repairing shape :shape-ref-in-main" :id (:id shape) :name (:name shape) :page-id page-id)
|
||||
(log/dbg :hint "repairing shape :shape-ref-in-main" :id (:id shape) :name (:name shape) :page-id page-id)
|
||||
(-> (pcb/empty-changes nil page-id)
|
||||
(pcb/with-file-data file-data)
|
||||
(pcb/update-shapes [(:id shape)] repair-shape))))
|
||||
@@ -307,10 +307,10 @@
|
||||
(let [repair-shape
|
||||
(fn [shape]
|
||||
; Convert the shape in a nested main head.
|
||||
(log/debug :hint " -> Unset :component-root")
|
||||
(log/debug :hint " -> unset :component-root")
|
||||
(dissoc shape :component-root))]
|
||||
|
||||
(log/info :hint "Repairing shape :root-main-not-allowed" :id (:id shape) :name (:name shape) :page-id page-id)
|
||||
(log/dbg :hint "repairing shape :root-main-not-allowed" :id (:id shape) :name (:name shape) :page-id page-id)
|
||||
(-> (pcb/empty-changes nil page-id)
|
||||
(pcb/with-file-data file-data)
|
||||
(pcb/update-shapes [(:id shape)] repair-shape))))
|
||||
@@ -320,10 +320,10 @@
|
||||
(let [repair-shape
|
||||
(fn [shape]
|
||||
; Convert the shape in a top main head.
|
||||
(log/debug :hint " -> Set :component-root")
|
||||
(log/debug :hint " -> set :component-root")
|
||||
(assoc shape :component-root true))]
|
||||
|
||||
(log/info :hint "Repairing shape :nested-main-not-allowed" :id (:id shape) :name (:name shape) :page-id page-id)
|
||||
(log/dbg :hint "repairing shape :nested-main-not-allowed" :id (:id shape) :name (:name shape) :page-id page-id)
|
||||
(-> (pcb/empty-changes nil page-id)
|
||||
(pcb/with-file-data file-data)
|
||||
(pcb/update-shapes [(:id shape)] repair-shape))))
|
||||
@@ -333,10 +333,10 @@
|
||||
(let [repair-shape
|
||||
(fn [shape]
|
||||
; Convert the shape in a nested copy head.
|
||||
(log/debug :hint " -> Unset :component-root")
|
||||
(log/debug :hint " -> unset :component-root")
|
||||
(dissoc shape :component-root))]
|
||||
|
||||
(log/info :hint "Repairing shape :root-copy-not-allowed" :id (:id shape) :name (:name shape) :page-id page-id)
|
||||
(log/dbg :hint "repairing shape :root-copy-not-allowed" :id (:id shape) :name (:name shape) :page-id page-id)
|
||||
(-> (pcb/empty-changes nil page-id)
|
||||
(pcb/with-file-data file-data)
|
||||
(pcb/update-shapes [(:id shape)] repair-shape))))
|
||||
@@ -346,10 +346,10 @@
|
||||
(let [repair-shape
|
||||
(fn [shape]
|
||||
; Convert the shape in a top copy root.
|
||||
(log/debug :hint " -> Set :component-root")
|
||||
(log/debug :hint " -> set :component-root")
|
||||
(assoc shape :component-root true))]
|
||||
|
||||
(log/info :hint "Repairing shape :nested-copy-not-allowed" :id (:id shape) :name (:name shape) :page-id page-id)
|
||||
(log/dbg :hint "repairing shape :nested-copy-not-allowed" :id (:id shape) :name (:name shape) :page-id page-id)
|
||||
(-> (pcb/empty-changes nil page-id)
|
||||
(pcb/with-file-data file-data)
|
||||
(pcb/update-shapes [(:id shape)] repair-shape))))
|
||||
@@ -359,10 +359,10 @@
|
||||
(let [repair-shape
|
||||
(fn [shape]
|
||||
; Detach the shape and convert it to non instance.
|
||||
(log/debug :hint " -> Detach shape" :shape-id (:id shape))
|
||||
(log/debug :hint " -> detach shape" :shape-id (:id shape))
|
||||
(ctk/detach-shape shape))]
|
||||
|
||||
(log/info :hint "Repairing shape :not-head-main-not-allowed" :id (:id shape) :name (:name shape) :page-id page-id)
|
||||
(log/dbg :hint "repairing shape :not-head-main-not-allowed" :id (:id shape) :name (:name shape) :page-id page-id)
|
||||
(-> (pcb/empty-changes nil page-id)
|
||||
(pcb/with-file-data file-data)
|
||||
(pcb/update-shapes [(:id shape)] repair-shape))))
|
||||
@@ -372,10 +372,10 @@
|
||||
(let [repair-shape
|
||||
(fn [shape]
|
||||
; Detach the shape and convert it to non instance.
|
||||
(log/debug :hint " -> Detach shape" :shape-id (:id shape))
|
||||
(log/debug :hint " -> detach shape" :shape-id (:id shape))
|
||||
(ctk/detach-shape shape))]
|
||||
|
||||
(log/info :hint "Repairing shape :not-head-copy-not-allowed" :id (:id shape) :name (:name shape) :page-id page-id)
|
||||
(log/dbg :hint "repairing shape :not-head-copy-not-allowed" :id (:id shape) :name (:name shape) :page-id page-id)
|
||||
(-> (pcb/empty-changes nil page-id)
|
||||
(pcb/with-file-data file-data)
|
||||
(pcb/update-shapes [(:id shape)] repair-shape))))
|
||||
@@ -388,7 +388,7 @@
|
||||
(log/warn :hint " -> CANNOT REPAIR THIS AUTOMATICALLY.")
|
||||
shape)]
|
||||
|
||||
(log/info :hint "Repairing shape :not-component-not-allowed" :id (:id shape) :name (:name shape) :page-id page-id)
|
||||
(log/dbg :hint "repairing shape :not-component-not-allowed" :id (:id shape) :name (:name shape) :page-id page-id)
|
||||
(-> (pcb/empty-changes nil page-id)
|
||||
(pcb/with-file-data file-data)
|
||||
(pcb/update-shapes [(:id shape)] repair-shape))))
|
||||
@@ -398,14 +398,14 @@
|
||||
(let [repair-shape
|
||||
(fn [shape]
|
||||
; Convert the shape in a frame.
|
||||
(log/debug :hint " -> Set :type :frame")
|
||||
(log/debug :hint " -> set :type :frame")
|
||||
(assoc shape :type :frame
|
||||
:fills []
|
||||
:hide-in-viewer true
|
||||
:rx 0
|
||||
:ry 0))]
|
||||
|
||||
(log/info :hint "Repairing shape :instance-head-not-frame" :id (:id shape) :name (:name shape) :page-id page-id)
|
||||
(log/dbg :hint "repairing shape :instance-head-not-frame" :id (:id shape) :name (:name shape) :page-id page-id)
|
||||
(-> (pcb/empty-changes nil page-id)
|
||||
(pcb/with-file-data file-data)
|
||||
(pcb/update-shapes [(:id shape)] repair-shape))))
|
||||
@@ -417,13 +417,13 @@
|
||||
; Remove the objects key, or set it to {} if the component is deleted
|
||||
(if (:deleted component)
|
||||
(do
|
||||
(log/debug :hint " -> Set :objects {}")
|
||||
(log/debug :hint " -> set :objects {}")
|
||||
(assoc component :objects {}))
|
||||
(do
|
||||
(log/debug :hint " -> Remove :objects")
|
||||
(log/debug :hint " -> remove :objects")
|
||||
(dissoc component :objects))))]
|
||||
|
||||
(log/info :hint "Repairing component :component-nil-objects-not-allowed" :id (:id shape) :name (:name shape))
|
||||
(log/dbg :hint "repairing component :component-nil-objects-not-allowed" :id (:id shape) :name (:name shape))
|
||||
(-> (pcb/empty-changes nil)
|
||||
(pcb/with-library-data file-data)
|
||||
(pcb/update-component (:id shape) repair-component))))
|
||||
@@ -434,13 +434,15 @@
|
||||
file)
|
||||
|
||||
(defn repair-file
|
||||
[file-data libraries errors]
|
||||
(log/info :hint "Repairing file" :id (:id file-data) :error-count (count errors))
|
||||
(reduce (fn [changes error]
|
||||
(pcb/concat-changes changes
|
||||
(repair-error (:code error)
|
||||
error
|
||||
file-data
|
||||
libraries)))
|
||||
(pcb/empty-changes nil)
|
||||
errors))
|
||||
[{:keys [data id] :as file} libraries errors]
|
||||
(log/dbg :hint "repairing file" :id (str id) :errors (count errors))
|
||||
(let [{:keys [redo-changes]}
|
||||
(reduce (fn [changes error]
|
||||
(pcb/concat-changes changes
|
||||
(repair-error (:code error)
|
||||
error
|
||||
data
|
||||
libraries)))
|
||||
(pcb/empty-changes nil)
|
||||
errors)]
|
||||
redo-changes))
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
(ns app.common.files.validate
|
||||
(:require
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.exceptions :as ex]
|
||||
[app.common.files.helpers :as cfh]
|
||||
[app.common.schema :as sm]
|
||||
@@ -49,50 +50,50 @@
|
||||
:not-component-not-allowed
|
||||
:component-nil-objects-not-allowed})
|
||||
|
||||
(def validation-error
|
||||
[:map {:title "ValidationError"}
|
||||
[:code {:optional false} [::sm/one-of error-codes]]
|
||||
[:hint {:optional false} :string]
|
||||
[:shape {:optional true} :map] ; Cannot validate a shape because here it may be broken
|
||||
[:file-id ::sm/uuid]
|
||||
[:page-id ::sm/uuid]])
|
||||
(def ^:private
|
||||
schema:error
|
||||
(sm/define
|
||||
[:map {:title "ValidationError"}
|
||||
[:code {:optional false} [::sm/one-of error-codes]]
|
||||
[:hint {:optional false} :string]
|
||||
[:shape {:optional true} :map] ; Cannot validate a shape because here it may be broken
|
||||
[:shape-id {:optional true} ::sm/uuid]
|
||||
[:file-id ::sm/uuid]
|
||||
[:page-id ::sm/uuid]]))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; ERROR HANDLING
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(def ^:dynamic *errors* nil)
|
||||
(def ^:dynamic ^:private *errors* nil)
|
||||
|
||||
(defn report-error!
|
||||
(defn- report-error
|
||||
[code hint shape file page & {:as args}]
|
||||
(if (some? *errors*)
|
||||
(vswap! *errors* conj {:code code
|
||||
:hint hint
|
||||
:shape shape
|
||||
:file-id (:id file)
|
||||
:page-id (:id page)
|
||||
:args args})
|
||||
(let [error {:code code
|
||||
:hint hint
|
||||
:shape shape
|
||||
:file-id (:id file)
|
||||
:page-id (:id page)
|
||||
:shape-id (:id shape)
|
||||
:args args}]
|
||||
|
||||
(let [explain (str/ffmt "file %, page %, shape %"
|
||||
(:id file)
|
||||
(:id page)
|
||||
(:id shape))]
|
||||
(ex/raise :type :validation
|
||||
:code code
|
||||
:hint hint
|
||||
:args args
|
||||
:file-id (:id file)
|
||||
:page-id (:id page)
|
||||
:shape-id (:id shape)
|
||||
::explain explain))))
|
||||
(dm/assert!
|
||||
"expected a valid `*errors*` dynamic binding"
|
||||
(some? *errors*))
|
||||
|
||||
(dm/assert!
|
||||
"expected valid error"
|
||||
(sm/check! schema:error error))
|
||||
|
||||
(vswap! *errors* conj error)))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; VALIDATION FUNCTIONS
|
||||
;; PRIVATE API: VALIDATION FUNCTIONS
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(declare validate-shape!)
|
||||
(declare check-shape)
|
||||
|
||||
(defn validate-geometry!
|
||||
(defn- check-geometry
|
||||
"Validate that the shape has valid coordinates, selrect and points."
|
||||
[shape file page]
|
||||
(when (and (not (#{:path :bool} (:type shape)))
|
||||
@@ -102,175 +103,175 @@
|
||||
(nil? (:height shape))
|
||||
(nil? (:selrect shape))
|
||||
(nil? (:points shape))))
|
||||
(report-error! :invalid-geometry
|
||||
"Shape greometry is invalid"
|
||||
shape file page)))
|
||||
(report-error :invalid-geometry
|
||||
"Shape greometry is invalid"
|
||||
shape file page)))
|
||||
|
||||
(defn validate-parent-children!
|
||||
(defn- check-parent-children
|
||||
"Validate parent and children exists, and the link is bidirectional."
|
||||
[shape file page]
|
||||
(let [parent (ctst/get-shape page (:parent-id shape))]
|
||||
(if (nil? parent)
|
||||
(report-error! :parent-not-found
|
||||
(str/ffmt "Parent % not found" (:parent-id shape))
|
||||
shape file page)
|
||||
(report-error :parent-not-found
|
||||
(str/ffmt "Parent % not found" (:parent-id shape))
|
||||
shape file page)
|
||||
(do
|
||||
(when-not (cfh/root? shape)
|
||||
(when-not (some #{(:id shape)} (:shapes parent))
|
||||
(report-error! :child-not-in-parent
|
||||
(str/ffmt "Shape % not in parent's children list" (:id shape))
|
||||
shape file page)))
|
||||
(report-error :child-not-in-parent
|
||||
(str/ffmt "Shape % not in parent's children list" (:id shape))
|
||||
shape file page)))
|
||||
|
||||
(doseq [child-id (:shapes shape)]
|
||||
(let [child (ctst/get-shape page child-id)]
|
||||
(if (nil? child)
|
||||
(report-error! :child-not-found
|
||||
(str/ffmt "Child % not found in parent %" child-id (:id shape))
|
||||
shape file page
|
||||
:parent-id (:id shape)
|
||||
:child-id child-id)
|
||||
(report-error :child-not-found
|
||||
(str/ffmt "Child % not found in parent %" child-id (:id shape))
|
||||
shape file page
|
||||
:parent-id (:id shape)
|
||||
:child-id child-id)
|
||||
(when (not= (:parent-id child) (:id shape))
|
||||
(report-error! :invalid-parent
|
||||
(str/ffmt "Child % has invalid parent %" child-id (:id shape))
|
||||
child file page
|
||||
:parent-id (:id shape))))))))))
|
||||
(report-error :invalid-parent
|
||||
(str/ffmt "Child % has invalid parent %" child-id (:id shape))
|
||||
child file page
|
||||
:parent-id (:id shape))))))))))
|
||||
|
||||
(defn validate-frame!
|
||||
(defn- check-frame
|
||||
"Validate that the frame-id shape exists and is indeed a frame. Also
|
||||
it must point to the parent shape (if this is a frame) or to the
|
||||
frame-id of the parent (if not)."
|
||||
[shape file page]
|
||||
(let [frame (ctst/get-shape page (:frame-id shape))]
|
||||
(if (nil? frame)
|
||||
(report-error! :frame-not-found
|
||||
(str/ffmt "Frame % not found" (:frame-id shape))
|
||||
shape file page)
|
||||
(report-error :frame-not-found
|
||||
(str/ffmt "Frame % not found" (:frame-id shape))
|
||||
shape file page)
|
||||
(if (not= (:type frame) :frame)
|
||||
(report-error! :invalid-frame
|
||||
(str/ffmt "Frame % is not actually a frame" (:frame-id shape))
|
||||
shape file page)
|
||||
(report-error :invalid-frame
|
||||
(str/ffmt "Frame % is not actually a frame" (:frame-id shape))
|
||||
shape file page)
|
||||
(let [parent (ctst/get-shape page (:parent-id shape))]
|
||||
(when (some? parent)
|
||||
(if (= (:type parent) :frame)
|
||||
(when-not (= (:frame-id shape) (:id parent))
|
||||
(report-error! :invalid-frame
|
||||
(str/ffmt "Frame-id should point to parent %" (:id parent))
|
||||
shape file page))
|
||||
(report-error :invalid-frame
|
||||
(str/ffmt "Frame-id should point to parent %" (:id parent))
|
||||
shape file page))
|
||||
(when-not (= (:frame-id shape) (:frame-id parent))
|
||||
(report-error! :invalid-frame
|
||||
(str/ffmt "Frame-id should point to parent frame %" (:frame-id parent))
|
||||
shape file page)))))))))
|
||||
(report-error :invalid-frame
|
||||
(str/ffmt "Frame-id should point to parent frame %" (:frame-id parent))
|
||||
shape file page)))))))))
|
||||
|
||||
(defn validate-component-main-head!
|
||||
(defn- check-component-main-head
|
||||
"Validate shape is a main instance head, component exists
|
||||
and its main-instance points to this shape."
|
||||
[shape file page libraries]
|
||||
(when (nil? (:main-instance shape))
|
||||
(report-error! :component-not-main
|
||||
"Shape expected to be main instance"
|
||||
shape file page))
|
||||
(report-error :component-not-main
|
||||
"Shape expected to be main instance"
|
||||
shape file page))
|
||||
(when-not (= (:component-file shape) (:id file))
|
||||
(report-error! :component-main-external
|
||||
"Main instance should refer to a component in the same file"
|
||||
shape file page))
|
||||
(report-error :component-main-external
|
||||
"Main instance should refer to a component in the same file"
|
||||
shape file page))
|
||||
(let [component (ctf/resolve-component shape file libraries :include-deleted? true)]
|
||||
(if (nil? component)
|
||||
(report-error! :component-not-found
|
||||
(str/ffmt "Component % not found in file %" (:component-id shape) (:component-file shape))
|
||||
shape file page)
|
||||
(report-error :component-not-found
|
||||
(str/ffmt "Component % not found in file %" (:component-id shape) (:component-file shape))
|
||||
shape file page)
|
||||
(do
|
||||
(when-not (= (:main-instance-id component) (:id shape))
|
||||
(report-error! :invalid-main-instance-id
|
||||
(str/ffmt "Main instance id of component % is not valid" (:component-id shape))
|
||||
shape file page))
|
||||
(report-error :invalid-main-instance-id
|
||||
(str/ffmt "Main instance id of component % is not valid" (:component-id shape))
|
||||
shape file page))
|
||||
(when-not (= (:main-instance-page component) (:id page))
|
||||
(let [component-page (ctf/get-component-page (:data file) component)
|
||||
main-component (ctst/get-shape component-page (:main-instance-id component))]
|
||||
;; We must check if the same component has main instances in different pages.
|
||||
;; In that case one of those instances shouldn't be main
|
||||
(if (:main-instance main-component)
|
||||
(report-error! :component-main
|
||||
"Shape not expected to be main instance"
|
||||
shape file page)
|
||||
(report-error! :invalid-main-instance-page
|
||||
(str/ffmt "Main instance page of component % is not valid" (:component-id shape))
|
||||
shape file page))))))))
|
||||
(report-error :component-main
|
||||
"Shape not expected to be main instance"
|
||||
shape file page)
|
||||
(report-error :invalid-main-instance-page
|
||||
(str/ffmt "Main instance page of component % is not valid" (:component-id shape))
|
||||
shape file page))))))))
|
||||
|
||||
(defn validate-component-not-main-head!
|
||||
(defn- check-component-not-main-head
|
||||
"Validate shape is a not-main instance head, component
|
||||
exists and its main-instance does not point to this
|
||||
shape."
|
||||
[shape file page libraries]
|
||||
(when (true? (:main-instance shape))
|
||||
(report-error! :component-not-main
|
||||
"Shape not expected to be main instance"
|
||||
shape file page))
|
||||
(report-error :component-not-main
|
||||
"Shape not expected to be main instance"
|
||||
shape file page))
|
||||
|
||||
(let [library-exists? (or (= (:component-file shape) (:id file))
|
||||
(contains? libraries (:component-file shape)))
|
||||
(contains? libraries (:component-file shape)))
|
||||
component (when library-exists?
|
||||
(ctf/resolve-component shape file libraries {:include-deleted? true}))]
|
||||
(if (nil? component)
|
||||
(when library-exists?
|
||||
(report-error! :component-not-found
|
||||
(str/ffmt "Component % not found in file %" (:component-id shape) (:component-file shape))
|
||||
shape file page))
|
||||
(report-error :component-not-found
|
||||
(str/ffmt "Component % not found in file %" (:component-id shape) (:component-file shape))
|
||||
shape file page))
|
||||
(when (and (= (:main-instance-id component) (:id shape))
|
||||
(= (:main-instance-page component) (:id page)))
|
||||
(report-error! :invalid-main-instance
|
||||
(str/ffmt "Main instance of component % should not be this shape" (:id component))
|
||||
shape file page)))))
|
||||
(report-error :invalid-main-instance
|
||||
(str/ffmt "Main instance of component % should not be this shape" (:id component))
|
||||
shape file page)))))
|
||||
|
||||
(defn validate-component-not-main-not-head!
|
||||
(defn- check-component-not-main-not-head
|
||||
"Validate that this shape is not main instance and not head."
|
||||
[shape file page]
|
||||
(when (true? (:main-instance shape))
|
||||
(report-error! :component-main
|
||||
"Shape not expected to be main instance"
|
||||
shape file page))
|
||||
(report-error :component-main
|
||||
"Shape not expected to be main instance"
|
||||
shape file page))
|
||||
(when (or (some? (:component-id shape))
|
||||
(some? (:component-file shape)))
|
||||
(report-error! :component-main
|
||||
"Shape not expected to be component head"
|
||||
shape file page)))
|
||||
(report-error :component-main
|
||||
"Shape not expected to be component head"
|
||||
shape file page)))
|
||||
|
||||
(defn validate-component-root!
|
||||
(defn- check-component-root
|
||||
"Validate that this shape is an instance root."
|
||||
[shape file page]
|
||||
(when (nil? (:component-root shape))
|
||||
(report-error! :should-be-component-root
|
||||
"Shape should be component root"
|
||||
shape file page)))
|
||||
(report-error :should-be-component-root
|
||||
"Shape should be component root"
|
||||
shape file page)))
|
||||
|
||||
(defn validate-component-not-root!
|
||||
(defn- check-component-not-root
|
||||
"Validate that this shape is not an instance root."
|
||||
[shape file page]
|
||||
(when (true? (:component-root shape))
|
||||
(report-error! :should-not-be-component-root
|
||||
"Shape should not be component root"
|
||||
shape file page)))
|
||||
(report-error :should-not-be-component-root
|
||||
"Shape should not be component root"
|
||||
shape file page)))
|
||||
|
||||
(defn validate-component-ref!
|
||||
(defn- check-component-ref
|
||||
"Validate that the referenced shape exists in the near component."
|
||||
[shape file page libraries]
|
||||
(let [library-exists? (or (= (:component-file shape) (:id file))
|
||||
(contains? libraries (:component-file shape)))
|
||||
(contains? libraries (:component-file shape)))
|
||||
ref-shape (when library-exists?
|
||||
(ctf/find-ref-shape file page libraries shape :include-deleted? true))]
|
||||
(when (and library-exists? (nil? ref-shape))
|
||||
(report-error! :ref-shape-not-found
|
||||
(str/ffmt "Referenced shape % not found in near component" (:shape-ref shape))
|
||||
shape file page))))
|
||||
(report-error :ref-shape-not-found
|
||||
(str/ffmt "Referenced shape % not found in near component" (:shape-ref shape))
|
||||
shape file page))))
|
||||
|
||||
(defn validate-component-not-ref!
|
||||
(defn- check-component-not-ref
|
||||
"Validate that this shape does not reference other one."
|
||||
[shape file page]
|
||||
(when (some? (:shape-ref shape))
|
||||
(report-error! :shape-ref-in-main
|
||||
"Shape inside main instance should not have shape-ref"
|
||||
shape file page)))
|
||||
(report-error :shape-ref-in-main
|
||||
"Shape inside main instance should not have shape-ref"
|
||||
shape file page)))
|
||||
|
||||
(defn validate-shape-main-root-top!
|
||||
(defn- check-shape-main-root-top
|
||||
"Root shape of a top main instance:
|
||||
|
||||
- :main-instance
|
||||
@@ -278,78 +279,78 @@
|
||||
- :component-file
|
||||
- :component-root"
|
||||
[shape file page libraries]
|
||||
(validate-component-main-head! shape file page libraries)
|
||||
(validate-component-root! shape file page)
|
||||
(validate-component-not-ref! shape file page)
|
||||
(check-component-main-head shape file page libraries)
|
||||
(check-component-root shape file page)
|
||||
(check-component-not-ref shape file page)
|
||||
(doseq [child-id (:shapes shape)]
|
||||
(validate-shape! child-id file page libraries :context :main-top)))
|
||||
(check-shape child-id file page libraries :context :main-top)))
|
||||
|
||||
(defn validate-shape-main-root-nested!
|
||||
(defn- check-shape-main-root-nested
|
||||
"Root shape of a nested main instance
|
||||
- :main-instance
|
||||
- :component-id
|
||||
- :component-file"
|
||||
[shape file page libraries]
|
||||
(validate-component-main-head! shape file page libraries)
|
||||
(validate-component-not-root! shape file page)
|
||||
(validate-component-not-ref! shape file page)
|
||||
(check-component-main-head shape file page libraries)
|
||||
(check-component-not-root shape file page)
|
||||
(check-component-not-ref shape file page)
|
||||
(doseq [child-id (:shapes shape)]
|
||||
(validate-shape! child-id file page libraries :context :main-nested)))
|
||||
(check-shape child-id file page libraries :context :main-nested)))
|
||||
|
||||
(defn validate-shape-copy-root-top!
|
||||
(defn- check-shape-copy-root-top
|
||||
"Root shape of a top copy instance
|
||||
- :component-id
|
||||
- :component-file
|
||||
- :component-root
|
||||
- :shape-ref"
|
||||
[shape file page libraries]
|
||||
(validate-component-not-main-head! shape file page libraries)
|
||||
(validate-component-root! shape file page)
|
||||
(validate-component-ref! shape file page libraries)
|
||||
(check-component-not-main-head shape file page libraries)
|
||||
(check-component-root shape file page)
|
||||
(check-component-ref shape file page libraries)
|
||||
(doseq [child-id (:shapes shape)]
|
||||
(validate-shape! child-id file page libraries :context :copy-top)))
|
||||
(check-shape child-id file page libraries :context :copy-top)))
|
||||
|
||||
(defn validate-shape-copy-root-nested!
|
||||
(defn- check-shape-copy-root-nested
|
||||
"Root shape of a nested copy instance
|
||||
- :component-id
|
||||
- :component-file
|
||||
- :shape-ref"
|
||||
[shape file page libraries]
|
||||
(validate-component-not-main-head! shape file page libraries)
|
||||
(validate-component-not-root! shape file page)
|
||||
(validate-component-ref! shape file page libraries)
|
||||
(check-component-not-main-head shape file page libraries)
|
||||
(check-component-not-root shape file page)
|
||||
(check-component-ref shape file page libraries)
|
||||
(doseq [child-id (:shapes shape)]
|
||||
(validate-shape! child-id file page libraries :context :copy-nested)))
|
||||
(check-shape child-id file page libraries :context :copy-nested)))
|
||||
|
||||
(defn validate-shape-main-not-root!
|
||||
(defn- check-shape-main-not-root
|
||||
"Not-root shape of a main instance (not any attribute)"
|
||||
[shape file page libraries]
|
||||
(validate-component-not-main-not-head! shape file page)
|
||||
(validate-component-not-root! shape file page)
|
||||
(validate-component-not-ref! shape file page)
|
||||
(check-component-not-main-not-head shape file page)
|
||||
(check-component-not-root shape file page)
|
||||
(check-component-not-ref shape file page)
|
||||
(doseq [child-id (:shapes shape)]
|
||||
(validate-shape! child-id file page libraries :context :main-any)))
|
||||
(check-shape child-id file page libraries :context :main-any)))
|
||||
|
||||
(defn validate-shape-copy-not-root!
|
||||
(defn- check-shape-copy-not-root
|
||||
"Not-root shape of a copy instance :shape-ref"
|
||||
[shape file page libraries]
|
||||
(validate-component-not-main-not-head! shape file page)
|
||||
(validate-component-not-root! shape file page)
|
||||
(validate-component-ref! shape file page libraries)
|
||||
(check-component-not-main-not-head shape file page)
|
||||
(check-component-not-root shape file page)
|
||||
(check-component-ref shape file page libraries)
|
||||
(doseq [child-id (:shapes shape)]
|
||||
(validate-shape! child-id file page libraries :context :copy-any)))
|
||||
(check-shape child-id file page libraries :context :copy-any)))
|
||||
|
||||
(defn validate-shape-not-component!
|
||||
(defn- check-shape-not-component
|
||||
"Shape is not in a component or is a fostered children (not any
|
||||
attribute)"
|
||||
[shape file page libraries]
|
||||
(validate-component-not-main-not-head! shape file page)
|
||||
(validate-component-not-root! shape file page)
|
||||
(validate-component-not-ref! shape file page)
|
||||
(check-component-not-main-not-head shape file page)
|
||||
(check-component-not-root shape file page)
|
||||
(check-component-not-ref shape file page)
|
||||
(doseq [child-id (:shapes shape)]
|
||||
(validate-shape! child-id file page libraries :context :not-component)))
|
||||
(check-shape child-id file page libraries :context :not-component)))
|
||||
|
||||
(defn validate-shape!
|
||||
(defn- check-shape
|
||||
"Validate referential integrity and semantic coherence of
|
||||
a shape and all its children. Report all errors found.
|
||||
|
||||
@@ -366,132 +367,140 @@
|
||||
(let [shape (ctst/get-shape page shape-id)]
|
||||
(when (some? shape)
|
||||
(do
|
||||
(validate-geometry! shape file page)
|
||||
(validate-parent-children! shape file page)
|
||||
(validate-frame! shape file page)
|
||||
(check-geometry shape file page)
|
||||
(check-parent-children shape file page)
|
||||
(check-frame shape file page)
|
||||
|
||||
(if (ctk/instance-head? shape)
|
||||
(if (not= :frame (:type shape))
|
||||
(report-error! :instance-head-not-frame
|
||||
"Instance head should be a frame"
|
||||
shape file page)
|
||||
(report-error :instance-head-not-frame
|
||||
"Instance head should be a frame"
|
||||
shape file page)
|
||||
|
||||
(if (ctk/instance-root? shape)
|
||||
(if (ctk/main-instance? shape)
|
||||
(if (not= context :not-component)
|
||||
(report-error! :root-main-not-allowed
|
||||
"Root main component not allowed inside other component"
|
||||
shape file page)
|
||||
(validate-shape-main-root-top! shape file page libraries))
|
||||
(report-error :root-main-not-allowed
|
||||
"Root main component not allowed inside other component"
|
||||
shape file page)
|
||||
(check-shape-main-root-top shape file page libraries))
|
||||
|
||||
(if (not= context :not-component)
|
||||
(report-error! :root-copy-not-allowed
|
||||
"Root copy component not allowed inside other component"
|
||||
shape file page)
|
||||
(validate-shape-copy-root-top! shape file page libraries)))
|
||||
(report-error :root-copy-not-allowed
|
||||
"Root copy component not allowed inside other component"
|
||||
shape file page)
|
||||
(check-shape-copy-root-top shape file page libraries)))
|
||||
|
||||
(if (ctk/main-instance? shape)
|
||||
(if (= context :not-component)
|
||||
(report-error! :nested-main-not-allowed
|
||||
"Nested main component only allowed inside other component"
|
||||
shape file page)
|
||||
(validate-shape-main-root-nested! shape file page libraries))
|
||||
(report-error :nested-main-not-allowed
|
||||
"Nested main component only allowed inside other component"
|
||||
shape file page)
|
||||
(check-shape-main-root-nested shape file page libraries))
|
||||
|
||||
(if (= context :not-component)
|
||||
(report-error! :nested-copy-not-allowed
|
||||
"Nested copy component only allowed inside other component"
|
||||
shape file page)
|
||||
(validate-shape-copy-root-nested! shape file page libraries)))))
|
||||
(report-error :nested-copy-not-allowed
|
||||
"Nested copy component only allowed inside other component"
|
||||
shape file page)
|
||||
(check-shape-copy-root-nested shape file page libraries)))))
|
||||
|
||||
(if (ctk/in-component-copy? shape)
|
||||
(if-not (#{:copy-top :copy-nested :copy-any} context)
|
||||
(report-error! :not-head-copy-not-allowed
|
||||
"Non-root copy only allowed inside a copy"
|
||||
shape file page)
|
||||
(validate-shape-copy-not-root! shape file page libraries))
|
||||
(report-error :not-head-copy-not-allowed
|
||||
"Non-root copy only allowed inside a copy"
|
||||
shape file page)
|
||||
(check-shape-copy-not-root shape file page libraries))
|
||||
|
||||
(if (ctn/inside-component-main? (:objects page) shape)
|
||||
(if-not (#{:main-top :main-nested :main-any} context)
|
||||
(report-error! :not-head-main-not-allowed
|
||||
"Non-root main only allowed inside a main component"
|
||||
shape file page)
|
||||
(validate-shape-main-not-root! shape file page libraries))
|
||||
(report-error :not-head-main-not-allowed
|
||||
"Non-root main only allowed inside a main component"
|
||||
shape file page)
|
||||
(check-shape-main-not-root shape file page libraries))
|
||||
|
||||
(if (#{:main-top :main-nested :main-any} context)
|
||||
(report-error! :not-component-not-allowed
|
||||
"Not compoments are not allowed inside a main"
|
||||
shape file page)
|
||||
(validate-shape-not-component! shape file page libraries)))))))))
|
||||
(report-error :not-component-not-allowed
|
||||
"Not compoments are not allowed inside a main"
|
||||
shape file page)
|
||||
(check-shape-not-component shape file page libraries)))))))))
|
||||
|
||||
(defn- check-component
|
||||
"Validate semantic coherence of a component. Report all errors found."
|
||||
[component file]
|
||||
(when (and (contains? component :objects) (nil? (:objects component)))
|
||||
(report-error :component-nil-objects-not-allowed
|
||||
"Objects list cannot be nil"
|
||||
component file nil)))
|
||||
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; PUBLIC API: VALIDATION FUNCTIONS
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defn validate-file
|
||||
"Validate full referential integrity and semantic coherence on file data.
|
||||
|
||||
Return a list of errors or `nil`"
|
||||
[{:keys [data features] :as file} libraries]
|
||||
(when (contains? features "components/v2")
|
||||
(binding [*errors* (volatile! [])]
|
||||
(doseq [page (filter :id (ctpl/pages-seq data))]
|
||||
(let [orphans (->> page
|
||||
:objects
|
||||
vals
|
||||
(filter #(not (contains? (:objects page) (:parent-id %))))
|
||||
(map :id))]
|
||||
(check-shape uuid/zero file page libraries)
|
||||
(doseq [shape-id orphans]
|
||||
(check-shape shape-id file page libraries))))
|
||||
|
||||
(doseq [component (vals (:components data))]
|
||||
(check-component component file))
|
||||
|
||||
(-> *errors* deref not-empty))))
|
||||
|
||||
(defn validate-shape
|
||||
"Validate a shape and all its children. Returns a list of errors."
|
||||
[shape-id file page libraries]
|
||||
(binding [*errors* (volatile! [])]
|
||||
(validate-shape! shape-id file page libraries)
|
||||
(check-shape shape-id file page libraries)
|
||||
(deref *errors*)))
|
||||
|
||||
(defn validate-component!
|
||||
"Validate semantic coherence of a component. Report all errors found."
|
||||
[component file]
|
||||
(when (and (contains? component :objects) (nil? (:objects component)))
|
||||
(report-error! :component-nil-objects-not-allowed
|
||||
"Objects list cannot be nil"
|
||||
component file nil)))
|
||||
|
||||
(defn validate-component
|
||||
"Validate a component. Returns a list of errors."
|
||||
[component file]
|
||||
(binding [*errors* (volatile! [])]
|
||||
(validate-component! component file)
|
||||
(check-component component file)
|
||||
(deref *errors*)))
|
||||
|
||||
(def valid-fdata?
|
||||
(def ^:private valid-fdata?
|
||||
"Structural validation of file data using defined schema"
|
||||
(sm/lazy-validator ::ctf/data))
|
||||
|
||||
(def get-fdata-explain
|
||||
(def ^:private get-fdata-explain
|
||||
"Get schema explain data for file data"
|
||||
(sm/lazy-explainer ::ctf/data))
|
||||
|
||||
(defn validate-file-schema!
|
||||
[{:keys [id data] :as file}]
|
||||
(when-not (valid-fdata? data)
|
||||
(if (some? *errors*)
|
||||
(vswap! *errors* conj
|
||||
{:code :invalid-file-data-structure
|
||||
:hint (str/ffmt "invalid file data structure found on file '%'" id)
|
||||
:file-id id})
|
||||
(ex/raise :type :validation
|
||||
:code :data-validation
|
||||
:hint (str/ffmt "invalid file data structure found on file '%'" id)
|
||||
:file-id id
|
||||
::sm/explain (get-fdata-explain data))))
|
||||
file)
|
||||
(ex/raise :type :validation
|
||||
:code :schema-validation
|
||||
:hint (str/ffmt "invalid file data structure found on file '%'" id)
|
||||
:file-id id
|
||||
::sm/explain (get-fdata-explain data))))
|
||||
|
||||
(defn validate-file!
|
||||
"Validate full referential integrity and semantic coherence on file data.
|
||||
|
||||
Raises a validation exception on first error found."
|
||||
[{:keys [data features] :as file} libraries]
|
||||
(when (contains? features "components/v2")
|
||||
(doseq [page (filter :id (ctpl/pages-seq data))]
|
||||
(let [orphans (->> page
|
||||
:objects
|
||||
vals
|
||||
(filter #(not (contains? (:objects page) (:parent-id %))))
|
||||
(map :id))]
|
||||
(validate-shape! uuid/zero file page libraries)
|
||||
(doseq [shape-id orphans]
|
||||
(validate-shape! shape-id file page libraries))))
|
||||
|
||||
(doseq [component (vals (:components data))]
|
||||
(validate-component! component file)))
|
||||
|
||||
file)
|
||||
|
||||
(defn validate-file
|
||||
"Validate structure, referencial integrity and semantic coherence of
|
||||
all contents of a file. Returns a list of errors."
|
||||
Raises an exception"
|
||||
[file libraries]
|
||||
(binding [*errors* (volatile! [])]
|
||||
(validate-file! file libraries)
|
||||
(deref *errors*)))
|
||||
(when-let [errors (validate-file file libraries)]
|
||||
(ex/raise :type :validation
|
||||
:code :referential-integrity
|
||||
:hint "error on validating file referential integrity"
|
||||
:file-id (:id file)
|
||||
:details errors)))
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -353,3 +353,19 @@
|
||||
(mth/max by1 y1)
|
||||
(mth/min bx2 x2)
|
||||
(mth/min by2 y2)))))
|
||||
(defn fix-aspect-ratio
|
||||
[bounds aspect-ratio]
|
||||
(if aspect-ratio
|
||||
(let [width (dm/get-prop bounds :width)
|
||||
height (dm/get-prop bounds :height)
|
||||
target-height (* width aspect-ratio)
|
||||
target-width (* height (/ 1 aspect-ratio))]
|
||||
(cond-> bounds
|
||||
(> target-height height)
|
||||
(-> (assoc :height target-height)
|
||||
(update :y - (/ (- target-height height ) 2)))
|
||||
|
||||
(< target-height height)
|
||||
(-> (assoc :width target-width)
|
||||
(update :x - (/ (- target-width width ) 2)))))
|
||||
bounds))
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
/**
|
||||
* Performance focused pure java implementation of the
|
||||
* SVG path parser.
|
||||
*
|
||||
* @author KALEIDOS INC
|
||||
* @license MPL-2.0 <https://www.mozilla.org/en-US/MPL/2.0/>
|
||||
*/
|
||||
|
||||
package app.common.svg.path;
|
||||
|
||||
import java.util.Arrays;
|
||||
@@ -61,9 +69,11 @@ public class Parser {
|
||||
command = MOVE_TO;
|
||||
params = new Object[] {K_X, this.params[0], K_Y, this.params[1]};
|
||||
break;
|
||||
|
||||
case 'Z':
|
||||
command = CLOSE_PATH;
|
||||
break;
|
||||
|
||||
case 'L':
|
||||
command = LINE_TO;
|
||||
params = new Object[] {K_X, this.params[0], K_Y, this.params[1]};
|
||||
@@ -636,8 +646,9 @@ public class Parser {
|
||||
|
||||
for (int i=0; i<pdata.size(); i++) {
|
||||
Segment segment = pdata.get(i);
|
||||
var currentCommand = segment.command;
|
||||
|
||||
switch(segment.command) {
|
||||
switch(currentCommand) {
|
||||
case 'M':
|
||||
x = segment.params[0];
|
||||
y = segment.params[1];
|
||||
@@ -780,16 +791,6 @@ public class Parser {
|
||||
var segments = arcToBeziers(currentX, currentY, x, y, fa, fs, rx, ry, phi);
|
||||
result.addAll(segments);
|
||||
|
||||
|
||||
// if (rx == 0 || ry == 0) {
|
||||
// segment.command = 'C';
|
||||
// segment.params = new double[] {currentX, currentY, x, y, x, y};
|
||||
// result.add(segment);
|
||||
// } else if (currentX != x || currentY != y) {
|
||||
// var segments = arcToBeziers(currentX, currentY, x, y, fa, fs, rx, ry, phi);
|
||||
// result.addAll(segments);
|
||||
// }
|
||||
|
||||
currentX = x;
|
||||
currentY = y;
|
||||
|
||||
@@ -803,7 +804,7 @@ public class Parser {
|
||||
break;
|
||||
}
|
||||
|
||||
lastCommand = segment.command;
|
||||
lastCommand = currentCommand;
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -870,7 +871,6 @@ public class Parser {
|
||||
}
|
||||
|
||||
private static void processCurve(double[] curve, double cx, double cy, double rx, double ry, double sinPhi, double cosPhi) {
|
||||
|
||||
double x0 = curve[0] * rx;
|
||||
double y0 = curve[1] * ry;
|
||||
double x1 = curve[2] * rx;
|
||||
@@ -910,7 +910,13 @@ public class Parser {
|
||||
double x1p = ((cosPhi * (x1 - x2)) / 2) + ((sinPhi * (y1 - y2)) / 2);
|
||||
double y1p = ((-sinPhi * (x1 - x2)) / 2) + ((cosPhi * (y1 - y2)) / 2);
|
||||
|
||||
if (x1p == 0 || y1p == 0 || rx == 0 || ry == 0) {
|
||||
if (x1p == 0 && y1p == 0) {
|
||||
// we're asked to draw line to itself
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
if (rx == 0 || ry == 0) {
|
||||
// one of the radii is zero
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
* Arc to Bezier curves transformer
|
||||
*
|
||||
* Is a modified and google closure compatible version of the a2c
|
||||
* functions by https://github.com/fontello/svgpath
|
||||
* functions by https://github.com/fontello/svgpath used as reference
|
||||
* implementation for tests
|
||||
*
|
||||
* @author KALEIDOS INC
|
||||
* @license MIT License <https://opensource.org/licenses/MIT>
|
||||
@@ -10,11 +11,11 @@
|
||||
|
||||
"use strict";
|
||||
|
||||
goog.provide("common_tests.arc_to_bezier");
|
||||
goog.provide("app.common.svg.path.arc_to_bezier");
|
||||
|
||||
// https://raw.githubusercontent.com/fontello/svgpath/master/lib/a2c.js
|
||||
goog.scope(function() {
|
||||
const self = common_tests.arc_to_bezier;
|
||||
const self = app.common.svg.path.arc_to_bezier;
|
||||
|
||||
var TAU = Math.PI * 2;
|
||||
|
||||
@@ -123,7 +124,7 @@ goog.scope(function() {
|
||||
return [ x1, y1, x1 - y1*alpha, y1 + x1*alpha, x2 + y2*alpha, y2 - x2*alpha, x2, y2 ];
|
||||
}
|
||||
|
||||
function a2c(x1, y1, x2, y2, fa, fs, rx, ry, phi) {
|
||||
function calculate_beziers(x1, y1, x2, y2, fa, fs, rx, ry, phi) {
|
||||
var sin_phi = Math.sin(phi * TAU / 360);
|
||||
var cos_phi = Math.cos(phi * TAU / 360);
|
||||
|
||||
@@ -132,6 +133,8 @@ goog.scope(function() {
|
||||
var x1p = cos_phi*(x1-x2)/2 + sin_phi*(y1-y2)/2;
|
||||
var y1p = -sin_phi*(x1-x2)/2 + cos_phi*(y1-y2)/2;
|
||||
|
||||
// console.log("L", x1p, y1p)
|
||||
|
||||
if (x1p === 0 && y1p === 0) {
|
||||
// we're asked to draw line to itself
|
||||
return [];
|
||||
@@ -204,5 +207,5 @@ goog.scope(function() {
|
||||
});
|
||||
}
|
||||
|
||||
self.a2c = a2c;
|
||||
self.calculateBeziers = calculate_beziers;
|
||||
});
|
||||
325
common/src/app/common/svg/path/legacy_parser1.cljs
Normal file
325
common/src/app/common/svg/path/legacy_parser1.cljs
Normal file
@@ -0,0 +1,325 @@
|
||||
;; 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
|
||||
|
||||
(ns app.common.svg.path.legacy-parser1
|
||||
"The first SVG Path parser implementation.
|
||||
|
||||
Written in a mix of CLJS and JS code and used in production until
|
||||
1.19, used mainly for tests."
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.geom.shapes.path :as upg]
|
||||
[app.common.svg :as csvg]
|
||||
[app.common.svg.path.arc-to-bezier :as a2b]
|
||||
[app.common.svg.path.command :as upc]
|
||||
[cuerdas.core :as str]))
|
||||
|
||||
(def commands-regex #"(?i)[mzlhvcsqta][^mzlhvcsqta]*")
|
||||
|
||||
;; Matches numbers for path values allows values like... -.01, 10, +12.22
|
||||
;; 0 and 1 are special because can refer to flags
|
||||
(def num-regex #"[+-]?(\d+(\.\d+)?|\.\d+)(e[+-]?\d+)?")
|
||||
|
||||
(def flag-regex #"[01]")
|
||||
|
||||
(defn extract-params [cmd-str extract-commands]
|
||||
(loop [result []
|
||||
extract-idx 0
|
||||
current {}
|
||||
remain (-> cmd-str (subs 1) (str/trim))]
|
||||
|
||||
(let [[param type] (nth extract-commands extract-idx)
|
||||
regex (case type
|
||||
:flag flag-regex
|
||||
#_:number num-regex)
|
||||
match (re-find regex remain)]
|
||||
|
||||
(if match
|
||||
(let [value (-> match first csvg/fix-dot-number d/read-string)
|
||||
remain (str/replace-first remain regex "")
|
||||
current (assoc current param value)
|
||||
extract-idx (inc extract-idx)
|
||||
[result current extract-idx]
|
||||
(if (>= extract-idx (count extract-commands))
|
||||
[(conj result current) {} 0]
|
||||
[result current extract-idx])]
|
||||
(recur result
|
||||
extract-idx
|
||||
current
|
||||
remain))
|
||||
(cond-> result
|
||||
(seq current) (conj current))))))
|
||||
|
||||
;; Path specification
|
||||
;; https://www.w3.org/TR/SVG11/paths.html
|
||||
(defmulti parse-command (comp str/upper first))
|
||||
|
||||
(defmethod parse-command "M" [cmd]
|
||||
(let [relative (str/starts-with? cmd "m")
|
||||
param-list (extract-params cmd [[:x :number]
|
||||
[:y :number]])]
|
||||
|
||||
(into [{:command :move-to
|
||||
:relative relative
|
||||
:params (first param-list)}]
|
||||
|
||||
(for [params (rest param-list)]
|
||||
{:command :line-to
|
||||
:relative relative
|
||||
:params params}))))
|
||||
|
||||
(defmethod parse-command "Z" [_]
|
||||
[{:command :close-path}])
|
||||
|
||||
(defmethod parse-command "L" [cmd]
|
||||
(let [relative (str/starts-with? cmd "l")
|
||||
param-list (extract-params cmd [[:x :number]
|
||||
[:y :number]])]
|
||||
(for [params param-list]
|
||||
{:command :line-to
|
||||
:relative relative
|
||||
:params params})))
|
||||
|
||||
(defmethod parse-command "H" [cmd]
|
||||
(let [relative (str/starts-with? cmd "h")
|
||||
param-list (extract-params cmd [[:value :number]])]
|
||||
(for [params param-list]
|
||||
{:command :line-to-horizontal
|
||||
:relative relative
|
||||
:params params})))
|
||||
|
||||
(defmethod parse-command "V" [cmd]
|
||||
(let [relative (str/starts-with? cmd "v")
|
||||
param-list (extract-params cmd [[:value :number]])]
|
||||
(for [params param-list]
|
||||
{:command :line-to-vertical
|
||||
:relative relative
|
||||
:params params})))
|
||||
|
||||
(defmethod parse-command "C" [cmd]
|
||||
(let [relative (str/starts-with? cmd "c")
|
||||
param-list (extract-params cmd [[:c1x :number]
|
||||
[:c1y :number]
|
||||
[:c2x :number]
|
||||
[:c2y :number]
|
||||
[:x :number]
|
||||
[:y :number]])
|
||||
]
|
||||
(for [params param-list]
|
||||
{:command :curve-to
|
||||
:relative relative
|
||||
:params params})))
|
||||
|
||||
(defmethod parse-command "S" [cmd]
|
||||
(let [relative (str/starts-with? cmd "s")
|
||||
param-list (extract-params cmd [[:cx :number]
|
||||
[:cy :number]
|
||||
[:x :number]
|
||||
[:y :number]])]
|
||||
(for [params param-list]
|
||||
{:command :smooth-curve-to
|
||||
:relative relative
|
||||
:params params})))
|
||||
|
||||
(defmethod parse-command "Q" [cmd]
|
||||
(let [relative (str/starts-with? cmd "q")
|
||||
param-list (extract-params cmd [[:cx :number]
|
||||
[:cy :number]
|
||||
[:x :number]
|
||||
[:y :number]])]
|
||||
(for [params param-list]
|
||||
{:command :quadratic-bezier-curve-to
|
||||
:relative relative
|
||||
:params params})))
|
||||
|
||||
(defmethod parse-command "T" [cmd]
|
||||
(let [relative (str/starts-with? cmd "t")
|
||||
param-list (extract-params cmd [[:x :number]
|
||||
[:y :number]])]
|
||||
(for [params param-list]
|
||||
{:command :smooth-quadratic-bezier-curve-to
|
||||
:relative relative
|
||||
:params params})))
|
||||
|
||||
(defmethod parse-command "A" [cmd]
|
||||
(let [relative (str/starts-with? cmd "a")
|
||||
param-list (extract-params cmd [[:rx :number]
|
||||
[:ry :number]
|
||||
[:x-axis-rotation :number]
|
||||
[:large-arc-flag :flag]
|
||||
[:sweep-flag :flag]
|
||||
[:x :number]
|
||||
[:y :number]])]
|
||||
(for [params param-list]
|
||||
{:command :elliptical-arc
|
||||
:relative relative
|
||||
:params params})))
|
||||
|
||||
(defn smooth->curve
|
||||
[{:keys [params]} pos handler]
|
||||
(let [{c1x :x c1y :y} (upg/calculate-opposite-handler pos handler)]
|
||||
{:c1x c1x
|
||||
:c1y c1y
|
||||
:c2x (:cx params)
|
||||
:c2y (:cy params)}))
|
||||
|
||||
(defn quadratic->curve
|
||||
[sp ep cp]
|
||||
(let [cp1 (-> (gpt/to-vec sp cp)
|
||||
(gpt/scale (/ 2 3))
|
||||
(gpt/add sp))
|
||||
|
||||
cp2 (-> (gpt/to-vec ep cp)
|
||||
(gpt/scale (/ 2 3))
|
||||
(gpt/add ep))]
|
||||
|
||||
{:c1x (:x cp1)
|
||||
:c1y (:y cp1)
|
||||
:c2x (:x cp2)
|
||||
:c2y (:y cp2)}))
|
||||
|
||||
(defn arc->beziers*
|
||||
[from-x from-y x y large-arc-flag sweep-flag rx ry x-axis-rotation]
|
||||
(a2b/calculateBeziers from-x from-y x y large-arc-flag sweep-flag rx ry x-axis-rotation))
|
||||
|
||||
(defn arc->beziers [from-p command]
|
||||
(let [to-command
|
||||
(fn [[_ _ c1x c1y c2x c2y x y]]
|
||||
{:command :curve-to
|
||||
:relative (:relative command)
|
||||
:params {:c1x c1x :c1y c1y
|
||||
:c2x c2x :c2y c2y
|
||||
:x x :y y}})
|
||||
|
||||
{from-x :x from-y :y} from-p
|
||||
{:keys [rx ry x-axis-rotation large-arc-flag sweep-flag x y]} (:params command)
|
||||
result (arc->beziers* from-x from-y x y large-arc-flag sweep-flag rx ry x-axis-rotation)]
|
||||
(mapv to-command result)))
|
||||
|
||||
(defn simplify-commands
|
||||
"Removes some commands and convert relative to absolute coordinates"
|
||||
[commands]
|
||||
(let [simplify-command
|
||||
;; prev-pos : previous position for the current path. Necessary for relative commands
|
||||
;; prev-start : previous move-to necessary for Z commands
|
||||
;; prev-cc : previous command control point for cubic beziers
|
||||
;; prev-qc : previous command control point for quadratic curves
|
||||
(fn [[result prev-pos prev-start prev-cc prev-qc] [command _prev]]
|
||||
(let [command (assoc command :prev-pos prev-pos)
|
||||
|
||||
command
|
||||
(cond-> command
|
||||
(:relative command)
|
||||
(-> (assoc :relative false)
|
||||
(d/update-in-when [:params :c1x] + (:x prev-pos))
|
||||
(d/update-in-when [:params :c1y] + (:y prev-pos))
|
||||
|
||||
(d/update-in-when [:params :c2x] + (:x prev-pos))
|
||||
(d/update-in-when [:params :c2y] + (:y prev-pos))
|
||||
|
||||
(d/update-in-when [:params :cx] + (:x prev-pos))
|
||||
(d/update-in-when [:params :cy] + (:y prev-pos))
|
||||
|
||||
(d/update-in-when [:params :x] + (:x prev-pos))
|
||||
(d/update-in-when [:params :y] + (:y prev-pos))
|
||||
|
||||
(cond->
|
||||
(= :line-to-horizontal (:command command))
|
||||
(d/update-in-when [:params :value] + (:x prev-pos))
|
||||
|
||||
(= :line-to-vertical (:command command))
|
||||
(d/update-in-when [:params :value] + (:y prev-pos)))))
|
||||
|
||||
params (:params command)
|
||||
orig-command command
|
||||
|
||||
command
|
||||
(cond-> command
|
||||
(= :line-to-horizontal (:command command))
|
||||
(-> (assoc :command :line-to)
|
||||
(update :params dissoc :value)
|
||||
(assoc-in [:params :x] (:value params))
|
||||
(assoc-in [:params :y] (:y prev-pos)))
|
||||
|
||||
(= :line-to-vertical (:command command))
|
||||
(-> (assoc :command :line-to)
|
||||
(update :params dissoc :value)
|
||||
(assoc-in [:params :y] (:value params))
|
||||
(assoc-in [:params :x] (:x prev-pos)))
|
||||
|
||||
(= :smooth-curve-to (:command command))
|
||||
(-> (assoc :command :curve-to)
|
||||
(update :params dissoc :cx :cy)
|
||||
(update :params merge (smooth->curve command prev-pos prev-cc)))
|
||||
|
||||
(= :quadratic-bezier-curve-to (:command command))
|
||||
(-> (assoc :command :curve-to)
|
||||
(update :params dissoc :cx :cy)
|
||||
(update :params merge (quadratic->curve prev-pos (gpt/point params) (gpt/point (:cx params) (:cy params)))))
|
||||
|
||||
(= :smooth-quadratic-bezier-curve-to (:command command))
|
||||
(-> (assoc :command :curve-to)
|
||||
(update :params merge (quadratic->curve prev-pos (gpt/point params) (upg/calculate-opposite-handler prev-pos prev-qc)))))
|
||||
|
||||
result (if (= :elliptical-arc (:command command))
|
||||
(into result (arc->beziers prev-pos command))
|
||||
(conj result command))
|
||||
|
||||
next-cc (case (:command orig-command)
|
||||
:smooth-curve-to
|
||||
(gpt/point (get-in orig-command [:params :cx]) (get-in orig-command [:params :cy]))
|
||||
|
||||
:curve-to
|
||||
(gpt/point (get-in orig-command [:params :c2x]) (get-in orig-command [:params :c2y]))
|
||||
|
||||
(:line-to-horizontal :line-to-vertical)
|
||||
(gpt/point (get-in command [:params :x]) (get-in command [:params :y]))
|
||||
|
||||
(gpt/point (get-in orig-command [:params :x]) (get-in orig-command [:params :y])))
|
||||
|
||||
next-qc (case (:command orig-command)
|
||||
:quadratic-bezier-curve-to
|
||||
(gpt/point (get-in orig-command [:params :cx]) (get-in orig-command [:params :cy]))
|
||||
|
||||
:smooth-quadratic-bezier-curve-to
|
||||
(upg/calculate-opposite-handler prev-pos prev-qc)
|
||||
|
||||
(gpt/point (get-in orig-command [:params :x]) (get-in orig-command [:params :y])))
|
||||
|
||||
next-pos (if (= :close-path (:command command))
|
||||
prev-start
|
||||
(upc/command->point prev-pos command))
|
||||
|
||||
next-start (if (= :move-to (:command command)) next-pos prev-start)]
|
||||
|
||||
[result next-pos next-start next-cc next-qc]))
|
||||
|
||||
start (first commands)
|
||||
start (cond-> start
|
||||
(:relative start)
|
||||
(assoc :relative false))
|
||||
|
||||
start-pos (gpt/point (:params start))]
|
||||
|
||||
(->> (map vector (rest commands) commands)
|
||||
(reduce simplify-command [[start] start-pos start-pos start-pos start-pos])
|
||||
(first))))
|
||||
|
||||
(defn parse [path-str]
|
||||
(if (empty? path-str)
|
||||
path-str
|
||||
(let [clean-path-str
|
||||
(-> path-str
|
||||
(str/trim)
|
||||
;; Change "commas" for spaces
|
||||
(str/replace #"," " ")
|
||||
;; Remove all consecutive spaces
|
||||
(str/replace #"\s+" " "))
|
||||
commands (re-seq commands-regex clean-path-str)]
|
||||
(-> (mapcat parse-command commands)
|
||||
(simplify-commands)))))
|
||||
|
||||
@@ -4,9 +4,11 @@
|
||||
;;
|
||||
;; Copyright (c) KALEIDOS INC
|
||||
|
||||
(ns app.common.svg.path.legacy
|
||||
"The first svg path parser implementation in pure clojure, used as reference impl
|
||||
and for tests."
|
||||
(ns app.common.svg.path.legacy-parser2
|
||||
"The second SVG Path parser implementation.
|
||||
|
||||
Written in crossplatform CLJC code. Used meanwhile a hight
|
||||
performance parser is developed in the 1.20 version."
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.geom.point :as gpt]
|
||||
@@ -16,7 +18,6 @@
|
||||
[app.common.svg.path.command :as upc]
|
||||
[cuerdas.core :as str]))
|
||||
|
||||
|
||||
(def commands-regex #"(?i)[mzlhvcsqta][^mzlhvcsqta]*")
|
||||
(def regex #"[+-]?(\d+(\.\d+)?|\.\d+)(e[+-]?\d+)?")
|
||||
|
||||
@@ -296,10 +297,10 @@
|
||||
y1p (+ (/ (* (- sin-phi) (- x1 x2)) 2)
|
||||
(/ (* cos-phi (- y1 y2)) 2))]
|
||||
|
||||
(if (or (zero? x1p)
|
||||
(zero? y1p)
|
||||
(zero? rx)
|
||||
(zero? ry))
|
||||
(if (or (and (zero? x1p)
|
||||
(zero? y1p))
|
||||
(and (zero? rx)
|
||||
(zero? ry)))
|
||||
[]
|
||||
(let [
|
||||
rx (mth/abs rx)
|
||||
@@ -462,19 +463,10 @@
|
||||
(reduce simplify-command [[start] start-pos start-pos start-pos start-pos])
|
||||
(first))))
|
||||
|
||||
|
||||
(defn parse
|
||||
[path-str]
|
||||
(if (empty? path-str)
|
||||
path-str
|
||||
(let [commands (re-seq commands-regex path-str)]
|
||||
(->> (mapcat parse-command commands)
|
||||
(simplify-commands)
|
||||
(map (fn [segment]
|
||||
;; (prn "LEGACY:" segment)
|
||||
segment))))))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
(simplify-commands)))))
|
||||
@@ -1,3 +1,11 @@
|
||||
/**
|
||||
* Performance focused pure javascript implementation of the
|
||||
* SVG path parser.
|
||||
*
|
||||
* @author KALEIDOS INC
|
||||
* @license MPL-2.0 <https://www.mozilla.org/en-US/MPL/2.0/>
|
||||
*/
|
||||
|
||||
import cljs from "goog:cljs.core";
|
||||
|
||||
const MOVE_TO = cljs.keyword("move-to");
|
||||
@@ -674,7 +682,13 @@ export function arcToBeziers(x1, y1, x2, y2, fa, fs, rx, ry, phi) {
|
||||
let x1p = (cosPhi * (x1 - x2)) / 2 + (sinPhi * (y1 - y2)) / 2;
|
||||
let y1p = (-sinPhi * (x1 - x2)) / 2 + (cosPhi * (y1 - y2)) / 2;
|
||||
|
||||
if (x1p === 0 || y1p === 0 || rx === 0 || ry === 0) {
|
||||
if (x1p === 0 && y1p === 0) {
|
||||
// we're asked to draw line to itself
|
||||
return [];
|
||||
}
|
||||
|
||||
if (rx === 0 || ry === 0) {
|
||||
// one of the radii is zero
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -711,7 +725,7 @@ export function arcToBeziers(x1, y1, x2, y2, fa, fs, rx, ry, phi) {
|
||||
// commands.
|
||||
function simplifyPathData(pdata) {
|
||||
var result = [];
|
||||
var lastType = null;
|
||||
var lastCommand = null;
|
||||
|
||||
var lastControlX = null;
|
||||
var lastControlY = null;
|
||||
@@ -724,8 +738,9 @@ function simplifyPathData(pdata) {
|
||||
|
||||
for (let i=0; i<pdata.length; i++) {
|
||||
const segment = pdata[i];
|
||||
const currentCommand = segment.command;
|
||||
|
||||
switch(segment.command) {
|
||||
switch(currentCommand) {
|
||||
case "M":
|
||||
var x = segment.params[0];
|
||||
var y = segment.params[1];
|
||||
@@ -787,7 +802,7 @@ function simplifyPathData(pdata) {
|
||||
|
||||
var cx1, cy1;
|
||||
|
||||
if (lastType === "C" || lastType === "S") {
|
||||
if (lastCommand === "C" || lastCommand === "S") {
|
||||
cx1 = currentX + (currentX - lastControlX);
|
||||
cy1 = currentY + (currentY - lastControlY);
|
||||
} else {
|
||||
@@ -813,7 +828,7 @@ function simplifyPathData(pdata) {
|
||||
|
||||
var x1, y1;
|
||||
|
||||
if (lastType === "Q" || lastType === "T") {
|
||||
if (lastCommand === "Q" || lastCommand === "T") {
|
||||
x1 = currentX + (currentX - lastControlX);
|
||||
y1 = currentY + (currentY - lastControlY);
|
||||
} else {
|
||||
@@ -876,6 +891,7 @@ function simplifyPathData(pdata) {
|
||||
currentX = x;
|
||||
currentY = y;
|
||||
} else if (currentX !== x || currentY !== y) {
|
||||
|
||||
var segments = arcToBeziers(currentX, currentY, x, y, fa, fs, rx, ry, phi);
|
||||
result.push(...segments);
|
||||
|
||||
@@ -891,7 +907,7 @@ function simplifyPathData(pdata) {
|
||||
break;
|
||||
}
|
||||
|
||||
lastCommand = segment.command;
|
||||
lastCommand = currentCommand;
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
@@ -229,6 +229,7 @@
|
||||
:svg-viewbox selrect
|
||||
:svg-attrs attrs
|
||||
:svg-transform transform
|
||||
:strokes []
|
||||
:fills []})
|
||||
(gsh/translate-to-frame origin)))))
|
||||
|
||||
@@ -355,9 +356,9 @@
|
||||
(assoc :svg-attrs props))))))
|
||||
|
||||
(defn setup-fill
|
||||
[shape]
|
||||
(let [color-attr (str/trim (dm/get-in shape [:svg-attrs :fill]))
|
||||
color-attr (if (= color-attr "currentColor") clr/black color-attr)
|
||||
[shape]
|
||||
(let [color-attr (str/trim (dm/get-in shape [:svg-attrs :fill]))
|
||||
color-attr (if (= color-attr "currentColor") clr/black color-attr)
|
||||
color-style (str/trim (dm/get-in shape [:svg-attrs :style :fill]))
|
||||
color-style (if (= color-style "currentColor") clr/black color-style)]
|
||||
(cond-> shape
|
||||
@@ -384,6 +385,7 @@
|
||||
(update :svg-attrs dissoc :fillOpacity)
|
||||
(assoc-in [:fills 0 :fill-opacity] (-> (dm/get-in shape [:svg-attrs :style :fillOpacity])
|
||||
(d/parse-double 1)))))))
|
||||
|
||||
(defn- setup-stroke
|
||||
[shape]
|
||||
(let [attrs (get shape :svg-attrs)
|
||||
@@ -422,7 +424,8 @@
|
||||
(dissoc :stroke)
|
||||
(dissoc :strokeLinecap)
|
||||
(dissoc :strokeWidth)
|
||||
(dissoc :strokeOpacity)))))]
|
||||
(dissoc :strokeOpacity))))
|
||||
(d/without-nils))]
|
||||
|
||||
(cond-> (assoc shape :svg-attrs attrs)
|
||||
(some? color)
|
||||
@@ -434,7 +437,7 @@
|
||||
(and (some? color) (some? width))
|
||||
(assoc-in [:strokes 0 :stroke-width] width)
|
||||
|
||||
(and (some? linecap) (= (:type shape) :path)
|
||||
(and (some? linecap) (cfh/path-shape? shape)
|
||||
(or (= linecap :round) (= linecap :square)))
|
||||
(assoc :stroke-cap-start linecap
|
||||
:stroke-cap-end linecap)
|
||||
@@ -464,9 +467,6 @@
|
||||
(-> (update-in [:svg-attrs :style] dissoc :mixBlendMode)
|
||||
(assoc :blend-mode (-> (dm/get-in shape [:svg-attrs :style :mixBlendMode]) assert-valid-blend-mode)))))
|
||||
|
||||
|
||||
|
||||
|
||||
(defn tag->name
|
||||
"Given a tag returns its layer name"
|
||||
[tag]
|
||||
|
||||
@@ -1,7 +1,18 @@
|
||||
(ns app.common.thumbnails
|
||||
(:require [cuerdas.core :as str]))
|
||||
(:require
|
||||
[app.common.uuid :as uuid]
|
||||
[cuerdas.core :as str]))
|
||||
|
||||
(defn fmt-object-id
|
||||
"Returns ids formatted as a string (object-id)"
|
||||
[file-id page-id frame-id tag]
|
||||
(str/ffmt "%/%/%/%" file-id page-id frame-id tag))
|
||||
|
||||
(defn file-id?
|
||||
"Returns ids formatted as a string (file-id)"
|
||||
[object-id file-id]
|
||||
(str/starts-with? object-id (str/concat file-id "/")))
|
||||
|
||||
(defn get-file-id
|
||||
[object-id]
|
||||
(uuid/uuid (str/slice object-id 0 (str/index-of object-id "/"))))
|
||||
|
||||
@@ -676,7 +676,7 @@
|
||||
[id cell])))
|
||||
|
||||
(defn remove-grid-column
|
||||
[parent index]
|
||||
[parent index objects]
|
||||
|
||||
(let [track-num (inc index)
|
||||
|
||||
@@ -692,10 +692,10 @@
|
||||
(-> parent
|
||||
(update :layout-grid-columns d/remove-at-index index)
|
||||
(update :layout-grid-cells update-cells)
|
||||
(assign-cells))))
|
||||
(assign-cells objects))))
|
||||
|
||||
(defn remove-grid-row
|
||||
[parent index]
|
||||
[parent index objects]
|
||||
(let [track-num (inc index)
|
||||
|
||||
decrease-track-num (make-decrease-track-num :row :row-span track-num)
|
||||
@@ -710,7 +710,7 @@
|
||||
(-> parent
|
||||
(update :layout-grid-rows d/remove-at-index index)
|
||||
(update :layout-grid-cells update-cells)
|
||||
(assign-cells))))
|
||||
(assign-cells objects))))
|
||||
|
||||
(defn- reorder-grid-tracks
|
||||
"Swap the positions of the tracks info"
|
||||
@@ -781,6 +781,7 @@
|
||||
|
||||
parent
|
||||
(reorder-grid-tracks parent tracks-props from-index to-index)]
|
||||
|
||||
(cond-> parent
|
||||
move-content?
|
||||
(swap-track-content prop from-track to-track))))
|
||||
@@ -828,14 +829,24 @@
|
||||
|
||||
(defn check-deassigned-cells
|
||||
"Clean the cells whith shapes that are no longer in the layout"
|
||||
[parent]
|
||||
[parent objects]
|
||||
|
||||
(let [child? (set (:shapes parent))
|
||||
cells (update-vals
|
||||
(:layout-grid-cells parent)
|
||||
(fn [cell] (update cell :shapes #(filterv child? %))))]
|
||||
(let [child-set (set (:shapes parent))
|
||||
|
||||
(assoc parent :layout-grid-cells cells)))
|
||||
assigned?
|
||||
(fn [id]
|
||||
(and (contains? child-set id)
|
||||
(not (position-absolute? objects id))))
|
||||
|
||||
cells
|
||||
(update-vals
|
||||
(:layout-grid-cells parent)
|
||||
(fn [cell]
|
||||
(-> cell
|
||||
(update :shapes #(filterv assigned? %)))))]
|
||||
|
||||
(-> parent
|
||||
(assoc :layout-grid-cells cells))))
|
||||
|
||||
(defn overlapping-cells
|
||||
"Find overlapping cells"
|
||||
@@ -902,12 +913,12 @@
|
||||
;; - Shape duplication
|
||||
;; - (maybe) create group/frames. This case will assigna a cell that had one of its children
|
||||
(defn assign-cells
|
||||
[parent]
|
||||
[parent objects]
|
||||
(let [;; TODO: Remove this, shouldn't be happening
|
||||
;;overlaps (overlapping-cells parent)
|
||||
;;_ (when (not (empty? overlaps))
|
||||
;; (.warn js/console "OVERLAPS" overlaps))
|
||||
parent (cond-> (check-deassigned-cells parent)
|
||||
parent (cond-> (check-deassigned-cells parent objects)
|
||||
#_(d/not-empty? overlaps)
|
||||
#_(fix-overlaps overlaps))
|
||||
|
||||
@@ -915,7 +926,9 @@
|
||||
(into #{} (mapcat (comp :shapes second)) (:layout-grid-cells parent))
|
||||
|
||||
no-cell-shapes
|
||||
(->> (:shapes parent) (remove shape-has-cell?))
|
||||
(->> (:shapes parent)
|
||||
(remove shape-has-cell?)
|
||||
(remove (partial position-absolute? objects)))
|
||||
|
||||
parent (position-auto-shapes parent)]
|
||||
|
||||
@@ -1174,7 +1187,7 @@
|
||||
(let [;; Temporary remove the children when moving them
|
||||
frame (-> frame
|
||||
(update :shapes #(d/removev children %))
|
||||
(assign-cells))
|
||||
(assign-cells objects))
|
||||
|
||||
children (->> children (remove #(position-absolute? objects %)))]
|
||||
|
||||
@@ -1182,7 +1195,7 @@
|
||||
(update :shapes d/concat-vec children)
|
||||
(cond-> (some? cell)
|
||||
(push-into-cell children row column))
|
||||
(assign-cells))))
|
||||
(assign-cells objects))))
|
||||
|
||||
(defn add-children-to-index
|
||||
[parent ids objects to-index]
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -10,9 +10,9 @@
|
||||
[app.common.pprint :as pp]
|
||||
[app.common.math :as mth]
|
||||
[app.common.svg.path :as svg.path]
|
||||
[app.common.svg.path.legacy :as svg.path.legacy]
|
||||
[app.common.svg.path.legacy-parser2 :as svg.path.legacy2]
|
||||
[clojure.test :as t]
|
||||
#?(:cljs [common-tests.arc-to-bezier :as impl])))
|
||||
#?(:cljs [app.common.svg.path.legacy-parser2 :as svg.path.legacy1])))
|
||||
|
||||
(t/deftest parse-test-1
|
||||
(let [data (str "m -994.563 4564.1423 149.3086 -52.8821 30.1828 "
|
||||
@@ -23,14 +23,25 @@
|
||||
result1 (->> (svg.path/parse data)
|
||||
(mapv (fn [entry]
|
||||
(update entry :params #(into (sorted-map) %)))))
|
||||
result2 (->> (svg.path.legacy/parse data)
|
||||
result2 (->> (svg.path.legacy2/parse data)
|
||||
(mapv (fn [entry]
|
||||
(update entry :params #(into (sorted-map) %)))))]
|
||||
(update entry :params #(into (sorted-map) %)))))
|
||||
|
||||
result3 #?(:cljs (->> (svg.path.legacy1/parse data)
|
||||
(mapv (fn [entry]
|
||||
(update entry :params #(into (sorted-map) %)))))
|
||||
:clj nil)]
|
||||
|
||||
(t/is (= 15
|
||||
(count result1)
|
||||
(count result2)))
|
||||
|
||||
|
||||
#?(:cljs
|
||||
(t/is (= 15
|
||||
(count result1)
|
||||
(count result3))))
|
||||
|
||||
(dotimes [i (count result1)]
|
||||
(let [item1 (nth result1 i)
|
||||
item2 (nth result2 i)]
|
||||
@@ -40,6 +51,14 @@
|
||||
(t/is (= (:params item1)
|
||||
(:params item2)))
|
||||
|
||||
#?(:cljs
|
||||
(let [item3 (nth result3 i)]
|
||||
(t/is (= (:command item1)
|
||||
(:command item3)))
|
||||
(t/is (= (:params item1)
|
||||
(:params item3)))))
|
||||
|
||||
|
||||
#_(println "------------------------")
|
||||
#_(pp/pprint (dissoc item1 :relative))
|
||||
#_(pp/pprint (dissoc item2 :prev-pos :relative))))))
|
||||
@@ -92,7 +111,7 @@
|
||||
result1 (->> (svg.path/parse data)
|
||||
(mapv (fn [entry]
|
||||
(update entry :params #(into (sorted-map) %)))))
|
||||
result2 (->> (svg.path.legacy/parse data)
|
||||
result2 (->> (svg.path.legacy2/parse data)
|
||||
(mapv (fn [entry]
|
||||
(update entry :params #(into (sorted-map) %)))))]
|
||||
|
||||
@@ -108,7 +127,6 @@
|
||||
(t/is (= (:command item1)
|
||||
(:command item2)))
|
||||
|
||||
|
||||
;; (println "================" (:command item1))
|
||||
;; (pp/pprint (:params item1))
|
||||
;; (println "---------")
|
||||
@@ -124,7 +142,7 @@
|
||||
result1 (->> (svg.path/parse data)
|
||||
(mapv (fn [entry]
|
||||
(update entry :params #(into (sorted-map) %)))))
|
||||
result2 (->> (svg.path.legacy/parse data)
|
||||
result2 (->> (svg.path.legacy2/parse data)
|
||||
(mapv (fn [entry]
|
||||
(update entry :params #(into (sorted-map) %)))))]
|
||||
|
||||
@@ -142,6 +160,198 @@
|
||||
(doseq [[k v] (:params item1)]
|
||||
(t/is (mth/close? v (get-in item2 [:params k]) 0.000000001)))))))
|
||||
|
||||
|
||||
(t/deftest parse-test-4
|
||||
(let [data (str "m480 839-41-37c-70.512-64.747-128.807-120.601-174.884-167.561C218.039 587.48 181.333 "
|
||||
"545.5 154 508.5S107.5 438 96.5 408 80 347.667 80 317c0-60.103 20.167-110.296 "
|
||||
"60.5-150.577C180.833 126.141 230.667 106 290 106c38 0 73.167 9 105.5 27s60.5 44 84.5 "
|
||||
"78c28-36 57.667-62.5 89-79.5S634 106 670 106c59.333 0 109.167 20.141 149.5 60.423C859.833 "
|
||||
"206.704 880 256.897 880 317c0 30.667-5.5 61-16.5 91s-30.167 63.5-57.5 100.5-64.039 "
|
||||
"78.98-110.116 125.939C649.807 681.399 591.512 737.253 521 802l-41 37Zm0-79c67.491-61.997 "
|
||||
"123.03-115.163 166.618-159.498C690.206 556.167 724.833 517.333 750.5 484s43.667-63.045 "
|
||||
"54-89.135c10.333-26.091 15.5-51.997 15.5-77.72 0-44.097-14-80.312-42-108.645S714.075 166 "
|
||||
"670.225 166c-34.349 0-66.141 10.5-95.375 31.5C545.617 218.5 522 248 504 286h-49c-17.333"
|
||||
"-37.333-40.617-66.667-69.85-88-29.234-21.333-61.026-32-95.375-32C245.925 166 210 180.167 "
|
||||
"182 208.5s-42 64.605-42 108.816c0 25.789 5.167 51.851 15.5 78.184s28.333 56.333 54 90S270 "
|
||||
"558 314 602s99.333 96.667 166 158Zm0-297Z")
|
||||
|
||||
expect [{:command :move-to, :params {:x 480.0, :y 839.0}}
|
||||
{:command :line-to, :params {:x 439.0, :y 802.0}}
|
||||
{:command :curve-to, :params {:c1x 368.488, :c1y 737.253, :c2x 310.193, :c2y 681.399, :x 264.116, :y 634.439}}
|
||||
{:command :curve-to, :params {:c1x 218.039, :c1y 587.48, :c2x 181.333, :c2y 545.5, :x 154.0, :y 508.5}}
|
||||
{:command :curve-to, :params {:c1x 126.667, :c1y 471.5, :c2x 107.5, :c2y 438.0, :x 96.5, :y 408.0}}
|
||||
{:command :curve-to, :params {:c1x 85.5, :c1y 378.0, :c2x 80.0, :c2y 347.667, :x 80.0, :y 317.0}}
|
||||
{:command :curve-to, :params {:c1x 80.0, :c1y 256.897, :c2x 100.167, :c2y 206.704, :x 140.5, :y 166.423}}
|
||||
{:command :curve-to, :params {:c1x 180.833, :c1y 126.141, :c2x 230.667, :c2y 106.0, :x 290.0, :y 106.0}}
|
||||
{:command :curve-to, :params {:c1x 328.0, :c1y 106.0, :c2x 363.16700000000003, :c2y 115.0, :x 395.5, :y 133.0}}
|
||||
{:command :curve-to, :params {:c1x 427.83299999999997, :c1y 151.0, :c2x 456.0, :c2y 177.0, :x 480.0, :y 211.0}}
|
||||
{:command :curve-to, :params {:c1x 508.0, :c1y 175.0, :c2x 537.667, :c2y 148.5, :x 569.0, :y 131.5}}
|
||||
{:command :curve-to, :params {:c1x 600.333, :c1y 114.5, :c2x 634.0, :c2y 106.0, :x 670.0, :y 106.0}}
|
||||
{:command :curve-to, :params {:c1x 729.333, :c1y 106.0, :c2x 779.167, :c2y 126.14099999999999, :x 819.5, :y 166.423}}
|
||||
{:command :curve-to, :params {:c1x 859.833, :c1y 206.704, :c2x 880.0, :c2y 256.897, :x 880.0, :y 317.0}}
|
||||
{:command :curve-to, :params {:c1x 880.0, :c1y 347.66700000000003, :c2x 874.5, :c2y 378.0, :x 863.5, :y 408.0}}
|
||||
{:command :curve-to, :params {:c1x 852.5, :c1y 438.0, :c2x 833.333, :c2y 471.5, :x 806.0, :y 508.5}}
|
||||
{:command :curve-to, :params {:c1x 778.667, :c1y 545.5, :c2x 741.961, :c2y 587.48, :x 695.884, :y 634.439}}
|
||||
{:command :curve-to, :params {:c1x 649.807, :c1y 681.399, :c2x 591.512, :c2y 737.253, :x 521.0, :y 802.0}}
|
||||
{:command :line-to, :params {:x 480.0, :y 839.0}}
|
||||
{:command :close-path, :params {}}
|
||||
{:command :move-to, :params {:x 480.0, :y 760.0}}
|
||||
{:command :curve-to, :params {:c1x 547.491, :c1y 698.003, :c2x 603.03, :c2y 644.837, :x 646.6179999999999, :y 600.502}}
|
||||
{:command :curve-to, :params {:c1x 690.206, :c1y 556.167, :c2x 724.833, :c2y 517.333, :x 750.5, :y 484.0}}
|
||||
{:command :curve-to, :params {:c1x 776.167, :c1y 450.66700000000003, :c2x 794.167, :c2y 420.955, :x 804.5, :y 394.865}}
|
||||
{:command :curve-to, :params {:c1x 814.833, :c1y 368.774, :c2x 820.0, :c2y 342.868, :x 820.0, :y 317.145}}
|
||||
{:command :curve-to, :params {:c1x 820.0, :c1y 273.048, :c2x 806.0, :c2y 236.83299999999997, :x 778.0, :y 208.5}}
|
||||
{:command :curve-to, :params {:c1x 750.0, :c1y 180.16700000000003, :c2x 714.075, :c2y 166.0, :x 670.225, :y 166.0}}
|
||||
{:command :curve-to, :params {:c1x 635.876, :c1y 166.0, :c2x 604.0840000000001, :c2y 176.5, :x 574.85, :y 197.5}}
|
||||
{:command :curve-to, :params {:c1x 545.617, :c1y 218.5, :c2x 522.0, :c2y 248.0, :x 504.0, :y 286.0}}
|
||||
{:command :line-to, :params {:x 455.0, :y 286.0}}
|
||||
{:command :curve-to, :params {:c1x 437.66700000000003, :c1y 248.667, :c2x 414.383, :c2y 219.333, :x 385.15, :y 198.0}}
|
||||
{:command :curve-to, :params {:c1x 355.916, :c1y 176.667, :c2x 324.12399999999997, :c2y 166.0, :x 289.775, :y 166.0}}
|
||||
{:command :curve-to, :params {:c1x 245.925, :c1y 166.0, :c2x 210.0, :c2y 180.167, :x 182.0, :y 208.5}}
|
||||
{:command :curve-to, :params {:c1x 154.0, :c1y 236.833, :c2x 140.0, :c2y 273.105, :x 140.0, :y 317.31600000000003}}
|
||||
{:command :curve-to, :params {:c1x 140.0, :c1y 343.105, :c2x 145.167, :c2y 369.16700000000003, :x 155.5, :y 395.5}}
|
||||
{:command :curve-to,
|
||||
:params {:c1x 165.833, :c1y 421.83299999999997, :c2x 183.833, :c2y 451.83299999999997, :x 209.5, :y 485.5}}
|
||||
{:command :curve-to, :params {:c1x 235.167, :c1y 519.167, :c2x 270.0, :c2y 558.0, :x 314.0, :y 602.0}}
|
||||
{:command :curve-to, :params {:c1x 358.0, :c1y 646.0, :c2x 413.33299999999997, :c2y 698.667, :x 480.0, :y 760.0}}
|
||||
{:command :close-path, :params {}}
|
||||
{:command :move-to, :params {:x 480.0, :y 463.0}}
|
||||
{:command :close-path, :params {}}]
|
||||
|
||||
result1 (->> (svg.path/parse data)
|
||||
(mapv (fn [entry]
|
||||
(update entry :params #(into (sorted-map) %)))))
|
||||
result2 (->> (svg.path.legacy2/parse data)
|
||||
(mapv (fn [entry]
|
||||
(update entry :params #(into (sorted-map) %)))))]
|
||||
|
||||
(t/is (= 41
|
||||
(count result1)
|
||||
(count result2)))
|
||||
|
||||
;; (pp/pprint result1 {:length 50})
|
||||
|
||||
(dotimes [i (count result1)]
|
||||
(let [item1 (nth result1 i)
|
||||
item2 (nth result2 i)
|
||||
item3 (nth expect i)]
|
||||
|
||||
(t/is (= (:command item1)
|
||||
(:command item2)
|
||||
(:command item3)))
|
||||
|
||||
(doseq [[k v] (:params item1)]
|
||||
(t/is (mth/close? v (get-in item2 [:params k]) 0.000000001))
|
||||
(t/is (mth/close? v (get-in item3 [:params k]) 0.000000001))
|
||||
)))))
|
||||
|
||||
(t/deftest parse-test-5
|
||||
(let [data (str "M363 826"
|
||||
"q11-56 54-93"
|
||||
"t101-37"
|
||||
"h176"
|
||||
"q22-35 34-75.179 12-40.178 12-84.821 0-125.357-87.321-212.679"
|
||||
"Q565.357 236 440 236"
|
||||
"t-212.679 87.321"
|
||||
"Q140 410.643 140 536"
|
||||
"q0 105 63 184.5T363 826Zm157 190"
|
||||
"q-58 0-102"
|
||||
"-36.5T363 888q-122-26-202.5-124T80 536q0-150 105-255t255-105"
|
||||
"q150 0 255 105t105 "
|
||||
"255q0 43-9.5 83.5T763 696q66 0 111.5 47T920 856q0 66-47 113t-113 47H520Zm-80"
|
||||
"-485Zm200 325ZM520 956h240q42 0 71-29t29-71q0-42-29-71t-71-29H520q-42 0-71 29t"
|
||||
"-29 71q0 42 29 71t71 29Zm-.175-70Q507 886 498.5 877.325"
|
||||
"q-8.5-8.676-8.5-21.5 0"
|
||||
"-12.825 8.675-21.325 8.676-8.5 21.5-8.5 12.825 0 21.325 8.675 8.5 8.676 8.5 "
|
||||
"21.5 0 12.825-8.675 21.325-8.676 8.5-21.5 8.5Zm120 0Q627 886 618.5 877.325q-8.5"
|
||||
"-8.676-8.5-21.5 0-12.825 8.675-21.325 8.676-8.5 21.5-8.5 12.825 0 21.325 8.675 "
|
||||
"8.5 8.676 8.5 21.5 0 12.825-8.675 21.325-8.676 8.5-21.5 8.5Zm120 0Q747 886 "
|
||||
"738.5 877.325q-8.5-8.676-8.5-21.5 0-12.825 8.675-21.325 8.676-8.5 21.5-8.5 "
|
||||
"12.825 0 21.325 8.675 8.5 8.676 8.5 21.5 0 12.825-8.675 21.325-8.676 8.5-21.5 "
|
||||
"8.5Z"
|
||||
)
|
||||
|
||||
result1 (->> (svg.path/parse data)
|
||||
(mapv (fn [entry]
|
||||
(update entry :params #(into (sorted-map) %)))))
|
||||
result2 (->> (svg.path.legacy2/parse data)
|
||||
(mapv (fn [entry]
|
||||
(update entry :params #(into (sorted-map) %)))))]
|
||||
|
||||
(t/is (= 76
|
||||
(count result1)
|
||||
(count result2)))
|
||||
|
||||
;; (pp/pprint result1 {:length 100})
|
||||
;; (pp/pprint result2 {:length 50})
|
||||
|
||||
(dotimes [i (count result1)]
|
||||
(let [item1 (nth result1 i)
|
||||
item2 (nth result2 i)
|
||||
]
|
||||
|
||||
(t/is (= (:command item1)
|
||||
(:command item2)))
|
||||
|
||||
(doseq [[k v] (:params item1)]
|
||||
(t/is (mth/close? v (get-in item2 [:params k]) 0.000000001))
|
||||
)))))
|
||||
|
||||
(t/deftest parse-test-6
|
||||
(let [data (str "M3.078 3.548v16.9a.5.5 0 0 0 1 0v-16.9a.5.5 0 0 0-1 0ZM18.422 11.5"
|
||||
"H7.582a2.5 2.5 0 0 1-2.5-2.5V6.565a2.5 2.5 0 0 1 2.5-2.5"
|
||||
"h10.84a2.5 2.5 0 0 1 2.5 2.5V9a2.5 2.5 0 0 1-2.5 2.5Z"
|
||||
"M7.582 5.065a1.5 1.5 0 0 0-1.5 1.5V9a1.5 1.5 0 0 0 1.5 1.5"
|
||||
"h10.84a1.5 1.5 0 0 0 1.5-1.5V6.565a1.5 1.5 0 0 0-1.5-1.5Z"
|
||||
"M13.451 19.938H7.582a2.5 2.5 0 0 1-2.5-2.5V15"
|
||||
"a2.5 2.5 0 0 1 2.5-2.5h5.869a2.5 2.5 0 0 1 2.5 2.5v2.436"
|
||||
"a2.5 2.5 0 0 1-2.5 2.502ZM7.582 13.5a1.5 1.5 0 0 0-1.5 1.5v2.436"
|
||||
"a1.5 1.5 0 0 0 1.5 1.5h5.869a1.5 1.5 0 0 0 1.5-1.5V15"
|
||||
"a1.5 1.5 0 0 0-1.5-1.5Z")
|
||||
|
||||
result1 (->> (svg.path/parse data)
|
||||
(mapv (fn [entry]
|
||||
(update entry :params #(into (sorted-map) %)))))
|
||||
result2 (->> (svg.path.legacy2/parse data)
|
||||
(mapv (fn [entry]
|
||||
(update entry :params #(into (sorted-map) %)))))]
|
||||
|
||||
(t/is (= 47
|
||||
(count result1)
|
||||
(count result2)))
|
||||
|
||||
;; (pp/pprint result1 {:length 100})
|
||||
;; (pp/pprint result2 {:length 50})
|
||||
|
||||
(dotimes [i (count result1)]
|
||||
(let [item1 (nth result1 i)
|
||||
item2 (nth result2 i)
|
||||
]
|
||||
|
||||
(t/is (= (:command item1)
|
||||
(:command item2)))
|
||||
|
||||
(doseq [[k v] (:params item1)]
|
||||
(t/is (mth/close? v (get-in item2 [:params k]) 0.000000001))
|
||||
)))
|
||||
|
||||
#?(:cljs
|
||||
(let [result3 (svg.path.legacy1/parse data)]
|
||||
(t/is (= 47
|
||||
(count result1)
|
||||
(count result3)))
|
||||
|
||||
(dotimes [i (count result1)]
|
||||
(let [item1 (nth result1 i)
|
||||
item3 (nth result2 i)]
|
||||
|
||||
(t/is (= (:command item1)
|
||||
(:command item3)))
|
||||
|
||||
(t/is (= (:params item1)
|
||||
(:params item3)))))))))
|
||||
|
||||
|
||||
(t/deftest arc-to-bezier-1
|
||||
(let [expected1 [-1.6697754290362354e-13
|
||||
-5.258016244624741e-13
|
||||
@@ -179,7 +389,7 @@
|
||||
(nth expected2 (+ i 2))
|
||||
0.0000000001))))
|
||||
|
||||
(let [[result1 result2 :as total] (svg.path.legacy/arc->beziers* 0 0 30 50 0 0 1 162.55 162.45)]
|
||||
(let [[result1 result2 :as total] (svg.path.legacy2/arc->beziers* 0 0 30 50 0 0 1 162.55 162.45)]
|
||||
(t/is (= (count total) 2))
|
||||
|
||||
(dotimes [i (count result1)]
|
||||
@@ -190,7 +400,96 @@
|
||||
(dotimes [i (count result2)]
|
||||
(t/is (mth/close? (nth result2 i)
|
||||
(nth expected2 i)
|
||||
0.000000000001))))))
|
||||
0.000000000001))))
|
||||
|
||||
#?(:cljs
|
||||
(let [[result1 result2 :as total] (svg.path.legacy1/arc->beziers* 0 0 30 50 0 0 1 162.55 162.45)]
|
||||
(t/is (= (count total) 2))
|
||||
|
||||
(dotimes [i (count result1)]
|
||||
(t/is (mth/close? (nth result1 i)
|
||||
(nth expected1 i)
|
||||
0.000000000001)))
|
||||
|
||||
(dotimes [i (count result2)]
|
||||
(t/is (mth/close? (nth result2 i)
|
||||
(nth expected2 i)
|
||||
0.000000000001)))))
|
||||
|
||||
))
|
||||
|
||||
(t/deftest arc-to-bezier-2
|
||||
(let [expected1 [3.0779999999999994,
|
||||
20.448,
|
||||
3.0780000082296834,
|
||||
20.724142369096132,
|
||||
3.3018576309038683,
|
||||
20.94799998509884,
|
||||
3.5779999999999994,
|
||||
20.94799998509884]
|
||||
|
||||
expected2 [3.5779999999999994,
|
||||
20.94799998509884,
|
||||
3.854142369096131,
|
||||
20.94799998509884,
|
||||
4.077999991770315,
|
||||
20.724142369096132,
|
||||
4.077999999999999,
|
||||
20.448]]
|
||||
|
||||
(let [[result1 result2 :as total] (->> (svg.path/arc->beziers 3.078 20.448 4.077999999999999 20.448 0 0 0.5 0.5 0)
|
||||
(mapv (fn [segment]
|
||||
(vec (.-params segment)))))]
|
||||
(t/is (= (count total) 2))
|
||||
;; (println "================" 11111111)
|
||||
;; (pp/pprint expected1 {:width 50})
|
||||
;; (println "------------")
|
||||
;; (pp/pprint result1 {:width 50})
|
||||
|
||||
(dotimes [i (count result1)]
|
||||
(t/is (mth/close? (nth result1 i)
|
||||
(nth expected1 (+ i 2))
|
||||
0.0000000001)))
|
||||
|
||||
(dotimes [i (count result2)]
|
||||
(t/is (mth/close? (nth result2 i)
|
||||
(nth expected2 (+ i 2))
|
||||
0.0000000001))))
|
||||
|
||||
(let [[result1 result2 :as total] (svg.path.legacy2/arc->beziers* 3.078 20.448 4.077999999999999 20.448 0 0 0.5 0.5 0)]
|
||||
(t/is (= (count total) 2))
|
||||
|
||||
;; (println "================" 11111111)
|
||||
;; (pp/pprint expected1 {:width 50})
|
||||
;; (println "------------")
|
||||
;; (pp/pprint (vec result1) {:width 50})
|
||||
|
||||
(dotimes [i (count result1)]
|
||||
(t/is (mth/close? (nth result1 i)
|
||||
(nth expected1 i)
|
||||
0.000000000001)))
|
||||
|
||||
(dotimes [i (count result2)]
|
||||
(t/is (mth/close? (nth result2 i)
|
||||
(nth expected2 i)
|
||||
0.000000000001))))
|
||||
|
||||
#?(:cljs
|
||||
(let [[result1 result2 :as total] (svg.path.legacy1/arc->beziers* 3.078 20.448 4.077999999999999 20.448 0 0 0.5 0.5 0)]
|
||||
(t/is (= (count total) 2))
|
||||
|
||||
(dotimes [i (count result1)]
|
||||
(t/is (mth/close? (nth result1 i)
|
||||
(nth expected1 i)
|
||||
0.000000000001)))
|
||||
|
||||
(dotimes [i (count result2)]
|
||||
(t/is (mth/close? (nth result2 i)
|
||||
(nth expected2 i)
|
||||
0.000000000001)))))
|
||||
|
||||
))
|
||||
|
||||
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
@@ -220,14 +519,14 @@
|
||||
"59.9137 -301.293 -1.0595 -51.375 25.7186 -261.0492 -7.706 ")
|
||||
pattern [[:x :number] [:y :number]]]
|
||||
|
||||
(t/is (= expected (svg.path.legacy/extract-params cmdstr pattern)))))
|
||||
(t/is (= expected (svg.path.legacy2/extract-params cmdstr pattern)))))
|
||||
|
||||
(t/deftest extract-params-legacy-2
|
||||
(let [expected [{:x -994.563, :y 4564.1423 :r 0}]
|
||||
cmdstr (str "m -994.563 4564.1423 0")
|
||||
pattern [[:x :number] [:y :number] [:r :flag]]]
|
||||
|
||||
(t/is (= expected (svg.path.legacy/extract-params cmdstr pattern)))))
|
||||
(t/is (= expected (svg.path.legacy2/extract-params cmdstr pattern)))))
|
||||
|
||||
(t/deftest extract-params-legacy-3
|
||||
(let [cmdstr (str "a1.42 1.42 0 00-1.415-1.416 1.42 1.42 0 00-1.416 1.416 "
|
||||
@@ -245,7 +544,7 @@
|
||||
[:sweep-flag :flag]
|
||||
[:x :number]
|
||||
[:y :number]]
|
||||
result (svg.path.legacy/extract-params cmdstr pattern)]
|
||||
result (svg.path.legacy2/extract-params cmdstr pattern)]
|
||||
|
||||
(t/is (= (nth result 0)
|
||||
(nth expected 0)))
|
||||
|
||||
@@ -6,10 +6,17 @@
|
||||
org.clojure/clojure {:mvn/version "1.11.1"}
|
||||
binaryage/devtools {:mvn/version "RELEASE"}
|
||||
metosin/reitit-core {:mvn/version "0.5.18"}
|
||||
|
||||
funcool/beicon {:mvn/version "2021.07.05-1"}
|
||||
funcool/okulary {:mvn/version "2022.04.11-16"}
|
||||
funcool/potok {:mvn/version "2022.12.16-71"}
|
||||
|
||||
funcool/potok2
|
||||
{:git/tag "v2.0"
|
||||
:git/sha "2bb377b"
|
||||
:git/url "https://github.com/funcool/potok.git"}
|
||||
|
||||
funcool/beicon2
|
||||
{:git/tag "v2.0"
|
||||
:git/sha "e7135e0"
|
||||
:git/url "https://github.com/funcool/beicon.git"}
|
||||
|
||||
funcool/rumext
|
||||
{:git/tag "v2.7"
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
"@storybook/react": "^7.5.3",
|
||||
"@storybook/react-vite": "^7.5.3",
|
||||
"@storybook/testing-library": "^0.2.2",
|
||||
"@types/node": "^20.10.5",
|
||||
"animate.css": "^4.1.1",
|
||||
"autoprefixer": "^10.4.15",
|
||||
"concurrently": "^8.2.2",
|
||||
@@ -73,13 +74,13 @@
|
||||
"sass": "^1.66.1",
|
||||
"shadow-cljs": "2.26.2",
|
||||
"storybook": "^7.5.3",
|
||||
"typescript": "^5.3.3",
|
||||
"vite": "^5.0.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"date-fns": "^2.30.0",
|
||||
"draft-js": "^0.11.7",
|
||||
"eventsource-parser": "^1.1.1",
|
||||
"gl-matrix": "^3.4.3",
|
||||
"highlight.js": "^11.8.0",
|
||||
"js-beautify": "^1.14.9",
|
||||
"jszip": "^3.10.1",
|
||||
@@ -91,7 +92,7 @@
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-virtualized": "^9.22.3",
|
||||
"rxjs": "~7.8.1",
|
||||
"rxjs": "8.0.0-alpha.13",
|
||||
"sax": "^1.2.4",
|
||||
"source-map-support": "^0.5.21",
|
||||
"tdigest": "^0.1.2",
|
||||
|
||||
BIN
frontend/resources/fonts/RobotoMono-Regular.ttf
Normal file
BIN
frontend/resources/fonts/RobotoMono-Regular.ttf
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
3
frontend/resources/images/icons/expand-refactor.svg
Normal file
3
frontend/resources/images/icons/expand-refactor.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M10.357 2.813h2.828v2.829m-10.37 4.713v2.829h2.828m7.071-9.9l-9.428 9.429z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 206 B |
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 273 KiB After Width: | Height: | Size: 279 KiB |
3
frontend/resources/images/icons/reload-refactor.svg
Normal file
3
frontend/resources/images/icons/reload-refactor.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M2.4 8a6 6 0 111.758 4.242M2.4 8l2.1-2zm0 0L1 5.5z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 182 B |
@@ -148,6 +148,7 @@
|
||||
stroke: var(--button-tertiary-foreground-color-active);
|
||||
}
|
||||
}
|
||||
|
||||
&:global(.disabled),
|
||||
&[disabled],
|
||||
&:disabled {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
//
|
||||
// Copyright (c) KALEIDOS INC
|
||||
|
||||
@use "sass:color" as color;
|
||||
@use "sass:color";
|
||||
|
||||
:root {
|
||||
// DARK
|
||||
@@ -25,16 +25,19 @@
|
||||
--dark-ok-color: var(--strong-green);
|
||||
--dark-warning-color: #ff6432;
|
||||
--dark-pending-color: var(--lilac);
|
||||
--dark-error-color: #ec1f1f;
|
||||
--dark-error-color: #ff3277;
|
||||
--default-presence-color: #dee563;
|
||||
|
||||
// LIGHT
|
||||
--light-gray-1: #fff;
|
||||
--light-gray-2: #e8eaee;
|
||||
--light-gray-2-30: rgba(232, 234, 238, 0.3);
|
||||
--light-gray-2-80: rgba(232, 234, 238, 0.8);
|
||||
--light-gray-3: #f3f4f6;
|
||||
--light-gray-4: #eef0f2;
|
||||
--black: #000;
|
||||
--off-black: #495e74;
|
||||
--off-black-30: #{color.change(#495e74, $alpha: 0.3)};
|
||||
--purple: #6911d4;
|
||||
--purple-30: rgba(105, 17, 212, 0.2);
|
||||
--blue: #1345aa;
|
||||
|
||||
@@ -157,6 +157,8 @@
|
||||
.btn-primary {
|
||||
@extend .button-primary;
|
||||
text-transform: uppercase;
|
||||
font-size: $fs-14;
|
||||
font-weight: $fw400;
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
--scrollbar-background-color: var(--color-foreground-secondary);
|
||||
--panel-background-color: var(--color-background-primary);
|
||||
--panel-title-background-color: var(--color-background-secondary);
|
||||
--presence-color: var(--default-presence-color);
|
||||
|
||||
// BUTTONS
|
||||
--button-foreground-hover: var(--color-accent-primary);
|
||||
@@ -179,7 +180,7 @@
|
||||
--menu-shortcut-foreground-color: var(--color-foreground-secondary);
|
||||
--menu-shortcut-foreground-color-selected: var(--color-foreground-primary);
|
||||
--menu-shortcut-foreground-color-hover: var(--color-foreground-primary);
|
||||
--menu-shadow-color: var(--color-background-subtle);
|
||||
--menu-shadow-color: var(--shadow-color);
|
||||
--menu-background-color-disabled: var(--color-background-primary);
|
||||
--menu-foreground-color-disabled: var(--color-foreground-secondary);
|
||||
--menu-border-color-disabled: var(--color-background-quaternary);
|
||||
@@ -297,4 +298,16 @@
|
||||
--tag-background-color: var(--color-accent-primary);
|
||||
|
||||
--link-foreground-color: var(--color-accent-primary);
|
||||
|
||||
--resize-area-background-color: var(--color-background-primary);
|
||||
--resize-area-border-color: var(--color-background-quaternary);
|
||||
|
||||
// VIEWER
|
||||
--viewer-background-color: var(--color-background-secondary);
|
||||
--viewer-paginator-background-color: var(--color-background-tertiary);
|
||||
--viewer-controls-background-color: var(--color-background-primary);
|
||||
--viewer-inspect-border-color: var(--color-background-tertiary);
|
||||
--viewer-thumbnails-control-foreground-color: var(--color-foreground-secondary);
|
||||
--viewer-thumbnail-border-color: var(--color-accent-primary);
|
||||
--viewer-thumbnail-background-color-selected: var(--color-accent-primary-muted);
|
||||
}
|
||||
|
||||
@@ -63,6 +63,13 @@
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
@mixin codeTypography {
|
||||
font-family: "robotomono", monospace;
|
||||
font-size: $fs-12;
|
||||
font-weight: $fw400;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
@mixin textEllipsis {
|
||||
max-width: 99%;
|
||||
overflow: hidden;
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
--color-accent-primary-muted: var(--green-30);
|
||||
--color-accent-secondary: var(--lilac);
|
||||
--color-accent-tertiary: var(--strong-green);
|
||||
|
||||
--overlay-color: rgba(0, 0, 0, 0.4);
|
||||
--ok-color: var(--dark-ok-color);
|
||||
--warning-color: var(--dark-warning-color);
|
||||
@@ -28,5 +29,8 @@
|
||||
--error-color: var(--dark-error-color);
|
||||
--canvas-color: var(--color-canvas);
|
||||
|
||||
--shadow-color: var(--dark-gray-2-30);
|
||||
--radio-button-box-shadow: 0 0 0 1px var(--dark-gray-2-30) inset;
|
||||
|
||||
@include meta.load-css("hljs-dark-theme");
|
||||
}
|
||||
|
||||
@@ -1,98 +1,124 @@
|
||||
/**
|
||||
* Panda Syntax Theme for Highlight.js
|
||||
* Based on: https://github.com/tinkertrain/panda-syntax-vscode
|
||||
* Author: Annmarie Switzer <https://github.com/annmarie-switzer>
|
||||
*/
|
||||
/*!
|
||||
Theme: GitHub Dark Dimmed
|
||||
Description: Dark dimmed theme as seen on github.com
|
||||
Author: github.com
|
||||
Maintainer: @Hirse
|
||||
Updated: 2021-05-15
|
||||
|
||||
Colors taken from GitHub's CSS
|
||||
*/
|
||||
|
||||
.hljs {
|
||||
color: #e6e6e6;
|
||||
background: #2a2c2d;
|
||||
color: #adbac7;
|
||||
background: #22272e;
|
||||
}
|
||||
|
||||
.hljs-doctag,
|
||||
.hljs-keyword,
|
||||
.hljs-meta .hljs-keyword,
|
||||
.hljs-template-tag,
|
||||
.hljs-template-variable,
|
||||
.hljs-type,
|
||||
.hljs-variable.language_ {
|
||||
/* prettylights-syntax-keyword */
|
||||
color: #f47067;
|
||||
}
|
||||
|
||||
.hljs-title,
|
||||
.hljs-title.class_,
|
||||
.hljs-title.class_.inherited__,
|
||||
.hljs-title.function_ {
|
||||
/* prettylights-syntax-entity */
|
||||
color: #dcbdfb;
|
||||
}
|
||||
|
||||
.hljs-attr,
|
||||
.hljs-attribute,
|
||||
.hljs-literal,
|
||||
.hljs-meta,
|
||||
.hljs-number,
|
||||
.hljs-operator,
|
||||
.hljs-variable,
|
||||
.hljs-selector-attr,
|
||||
.hljs-selector-class,
|
||||
.hljs-selector-id {
|
||||
/* prettylights-syntax-constant */
|
||||
color: #6cb6ff;
|
||||
}
|
||||
|
||||
.hljs-regexp,
|
||||
.hljs-string,
|
||||
.hljs-meta .hljs-string {
|
||||
/* prettylights-syntax-string */
|
||||
color: #96d0ff;
|
||||
}
|
||||
|
||||
.hljs-built_in,
|
||||
.hljs-symbol {
|
||||
/* prettylights-syntax-variable */
|
||||
color: #f69d50;
|
||||
}
|
||||
|
||||
.hljs-comment,
|
||||
.hljs-code,
|
||||
.hljs-formula {
|
||||
/* prettylights-syntax-comment */
|
||||
color: #768390;
|
||||
}
|
||||
|
||||
.hljs-name,
|
||||
.hljs-quote,
|
||||
.hljs-selector-tag,
|
||||
.hljs-selector-pseudo {
|
||||
/* prettylights-syntax-entity-tag */
|
||||
color: #8ddb8c;
|
||||
}
|
||||
|
||||
.hljs-subst {
|
||||
/* prettylights-syntax-storage-modifier-import */
|
||||
color: #adbac7;
|
||||
}
|
||||
|
||||
.hljs-section {
|
||||
/* prettylights-syntax-markup-heading */
|
||||
color: #316dca;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.hljs-bullet {
|
||||
/* prettylights-syntax-markup-list */
|
||||
color: #eac55f;
|
||||
}
|
||||
|
||||
.hljs-emphasis {
|
||||
/* prettylights-syntax-markup-italic */
|
||||
color: #adbac7;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.hljs-strong {
|
||||
/* prettylights-syntax-markup-bold */
|
||||
color: #adbac7;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.hljs-link {
|
||||
text-decoration: underline;
|
||||
.hljs-addition {
|
||||
/* prettylights-syntax-markup-inserted */
|
||||
color: #b4f1b4;
|
||||
background-color: #1b4721;
|
||||
}
|
||||
|
||||
.hljs-comment,
|
||||
.hljs-quote {
|
||||
color: #bbbbbb;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.hljs-params {
|
||||
color: #bbbbbb;
|
||||
}
|
||||
|
||||
.hljs-punctuation,
|
||||
.hljs-attr {
|
||||
color: #e6e6e6;
|
||||
}
|
||||
|
||||
.hljs-selector-tag,
|
||||
.hljs-name,
|
||||
.hljs-meta {
|
||||
color: #ff4b82;
|
||||
}
|
||||
|
||||
.hljs-operator,
|
||||
.hljs-char.escape_ {
|
||||
color: #b084eb;
|
||||
}
|
||||
|
||||
.hljs-keyword,
|
||||
.hljs-deletion {
|
||||
color: #ff75b5;
|
||||
/* prettylights-syntax-markup-deleted */
|
||||
color: #ffd8d3;
|
||||
background-color: #78191b;
|
||||
}
|
||||
|
||||
.hljs-regexp,
|
||||
.hljs-selector-pseudo,
|
||||
.hljs-selector-attr,
|
||||
.hljs-variable.language_ {
|
||||
color: #ff9ac1;
|
||||
}
|
||||
|
||||
.hljs-subst,
|
||||
.hljs-property,
|
||||
.hljs-code,
|
||||
.hljs-formula,
|
||||
.hljs-section,
|
||||
.hljs-title.function_ {
|
||||
color: #45a9f9;
|
||||
}
|
||||
|
||||
.hljs-string,
|
||||
.hljs-symbol,
|
||||
.hljs-bullet,
|
||||
.hljs-addition,
|
||||
.hljs-selector-class,
|
||||
.hljs-title.class_,
|
||||
.hljs-title.class_.inherited__,
|
||||
.hljs-meta .hljs-string {
|
||||
color: #19f9d8;
|
||||
}
|
||||
|
||||
.hljs-variable,
|
||||
.hljs-template-variable,
|
||||
.hljs-number,
|
||||
.hljs-literal,
|
||||
.hljs-type,
|
||||
.hljs-char.escape_,
|
||||
.hljs-link,
|
||||
.hljs-built_in,
|
||||
.hljs-title,
|
||||
.hljs-selector-id,
|
||||
.hljs-tag,
|
||||
.hljs-doctag,
|
||||
.hljs-attribute,
|
||||
.hljs-template-tag,
|
||||
.hljs-meta .hljs-keyword,
|
||||
.hljs-punctuation {
|
||||
color: #ffb86c;
|
||||
.hljs-params,
|
||||
.hljs-property,
|
||||
.hljs-punctuation,
|
||||
.hljs-tag {
|
||||
/* purposely ignored */
|
||||
}
|
||||
|
||||
@@ -1,94 +1,126 @@
|
||||
/**
|
||||
* Panda Syntax Theme for Highlight.js
|
||||
* Based on: https://github.com/tinkertrain/panda-syntax-vscode
|
||||
* Author: Annmarie Switzer <https://github.com/annmarie-switzer>
|
||||
*/
|
||||
/*!
|
||||
Theme: GitHub
|
||||
Description: Light theme as seen on github.com
|
||||
Author: github.com
|
||||
Maintainer: @Hirse
|
||||
Updated: 2021-05-15
|
||||
|
||||
Outdated base version: https://github.com/primer/github-syntax-light
|
||||
Current colors taken from GitHub's CSS
|
||||
*/
|
||||
|
||||
.hljs {
|
||||
color: #2a2c2d;
|
||||
background: #e6e6e6;
|
||||
color: #24292e;
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
.hljs-doctag,
|
||||
.hljs-keyword,
|
||||
.hljs-meta .hljs-keyword,
|
||||
.hljs-template-tag,
|
||||
.hljs-template-variable,
|
||||
.hljs-type,
|
||||
.hljs-variable.language_ {
|
||||
/* prettylights-syntax-keyword */
|
||||
color: #d73a49;
|
||||
}
|
||||
|
||||
.hljs-title,
|
||||
.hljs-title.class_,
|
||||
.hljs-title.class_.inherited__,
|
||||
.hljs-title.function_ {
|
||||
/* prettylights-syntax-entity */
|
||||
color: #6f42c1;
|
||||
}
|
||||
|
||||
.hljs-attr,
|
||||
.hljs-attribute,
|
||||
.hljs-literal,
|
||||
.hljs-meta,
|
||||
.hljs-number,
|
||||
.hljs-operator,
|
||||
.hljs-variable,
|
||||
.hljs-selector-attr,
|
||||
.hljs-selector-class,
|
||||
.hljs-selector-id {
|
||||
/* prettylights-syntax-constant */
|
||||
color: #005cc5;
|
||||
}
|
||||
|
||||
.hljs-regexp,
|
||||
.hljs-string,
|
||||
.hljs-meta .hljs-string {
|
||||
/* prettylights-syntax-string */
|
||||
color: #032f62;
|
||||
}
|
||||
|
||||
.hljs-built_in,
|
||||
.hljs-symbol {
|
||||
/* prettylights-syntax-variable */
|
||||
color: #e36209;
|
||||
}
|
||||
|
||||
.hljs-comment,
|
||||
.hljs-code,
|
||||
.hljs-formula {
|
||||
/* prettylights-syntax-comment */
|
||||
color: #6a737d;
|
||||
}
|
||||
|
||||
.hljs-name,
|
||||
.hljs-quote,
|
||||
.hljs-selector-tag,
|
||||
.hljs-selector-pseudo {
|
||||
/* prettylights-syntax-entity-tag */
|
||||
color: #22863a;
|
||||
}
|
||||
|
||||
.hljs-subst {
|
||||
/* prettylights-syntax-storage-modifier-import */
|
||||
color: #24292e;
|
||||
}
|
||||
|
||||
.hljs-section {
|
||||
/* prettylights-syntax-markup-heading */
|
||||
color: #005cc5;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.hljs-bullet {
|
||||
/* prettylights-syntax-markup-list */
|
||||
color: #735c0f;
|
||||
}
|
||||
|
||||
.hljs-emphasis {
|
||||
/* prettylights-syntax-markup-italic */
|
||||
color: #24292e;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.hljs-strong {
|
||||
/* prettylights-syntax-markup-bold */
|
||||
color: #24292e;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.hljs-link {
|
||||
text-decoration: underline;
|
||||
.hljs-addition {
|
||||
/* prettylights-syntax-markup-inserted */
|
||||
color: #22863a;
|
||||
background-color: #f0fff4;
|
||||
}
|
||||
|
||||
.hljs-comment,
|
||||
.hljs-quote {
|
||||
color: #676b79;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.hljs-params {
|
||||
color: #676b79;
|
||||
}
|
||||
|
||||
.hljs-punctuation,
|
||||
.hljs-attr {
|
||||
color: #2a2c2d;
|
||||
}
|
||||
|
||||
.hljs-selector-tag,
|
||||
.hljs-name,
|
||||
.hljs-meta,
|
||||
.hljs-operator,
|
||||
.hljs-char.escape_ {
|
||||
color: #c56200;
|
||||
}
|
||||
|
||||
.hljs-keyword,
|
||||
.hljs-deletion {
|
||||
color: #d92792;
|
||||
/* prettylights-syntax-markup-deleted */
|
||||
color: #b31d28;
|
||||
background-color: #ffeef0;
|
||||
}
|
||||
|
||||
.hljs-regexp,
|
||||
.hljs-selector-pseudo,
|
||||
.hljs-selector-attr,
|
||||
.hljs-variable.language_ {
|
||||
color: #cc5e91;
|
||||
}
|
||||
|
||||
.hljs-subst,
|
||||
.hljs-property,
|
||||
.hljs-code,
|
||||
.hljs-formula,
|
||||
.hljs-section,
|
||||
.hljs-title.function_ {
|
||||
color: #3787c7;
|
||||
}
|
||||
|
||||
.hljs-string,
|
||||
.hljs-symbol,
|
||||
.hljs-bullet,
|
||||
.hljs-addition,
|
||||
.hljs-selector-class,
|
||||
.hljs-title.class_,
|
||||
.hljs-title.class_.inherited__,
|
||||
.hljs-meta .hljs-string {
|
||||
color: #0d7d6c;
|
||||
}
|
||||
|
||||
.hljs-variable,
|
||||
.hljs-template-variable,
|
||||
.hljs-number,
|
||||
.hljs-literal,
|
||||
.hljs-type,
|
||||
.hljs-char.escape_,
|
||||
.hljs-link,
|
||||
.hljs-built_in,
|
||||
.hljs-title,
|
||||
.hljs-selector-id,
|
||||
.hljs-tag,
|
||||
.hljs-doctag,
|
||||
.hljs-attribute,
|
||||
.hljs-template-tag,
|
||||
.hljs-meta .hljs-keyword {
|
||||
color: #7641bb;
|
||||
.hljs-params,
|
||||
.hljs-property,
|
||||
.hljs-punctuation,
|
||||
.hljs-tag {
|
||||
/* purposely ignored */
|
||||
color: #6a737d;
|
||||
}
|
||||
|
||||
@@ -21,11 +21,16 @@
|
||||
--color-accent-primary-muted: var(--purple-30);
|
||||
--color-accent-secondary: var(--blue);
|
||||
--color-accent-tertiary: var(--strong-purple);
|
||||
|
||||
--overlay-color: rgba(255, 255, 255, 0.4);
|
||||
--ok-color: var(--light-ok-color);
|
||||
--warning-color: var(--light-warning-color);
|
||||
--pending-color: var(--light-pending-color);
|
||||
--error-color: var(--light-error-color);
|
||||
--canvas-color: var(--color-canvas);
|
||||
|
||||
--shadow-color: var(--off-black-30);
|
||||
--radio-button-box-shadow: 0 0 0 1px var(--light-gray-2) inset;
|
||||
|
||||
@include meta.load-css("hljs-light-theme");
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
[app.util.webapi :as wapi]
|
||||
[app.util.zip :as uz]
|
||||
[app.worker.export :as e]
|
||||
[beicon.core :as rx]
|
||||
[beicon.v2.core :as rx]
|
||||
[cuerdas.core :as str]))
|
||||
|
||||
(defn parse-data [data]
|
||||
@@ -55,7 +55,7 @@
|
||||
|
||||
(->> (rx/from (vals media))
|
||||
(rx/map #(assoc % :file-id file-id))
|
||||
(rx/flat-map
|
||||
(rx/merge-map
|
||||
(fn [media]
|
||||
(let [file-path (str/concat file-id "/media/" (:id media) (cm/mtype->extension (:mtype media)))
|
||||
blob (data-uri->blob (:uri media))]
|
||||
@@ -79,38 +79,38 @@
|
||||
|
||||
render-stream
|
||||
(->> files-stream
|
||||
(rx/flat-map vals)
|
||||
(rx/flat-map e/process-pages)
|
||||
(rx/merge-map vals)
|
||||
(rx/merge-map e/process-pages)
|
||||
(rx/observe-on :async)
|
||||
(rx/flat-map e/get-page-data)
|
||||
(rx/merge-map e/get-page-data)
|
||||
(rx/share))
|
||||
|
||||
colors-stream
|
||||
(->> files-stream
|
||||
(rx/flat-map vals)
|
||||
(rx/merge-map vals)
|
||||
(rx/map #(vector (:id %) (get-in % [:data :colors])))
|
||||
(rx/filter #(d/not-empty? (second %)))
|
||||
(rx/map e/parse-library-color))
|
||||
|
||||
typographies-stream
|
||||
(->> files-stream
|
||||
(rx/flat-map vals)
|
||||
(rx/merge-map vals)
|
||||
(rx/map #(vector (:id %) (get-in % [:data :typographies])))
|
||||
(rx/filter #(d/not-empty? (second %)))
|
||||
(rx/map e/parse-library-typographies))
|
||||
|
||||
media-stream
|
||||
(->> files-stream
|
||||
(rx/flat-map vals)
|
||||
(rx/merge-map vals)
|
||||
(rx/map #(vector (:id %) (get-in % [:data :media])))
|
||||
(rx/filter #(d/not-empty? (second %)))
|
||||
(rx/flat-map parse-library-media))
|
||||
(rx/merge-map parse-library-media))
|
||||
|
||||
components-stream
|
||||
(->> files-stream
|
||||
(rx/flat-map vals)
|
||||
(rx/merge-map vals)
|
||||
(rx/filter #(d/not-empty? (ctkl/components-seq (:data %))))
|
||||
(rx/flat-map e/parse-library-components))
|
||||
(rx/merge-map e/parse-library-components))
|
||||
|
||||
pages-stream
|
||||
(->> render-stream
|
||||
@@ -132,7 +132,7 @@
|
||||
typographies-stream)
|
||||
(rx/reduce conj [])
|
||||
(rx/with-latest-from files-stream)
|
||||
(rx/flat-map (fn [[data _]]
|
||||
(rx/merge-map (fn [[data _]]
|
||||
(->> (uz/compress-files data)
|
||||
(rx/map #(vector file %)))))))))
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
(:require
|
||||
[app.common.uuid :as uuid]
|
||||
[app.main.render :as r]
|
||||
[beicon.core :as rx]
|
||||
[beicon.v2.core :as rx]
|
||||
[promesa.core :as p]))
|
||||
|
||||
(defn render-page-export
|
||||
@@ -22,7 +22,7 @@
|
||||
(fn [resolve reject]
|
||||
(->> (r/render-page data)
|
||||
(rx/take 1)
|
||||
(rx/subs resolve reject))) )))
|
||||
(rx/subs! resolve reject))) )))
|
||||
|
||||
(defn exports []
|
||||
#js {:renderPage render-page-export})
|
||||
|
||||
@@ -28,10 +28,10 @@
|
||||
[app.util.dom :as dom]
|
||||
[app.util.i18n :as i18n]
|
||||
[app.util.theme :as theme]
|
||||
[beicon.core :as rx]
|
||||
[beicon.v2.core :as rx]
|
||||
[debug]
|
||||
[features]
|
||||
[potok.core :as ptk]
|
||||
[potok.v2.core :as ptk]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
(log/setup! {:app :info})
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
(:require
|
||||
[app.common.exceptions :as ex]
|
||||
[app.common.transit :as t]
|
||||
[beicon.core :as rx]
|
||||
[potok.core :as ptk]))
|
||||
[beicon.v2.core :as rx]
|
||||
[potok.v2.core :as ptk]))
|
||||
|
||||
(defrecord BroadcastMessage [id type data]
|
||||
cljs.core/IDeref
|
||||
|
||||
@@ -14,8 +14,8 @@
|
||||
[app.common.uuid :as uuid]
|
||||
[app.main.data.workspace.state-helpers :as wsh]
|
||||
[app.main.repo :as rp]
|
||||
[beicon.core :as rx]
|
||||
[potok.core :as ptk]))
|
||||
[beicon.v2.core :as rx]
|
||||
[potok.v2.core :as ptk]))
|
||||
|
||||
(def ^:private schema:comment-thread
|
||||
(sm/define
|
||||
|
||||
@@ -14,8 +14,8 @@
|
||||
[app.main.features :as features]
|
||||
[app.main.repo :as rp]
|
||||
[app.util.i18n :refer [tr]]
|
||||
[beicon.core :as rx]
|
||||
[potok.core :as ptk]))
|
||||
[beicon.v2.core :as rx]
|
||||
[potok.v2.core :as ptk]))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; SHARE LINK
|
||||
@@ -106,9 +106,8 @@
|
||||
(:typography-count summary))]
|
||||
(modal/show
|
||||
{:type :confirm
|
||||
:message ""
|
||||
:title (tr "modals.add-shared-confirm.message" (:name summary))
|
||||
:hint (if (zero? count) (tr "modals.add-shared-confirm-empty.hint") (tr "modals.add-shared-confirm.hint"))
|
||||
:message (if (zero? count) (tr "modals.add-shared-confirm-empty.hint") (tr "modals.add-shared-confirm.hint"))
|
||||
:cancel-label (if (zero? count) (tr "labels.cancel") :omit)
|
||||
:accept-label (tr "modals.add-shared-confirm.accept")
|
||||
:accept-style :primary
|
||||
|
||||
@@ -30,9 +30,9 @@
|
||||
[app.util.time :as dt]
|
||||
[app.util.timers :as tm]
|
||||
[app.util.webapi :as wapi]
|
||||
[beicon.core :as rx]
|
||||
[beicon.v2.core :as rx]
|
||||
[clojure.set :as set]
|
||||
[potok.core :as ptk]))
|
||||
[potok.v2.core :as ptk]))
|
||||
|
||||
(log/set-level! :warn)
|
||||
|
||||
@@ -419,7 +419,7 @@
|
||||
(rx/map di/validate-file)
|
||||
(rx/map prepare)
|
||||
(rx/mapcat #(rp/cmd! :update-team-photo %))
|
||||
(rx/do on-success)
|
||||
(rx/tap on-success)
|
||||
(rx/map du/fetch-teams)
|
||||
(rx/catch on-error))))))
|
||||
|
||||
@@ -831,9 +831,15 @@
|
||||
(ptk/reify ::set-file-thumbnail
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(-> state
|
||||
(d/update-in-when [:dashboard-files file-id] assoc :thumbnail-uri thumbnail-uri)
|
||||
(d/update-in-when [:dashboard-recent-files file-id] assoc :thumbnail-uri thumbnail-uri)))))
|
||||
(letfn [(update-search-files [files]
|
||||
(->> files
|
||||
(mapv #(cond-> %
|
||||
(= file-id (:id %))
|
||||
(assoc :thumbnail-uri thumbnail-uri)))))]
|
||||
(-> state
|
||||
(d/update-in-when [:dashboard-files file-id] assoc :thumbnail-uri thumbnail-uri)
|
||||
(d/update-in-when [:dashboard-recent-files file-id] assoc :thumbnail-uri thumbnail-uri)
|
||||
(d/update-when :dashboard-search-result update-search-files))))))
|
||||
|
||||
;; --- EVENT: create-file
|
||||
|
||||
|
||||
@@ -17,9 +17,10 @@
|
||||
[app.util.object :as obj]
|
||||
[app.util.storage :refer [storage]]
|
||||
[app.util.time :as dt]
|
||||
[beicon.core :as rx]
|
||||
[beicon.v2.core :as rx]
|
||||
[beicon.v2.operators :as rxo]
|
||||
[lambdaisland.uri :as u]
|
||||
[potok.core :as ptk]))
|
||||
[potok.v2.core :as ptk]))
|
||||
|
||||
(l/set-level! :info)
|
||||
|
||||
@@ -238,7 +239,7 @@
|
||||
profile (->> (rx/from-atom storage {:emit-current-value? true})
|
||||
(rx/map :profile)
|
||||
(rx/map :id)
|
||||
(rx/dedupe))]
|
||||
(rx/pipe (rxo/distinct-contiguous)))]
|
||||
|
||||
(l/debug :hint "event instrumentation initialized")
|
||||
|
||||
@@ -259,12 +260,12 @@
|
||||
(l/debug :hint "events chunk persisted" :total (count chunk))))
|
||||
(rx/map (constantly chunk))))))
|
||||
(rx/take-until stoper)
|
||||
(rx/subs (fn [chunk]
|
||||
(swap! buffer remove-from-buffer (count chunk)))
|
||||
(fn [cause]
|
||||
(l/error :hint "unexpected error on audit persistence" :cause cause))
|
||||
(fn []
|
||||
(l/debug :hint "audit persistence terminated"))))
|
||||
(rx/subs! (fn [chunk]
|
||||
(swap! buffer remove-from-buffer (count chunk)))
|
||||
(fn [cause]
|
||||
(l/error :hint "unexpected error on audit persistence" :cause cause))
|
||||
(fn []
|
||||
(l/debug :hint "audit persistence terminated"))))
|
||||
|
||||
(->> stream
|
||||
(rx/with-latest-from profile)
|
||||
@@ -290,10 +291,10 @@
|
||||
|
||||
(rx/switch-map #(rx/timer (inst-ms session-timeout)))
|
||||
(rx/take-until stoper)
|
||||
(rx/subs (fn [_]
|
||||
(l/debug :hint "session reinitialized")
|
||||
(reset! session nil))
|
||||
(fn [cause]
|
||||
(l/error :hint "error on event batching stream" :cause cause))
|
||||
(fn []
|
||||
(l/debug :hitn "events batching stream terminated")))))))))
|
||||
(rx/subs! (fn [_]
|
||||
(l/debug :hint "session reinitialized")
|
||||
(reset! session nil))
|
||||
(fn [cause]
|
||||
(l/error :hint "error on event batching stream" :cause cause))
|
||||
(fn []
|
||||
(l/debug :hitn "events batching stream terminated")))))))))
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
[app.util.dom :as dom]
|
||||
[app.util.time :as dt]
|
||||
[app.util.websocket :as ws]
|
||||
[beicon.core :as rx]
|
||||
[potok.core :as ptk]))
|
||||
[beicon.v2.core :as rx]
|
||||
[potok.v2.core :as ptk]))
|
||||
|
||||
(def default-timeout 5000)
|
||||
|
||||
|
||||
@@ -19,9 +19,9 @@
|
||||
[app.util.i18n :refer [tr]]
|
||||
[app.util.storage :refer [storage]]
|
||||
[app.util.webapi :as wa]
|
||||
[beicon.core :as rx]
|
||||
[beicon.v2.core :as rx]
|
||||
[cuerdas.core :as str]
|
||||
[potok.core :as ptk]))
|
||||
[potok.v2.core :as ptk]))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; General purpose events & IMPL
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
[app.main.data.messages :as dm]
|
||||
[app.main.store :as st]
|
||||
[app.util.i18n :refer [tr]]
|
||||
[beicon.core :as rx]
|
||||
[beicon.v2.core :as rx]
|
||||
[cljs.spec.alpha :as s]
|
||||
[cuerdas.core :as str]))
|
||||
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.schema :as sm]
|
||||
[beicon.core :as rx]
|
||||
[potok.core :as ptk]))
|
||||
[beicon.v2.core :as rx]
|
||||
[potok.v2.core :as ptk]))
|
||||
|
||||
(declare hide)
|
||||
(declare show)
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
[app.common.uuid :as uuid]
|
||||
[app.main.store :as st]
|
||||
[cljs.core :as c]
|
||||
[potok.core :as ptk]))
|
||||
[potok.v2.core :as ptk]))
|
||||
|
||||
(defonce components (atom {}))
|
||||
|
||||
|
||||
@@ -16,10 +16,10 @@
|
||||
[app.main.refs :as refs]
|
||||
[app.util.code-gen :as cg]
|
||||
[app.util.timers :as ts]
|
||||
[beicon.core :as rx]
|
||||
[beicon.v2.core :as rx]
|
||||
[clojure.set :as set]
|
||||
[cuerdas.core :as str]
|
||||
[potok.core :as ptk]))
|
||||
[potok.v2.core :as ptk]))
|
||||
|
||||
(def style-type "css")
|
||||
(def markup-type "html")
|
||||
@@ -80,7 +80,7 @@
|
||||
(rx/merge-map fonts/fetch-font-css)
|
||||
(rx/reduce conj [])
|
||||
(rx/map #(str/join "\n" %))
|
||||
(rx/subs
|
||||
(rx/subs!
|
||||
(fn [fontfaces-css]
|
||||
(let [style-code
|
||||
(dm/str
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
[app.common.schema :as sm]
|
||||
[app.config :as cf]
|
||||
[cuerdas.core :as str]
|
||||
[potok.core :as ptk]))
|
||||
[potok.v2.core :as ptk]))
|
||||
|
||||
(log/set-level! :warn)
|
||||
|
||||
|
||||
@@ -21,8 +21,8 @@
|
||||
[app.util.i18n :as i18n]
|
||||
[app.util.router :as rt]
|
||||
[app.util.storage :refer [storage]]
|
||||
[beicon.core :as rx]
|
||||
[potok.core :as ptk]))
|
||||
[beicon.v2.core :as rx]
|
||||
[potok.v2.core :as ptk]))
|
||||
|
||||
;; --- SCHEMAS
|
||||
|
||||
@@ -434,7 +434,7 @@
|
||||
(rx/map di/validate-file)
|
||||
(rx/map prepare)
|
||||
(rx/mapcat #(rp/cmd! :update-profile-photo %))
|
||||
(rx/do on-success)
|
||||
(rx/tap on-success)
|
||||
(rx/map (constantly (fetch-profile)))
|
||||
(rx/catch on-error))))))
|
||||
|
||||
|
||||
@@ -21,8 +21,8 @@
|
||||
[app.main.repo :as rp]
|
||||
[app.util.globals :as ug]
|
||||
[app.util.router :as rt]
|
||||
[beicon.core :as rx]
|
||||
[potok.core :as ptk]))
|
||||
[beicon.v2.core :as rx]
|
||||
[potok.v2.core :as ptk]))
|
||||
|
||||
;; --- Local State Initialization
|
||||
|
||||
|
||||
@@ -11,8 +11,8 @@
|
||||
[app.common.uri :as u]
|
||||
[app.config :as cf]
|
||||
[app.util.websocket :as ws]
|
||||
[beicon.core :as rx]
|
||||
[potok.core :as ptk]))
|
||||
[beicon.v2.core :as rx]
|
||||
[potok.v2.core :as ptk]))
|
||||
|
||||
(l/set-level! :error)
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
(ptk/reify ::initialize
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(l/trace :hint "event:initialize" :fn "watch")
|
||||
(l/trace :hint "initialize" :fn "watch")
|
||||
(let [sid (:session-id state)
|
||||
uri (prepare-uri {:session-id sid})
|
||||
ws (ws/create uri)]
|
||||
|
||||
@@ -78,10 +78,10 @@
|
||||
[app.util.router :as rt]
|
||||
[app.util.timers :as tm]
|
||||
[app.util.webapi :as wapi]
|
||||
[beicon.core :as rx]
|
||||
[beicon.v2.core :as rx]
|
||||
[cljs.spec.alpha :as s]
|
||||
[cuerdas.core :as str]
|
||||
[potok.core :as ptk]))
|
||||
[potok.v2.core :as ptk]))
|
||||
|
||||
(def default-workspace-local {:zoom 1})
|
||||
|
||||
@@ -444,7 +444,7 @@
|
||||
uris (into #{} xform (wsh/lookup-page-objects state page-id))]
|
||||
|
||||
(->> (rx/from uris)
|
||||
(rx/subs #(http/fetch-data-uri % false)))))))
|
||||
(rx/subs! #(http/fetch-data-uri % false)))))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Workspace Page CRUD
|
||||
@@ -872,10 +872,10 @@
|
||||
(pcb/update-shapes [parent-id] #(ctl/add-children-to-index % ids objects to-index)))
|
||||
|
||||
(pcb/update-shapes parents
|
||||
(fn [parent]
|
||||
(fn [parent objects]
|
||||
(cond-> parent
|
||||
(ctl/grid-layout? parent)
|
||||
(ctl/assign-cells))))
|
||||
(ctl/assign-cells objects))))
|
||||
|
||||
(pcb/reorder-grid-children parents)
|
||||
|
||||
|
||||
@@ -18,9 +18,9 @@
|
||||
[app.main.data.workspace.changes :as dch]
|
||||
[app.main.data.workspace.selection :as dws]
|
||||
[app.main.data.workspace.state-helpers :as wsh]
|
||||
[beicon.core :as rx]
|
||||
[beicon.v2.core :as rx]
|
||||
[cuerdas.core :as str]
|
||||
[potok.core :as ptk]))
|
||||
[potok.v2.core :as ptk]))
|
||||
|
||||
(defn selected-shapes-idx
|
||||
[state]
|
||||
|
||||
@@ -20,8 +20,8 @@
|
||||
[app.main.data.workspace.undo :as dwu]
|
||||
[app.main.store :as st]
|
||||
[app.main.worker :as uw]
|
||||
[beicon.core :as rx]
|
||||
[potok.core :as ptk]))
|
||||
[beicon.v2.core :as rx]
|
||||
[potok.v2.core :as ptk]))
|
||||
|
||||
;; Change this to :info :debug or :trace to debug this module
|
||||
(log/set-level! :warn)
|
||||
@@ -70,7 +70,7 @@
|
||||
update-layout-ids
|
||||
(->> ids
|
||||
(map (d/getf objects))
|
||||
(filter #(some update-layout-attr? (pcb/changed-attrs % update-fn {:attrs attrs})))
|
||||
(filter #(some update-layout-attr? (pcb/changed-attrs % objects update-fn {:attrs attrs})))
|
||||
(map :id))
|
||||
|
||||
changes (reduce
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
(:require
|
||||
[app.common.files.helpers :as cfh]
|
||||
[app.common.uuid :as uuid]
|
||||
[potok.core :as ptk]))
|
||||
[potok.v2.core :as ptk]))
|
||||
|
||||
;; --- Shape attrs (Layers Sidebar)
|
||||
|
||||
|
||||
@@ -23,8 +23,8 @@
|
||||
[app.main.data.workspace.undo :as dwu]
|
||||
[app.util.color :as uc]
|
||||
[app.util.storage :refer [storage]]
|
||||
[beicon.core :as rx]
|
||||
[potok.core :as ptk]))
|
||||
[beicon.v2.core :as rx]
|
||||
[potok.v2.core :as ptk]))
|
||||
|
||||
;; A set of keys that are used for shared state identifiers
|
||||
(def ^:const colorpicker-selected-broadcast-key ::colorpicker-selected)
|
||||
@@ -353,7 +353,7 @@
|
||||
;; Stream that updates the stroke/width and stops if `esc` pressed
|
||||
(->> sub
|
||||
(rx/take-until stop?)
|
||||
(rx/flat-map update-events))
|
||||
(rx/merge-map update-events))
|
||||
|
||||
;; Hide the modal if the stop event is emitted
|
||||
(->> stop?
|
||||
|
||||
@@ -22,8 +22,8 @@
|
||||
[app.main.streams :as ms]
|
||||
[app.util.mouse :as mse]
|
||||
[app.util.router :as rt]
|
||||
[beicon.core :as rx]
|
||||
[potok.core :as ptk]))
|
||||
[beicon.v2.core :as rx]
|
||||
[potok.v2.core :as ptk]))
|
||||
|
||||
(declare handle-interrupt)
|
||||
(declare handle-comment-layer-click)
|
||||
|
||||
@@ -13,8 +13,8 @@
|
||||
[app.main.data.workspace.state-helpers :as wsh]
|
||||
[app.main.data.workspace.undo :as dwu]
|
||||
[app.util.router :as rt]
|
||||
[beicon.core :as rx]
|
||||
[potok.core :as ptk]))
|
||||
[beicon.v2.core :as rx]
|
||||
[potok.v2.core :as ptk]))
|
||||
|
||||
;; Change this to :info :debug or :trace to debug this module
|
||||
(log/set-level! :warn)
|
||||
|
||||
@@ -14,8 +14,8 @@
|
||||
[app.main.data.workspace.drawing.common :as common]
|
||||
[app.main.data.workspace.drawing.curve :as curve]
|
||||
[app.main.data.workspace.path :as path]
|
||||
[beicon.core :as rx]
|
||||
[potok.core :as ptk]))
|
||||
[beicon.v2.core :as rx]
|
||||
[potok.v2.core :as ptk]))
|
||||
|
||||
(declare start-drawing)
|
||||
(declare handle-drawing)
|
||||
|
||||
@@ -24,9 +24,10 @@
|
||||
[app.main.data.workspace.state-helpers :as wsh]
|
||||
[app.main.snap :as snap]
|
||||
[app.main.streams :as ms]
|
||||
[app.util.array :as array]
|
||||
[app.util.mouse :as mse]
|
||||
[beicon.core :as rx]
|
||||
[potok.core :as ptk]))
|
||||
[beicon.v2.core :as rx]
|
||||
[potok.v2.core :as ptk]))
|
||||
|
||||
(defn adjust-ratio
|
||||
[point initial]
|
||||
@@ -128,12 +129,11 @@
|
||||
|
||||
(->> ms/mouse-position
|
||||
(rx/filter #(> (gpt/distance % initial) (/ 2 zoom)))
|
||||
(rx/with-latest vector ms/mouse-position-shift)
|
||||
(rx/with-latest conj ms/mouse-position-mod)
|
||||
(rx/with-latest-from ms/mouse-position-shift ms/mouse-position-mod)
|
||||
(rx/switch-map
|
||||
(fn [[point :as current]]
|
||||
(->> (snap/closest-snap-point page-id [shape] objects layout zoom focus point)
|
||||
(rx/map #(conj current %)))))
|
||||
(rx/map (partial array/conj current)))))
|
||||
(rx/map
|
||||
(fn [[_ shift? mod? point]]
|
||||
#(update-drawing % initial (cond-> point snap-pixel? (gpt/round-step snap-prec)) shift? mod?)))))
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
[app.main.data.workspace.state-helpers :as wsh]
|
||||
[app.main.data.workspace.undo :as dwu]
|
||||
[app.main.worker :as uw]
|
||||
[beicon.core :as rx]
|
||||
[potok.core :as ptk]))
|
||||
[beicon.v2.core :as rx]
|
||||
[potok.v2.core :as ptk]))
|
||||
|
||||
(defn clear-drawing
|
||||
[]
|
||||
|
||||
@@ -23,8 +23,8 @@
|
||||
[app.main.streams :as ms]
|
||||
[app.util.mouse :as mse]
|
||||
[app.util.path.simplify-curve :as ups]
|
||||
[beicon.core :as rx]
|
||||
[potok.core :as ptk]))
|
||||
[beicon.v2.core :as rx]
|
||||
[potok.v2.core :as ptk]))
|
||||
|
||||
(def simplify-tolerance 0.3)
|
||||
|
||||
|
||||
@@ -10,8 +10,8 @@
|
||||
[app.common.types.shape.layout :as ctl]
|
||||
[app.main.data.workspace.common :as dwc]
|
||||
[app.main.data.workspace.state-helpers :as wsh]
|
||||
[beicon.core :as rx]
|
||||
[potok.core :as ptk]))
|
||||
[beicon.v2.core :as rx]
|
||||
[potok.v2.core :as ptk]))
|
||||
|
||||
(defn interrupt? [e] (= e :interrupt))
|
||||
|
||||
|
||||
@@ -10,8 +10,8 @@
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.main.data.workspace.changes :as dch]
|
||||
[app.main.data.workspace.state-helpers :as wsh]
|
||||
[beicon.core :as rx]
|
||||
[potok.core :as ptk]))
|
||||
[beicon.v2.core :as rx]
|
||||
[potok.v2.core :as ptk]))
|
||||
|
||||
;; This event will update the file so the boolean data has a pre-generated path data
|
||||
;; to increase performance.
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
(ns app.main.data.workspace.fix-broken-shapes
|
||||
(:require
|
||||
[app.main.data.workspace.changes :as dch]
|
||||
[beicon.core :as rx]
|
||||
[potok.core :as ptk]))
|
||||
[beicon.v2.core :as rx]
|
||||
[potok.v2.core :as ptk]))
|
||||
|
||||
(defn- generate-broken-link-changes
|
||||
[attr {:keys [objects id] :as container}]
|
||||
|
||||
@@ -12,8 +12,8 @@
|
||||
[app.main.data.workspace.changes :as dch]
|
||||
[app.main.data.workspace.state-helpers :as wsh]
|
||||
[app.main.fonts :as fonts]
|
||||
[beicon.core :as rx]
|
||||
[potok.core :as ptk]))
|
||||
[beicon.v2.core :as rx]
|
||||
[potok.v2.core :as ptk]))
|
||||
|
||||
;; This event will update the file so the texts with non existing custom fonts try to be fixed.
|
||||
;; This can happen when:
|
||||
|
||||
@@ -12,8 +12,8 @@
|
||||
[app.common.files.changes-builder :as pcb]
|
||||
[app.main.data.workspace.changes :as dch]
|
||||
[app.main.data.workspace.state-helpers :as wsh]
|
||||
[beicon.core :as rx]
|
||||
[potok.core :as ptk]))
|
||||
[beicon.v2.core :as rx]
|
||||
[potok.v2.core :as ptk]))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Grid
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
[app.common.geom.rect :as grc]
|
||||
[app.common.types.shape.layout :as ctl]
|
||||
[app.main.data.workspace.state-helpers :as wsh]
|
||||
[potok.core :as ptk]))
|
||||
[potok.v2.core :as ptk]))
|
||||
|
||||
(defn hover-grid-cell
|
||||
[grid-id cell-id add-to-set]
|
||||
|
||||
@@ -10,8 +10,8 @@
|
||||
[app.main.data.workspace :as dw]
|
||||
[app.main.data.workspace.common :as dwc]
|
||||
[app.main.store :as st]
|
||||
[beicon.core :as rx]
|
||||
[potok.core :as ptk]))
|
||||
[beicon.v2.core :as rx]
|
||||
[potok.v2.core :as ptk]))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Shortcuts
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
[app.main.data.workspace.selection :as dws]
|
||||
[app.main.data.workspace.state-helpers :as wsh]
|
||||
[app.main.data.workspace.undo :as dwu]
|
||||
[beicon.core :as rx]
|
||||
[potok.core :as ptk]))
|
||||
[beicon.v2.core :as rx]
|
||||
[potok.v2.core :as ptk]))
|
||||
|
||||
(defn shapes-for-grouping
|
||||
[objects selected]
|
||||
|
||||
@@ -13,8 +13,8 @@
|
||||
[app.common.types.page :as ctp]
|
||||
[app.main.data.workspace.changes :as dwc]
|
||||
[app.main.data.workspace.state-helpers :as wsh]
|
||||
[beicon.core :as rx]
|
||||
[potok.core :as ptk]))
|
||||
[beicon.v2.core :as rx]
|
||||
[potok.v2.core :as ptk]))
|
||||
|
||||
(defn make-update-guide [guide]
|
||||
(fn [other]
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
(:require
|
||||
[app.common.data.macros :as dm]
|
||||
[clojure.set :as set]
|
||||
[potok.core :as ptk]))
|
||||
[potok.v2.core :as ptk]))
|
||||
|
||||
;; --- Manage shape's highlight status
|
||||
|
||||
|
||||
@@ -20,8 +20,8 @@
|
||||
[app.main.data.workspace.undo :as dwu]
|
||||
[app.main.streams :as ms]
|
||||
[app.util.mouse :as mse]
|
||||
[beicon.core :as rx]
|
||||
[potok.core :as ptk]))
|
||||
[beicon.v2.core :as rx]
|
||||
[potok.v2.core :as ptk]))
|
||||
|
||||
;; --- Flows
|
||||
|
||||
|
||||
@@ -11,9 +11,9 @@
|
||||
[app.common.math :as mth]
|
||||
[app.main.data.workspace.changes :as dch]
|
||||
[app.main.data.workspace.state-helpers :as wsh]
|
||||
[beicon.core :as rx]
|
||||
[beicon.v2.core :as rx]
|
||||
[cuerdas.core :as str]
|
||||
[potok.core :as ptk]))
|
||||
[potok.v2.core :as ptk]))
|
||||
|
||||
;; -- Opacity ----------------------------------------------------------
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
[app.common.data.macros :as dm]
|
||||
[app.util.storage :refer [storage]]
|
||||
[clojure.set :as set]
|
||||
[potok.core :as ptk]))
|
||||
[potok.v2.core :as ptk]))
|
||||
|
||||
(def valid-flags
|
||||
#{:sitemap
|
||||
|
||||
@@ -45,9 +45,9 @@
|
||||
[app.util.i18n :refer [tr]]
|
||||
[app.util.router :as rt]
|
||||
[app.util.time :as dt]
|
||||
[beicon.core :as rx]
|
||||
[beicon.v2.core :as rx]
|
||||
[cuerdas.core :as str]
|
||||
[potok.core :as ptk]))
|
||||
[potok.v2.core :as ptk]))
|
||||
|
||||
;; Change this to :info :debug or :trace to debug this module, or :warn to reset to default
|
||||
(log/set-level! :warn)
|
||||
@@ -754,6 +754,18 @@
|
||||
(dch/commit-changes (assoc nonlocal-changes
|
||||
:file-id file-id)))))))))))
|
||||
|
||||
(defn- update-component-thumbnail-sync
|
||||
[state component-id file-id tag]
|
||||
(let [current-file-id (:current-file-id state)
|
||||
current-file? (= current-file-id file-id)
|
||||
data (if current-file?
|
||||
(get state :workspace-data)
|
||||
(get-in state [:workspace-libraries file-id :data]))
|
||||
component (ctkl/get-component data component-id)
|
||||
page-id (:main-instance-page component)
|
||||
root-id (:main-instance-id component)]
|
||||
(dwt/request-thumbnail file-id page-id root-id tag)))
|
||||
|
||||
(defn update-component-sync
|
||||
([shape-id file-id] (update-component-sync shape-id file-id nil))
|
||||
([shape-id file-id undo-group]
|
||||
@@ -761,14 +773,18 @@
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [current-file-id (:current-file-id state)
|
||||
current-file? (= current-file-id file-id)
|
||||
page (wsh/lookup-page state)
|
||||
shape (ctn/get-shape page shape-id)
|
||||
component-id (:component-id shape)
|
||||
undo-id (js/Symbol)]
|
||||
(rx/of
|
||||
(dwu/start-undo-transaction undo-id)
|
||||
(update-component shape-id undo-group)
|
||||
(sync-file current-file-id file-id :components (:component-id shape) undo-group)
|
||||
(when (not= current-file-id file-id)
|
||||
(update-component-thumbnail-sync state component-id file-id "frame")
|
||||
(update-component-thumbnail-sync state component-id file-id "component")
|
||||
(when (not current-file?)
|
||||
(sync-file file-id file-id :components (:component-id shape) undo-group))
|
||||
(dwu/commit-undo-transaction undo-id)))))))
|
||||
|
||||
@@ -795,7 +811,8 @@
|
||||
(ptk/reify ::update-component-thumbnail
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [data (get state :workspace-data)
|
||||
(rx/of (update-component-thumbnail-sync state component-id file-id "component"))
|
||||
#_(let [data (get state :workspace-data)
|
||||
component (ctkl/get-component data component-id)
|
||||
page-id (:main-instance-page component)
|
||||
root-id (:main-instance-id component)]
|
||||
|
||||
@@ -182,10 +182,10 @@
|
||||
(-> changes
|
||||
(pcb/update-shapes
|
||||
[(:parent-id first-shape)]
|
||||
(fn [shape]
|
||||
(fn [shape objects]
|
||||
(-> shape
|
||||
(ctl/push-into-cell [(:id first-shape)] row column)
|
||||
(ctl/assign-cells))))
|
||||
(ctl/assign-cells objects))))
|
||||
(pcb/reorder-grid-children [(:parent-id first-shape)])))
|
||||
changes)
|
||||
|
||||
@@ -603,6 +603,23 @@
|
||||
(generate-detach-instance changes container shape-id))))
|
||||
changes)))
|
||||
|
||||
(defn- find-main-container
|
||||
"Find the container that has the main shape."
|
||||
[container-inst shape-inst shape-main library component]
|
||||
(loop [shape-inst' shape-inst
|
||||
component' component]
|
||||
(let [container (ctf/get-component-container library component')] ; TODO: this won't work if some intermediate component is in a different library
|
||||
(if (some? (ctn/get-shape container (:id shape-main))) ; for this to work we need to have access to the libraries list here
|
||||
container
|
||||
(let [parent (ctn/get-shape container-inst (:parent-id shape-inst'))
|
||||
shape-inst' (ctn/get-head-shape (:objects container-inst) parent)
|
||||
component' (or (ctkl/get-component library (:component-id shape-inst'))
|
||||
(ctkl/get-deleted-component library (:component-id shape-inst')))]
|
||||
(if (some? component)
|
||||
(recur shape-inst'
|
||||
component')
|
||||
nil))))))
|
||||
|
||||
(defn- generate-sync-shape-direct-recursive
|
||||
[changes container shape-inst component library shape-main root-inst root-main reset? initial-root? redirect-shaperef components-v2]
|
||||
(log/debug :msg "Sync shape direct recursive"
|
||||
@@ -639,7 +656,7 @@
|
||||
set-remote-synced?
|
||||
(change-remote-synced shape-inst container true))
|
||||
|
||||
component-container (ctf/get-component-container library component)
|
||||
component-container (find-main-container container shape-inst shape-main library component)
|
||||
|
||||
children-inst (vec (ctn/get-direct-children container shape-inst))
|
||||
children-main (vec (ctn/get-direct-children component-container shape-main))
|
||||
@@ -948,11 +965,11 @@
|
||||
(:id parent-shape)
|
||||
(:frame-id parent-shape)))
|
||||
|
||||
(nil? (:shape-ref original-shape))
|
||||
(assoc :shape-ref (:id original-shape))
|
||||
|
||||
set-remote-synced?
|
||||
(assoc :remote-synced true))))
|
||||
(assoc :remote-synced true)
|
||||
|
||||
:always
|
||||
(assoc :shape-ref (:id original-shape)))))
|
||||
|
||||
update-original-shape (fn [original-shape _new-shape]
|
||||
original-shape)
|
||||
|
||||
@@ -30,9 +30,9 @@
|
||||
[app.main.store :as st]
|
||||
[app.util.http :as http]
|
||||
[app.util.i18n :refer [tr]]
|
||||
[beicon.core :as rx]
|
||||
[beicon.v2.core :as rx]
|
||||
[cuerdas.core :as str]
|
||||
[potok.core :as ptk]
|
||||
[potok.v2.core :as ptk]
|
||||
[promesa.core :as p]
|
||||
[tubax.core :as tubax]))
|
||||
|
||||
@@ -122,13 +122,13 @@
|
||||
(->> (rx/from uris)
|
||||
(rx/filter (comp not svg-url?))
|
||||
(rx/mapcat upload)
|
||||
(rx/do on-image))
|
||||
(rx/tap on-image))
|
||||
|
||||
(->> (rx/from uris)
|
||||
(rx/filter svg-url?)
|
||||
(rx/merge-map (partial fetch-svg name))
|
||||
(rx/merge-map svg->clj)
|
||||
(rx/do on-svg)))))
|
||||
(rx/tap on-svg)))))
|
||||
|
||||
(defn- process-blobs
|
||||
[{:keys [file-id local? name blobs force-media on-image on-svg]}]
|
||||
@@ -154,14 +154,14 @@
|
||||
(rx/filter (comp not svg-blob?))
|
||||
(rx/map prepare-blob)
|
||||
(rx/mapcat #(rp/cmd! :upload-file-media-object %))
|
||||
(rx/do on-image))
|
||||
(rx/tap on-image))
|
||||
|
||||
(->> (rx/from blobs)
|
||||
(rx/map dmm/validate-file)
|
||||
(rx/filter svg-blob?)
|
||||
(rx/merge-map extract-content)
|
||||
(rx/merge-map svg->clj)
|
||||
(rx/do on-svg)))))
|
||||
(rx/tap on-svg)))))
|
||||
|
||||
(defn handle-media-error [error on-error]
|
||||
(if (ex/ex-info? error)
|
||||
@@ -278,7 +278,7 @@
|
||||
(rx/map dmm/validate-file)
|
||||
(rx/map prepare)
|
||||
(rx/mapcat #(rp/cmd! :upload-file-media-object %))
|
||||
(rx/do on-upload-success)
|
||||
(rx/tap on-upload-success)
|
||||
(rx/catch handle-media-error))))))
|
||||
|
||||
;; --- Upload File Media objects
|
||||
@@ -423,7 +423,7 @@
|
||||
:timeout nil
|
||||
:tag :media-loading}))
|
||||
(->> (rp/cmd! :clone-file-media-object params)
|
||||
(rx/do on-success)
|
||||
(rx/tap on-success)
|
||||
(rx/catch on-error)
|
||||
(rx/finalize #(st/emit! (msg/hide-tag :media-loading)))))))))
|
||||
|
||||
|
||||
@@ -27,8 +27,8 @@
|
||||
[app.main.data.workspace.guides :as-alias dwg]
|
||||
[app.main.data.workspace.state-helpers :as wsh]
|
||||
[app.main.data.workspace.undo :as dwu]
|
||||
[beicon.core :as rx]
|
||||
[potok.core :as ptk]))
|
||||
[beicon.v2.core :as rx]
|
||||
[potok.v2.core :as ptk]))
|
||||
|
||||
;; -- temporary modifiers -------------------------------------------
|
||||
|
||||
@@ -191,14 +191,14 @@
|
||||
;; Temporary remove the children when moving them
|
||||
frame (-> frame
|
||||
(update :shapes #(d/removev ids %))
|
||||
(ctl/assign-cells))
|
||||
(ctl/assign-cells objects))
|
||||
|
||||
ids (->> ids (remove #(ctl/position-absolute? objects %)))
|
||||
frame (-> frame
|
||||
(update :shapes d/concat-vec ids)
|
||||
(cond-> (some? cell)
|
||||
(ctl/push-into-cell ids row column))
|
||||
(ctl/assign-cells))]
|
||||
(ctl/assign-cells objects))]
|
||||
|
||||
(-> modifiers
|
||||
(ctm/change-property :layout-grid-rows (:layout-grid-rows frame))
|
||||
|
||||
@@ -19,10 +19,11 @@
|
||||
[app.util.globals :refer [global]]
|
||||
[app.util.mouse :as mse]
|
||||
[app.util.object :as obj]
|
||||
[app.util.rxops :as rxs]
|
||||
[app.util.time :as dt]
|
||||
[beicon.core :as rx]
|
||||
[beicon.v2.core :as rx]
|
||||
[clojure.set :as set]
|
||||
[potok.core :as ptk]))
|
||||
[potok.v2.core :as ptk]))
|
||||
|
||||
(declare process-message)
|
||||
(declare handle-presence)
|
||||
@@ -82,7 +83,7 @@
|
||||
;; position changes.
|
||||
(->> stream
|
||||
(rx/filter mse/pointer-event?)
|
||||
(rx/sample 50)
|
||||
(rx/pipe (rxs/throttle 100))
|
||||
(rx/map #(handle-pointer-send file-id (:pt %)))))
|
||||
|
||||
(rx/take-until stoper))]
|
||||
@@ -132,7 +133,7 @@
|
||||
"#faa6b7" ; salmon
|
||||
"#f9b489" ; orange
|
||||
"#fdcd79" ; soft-orange
|
||||
"#dee563" ; yellow
|
||||
"#dee563" ; yellow -> default presence color
|
||||
"#b1e96f" ; yellow-green
|
||||
})
|
||||
|
||||
|
||||
@@ -13,8 +13,8 @@
|
||||
[app.main.data.workspace.path.helpers :as helpers]
|
||||
[app.main.data.workspace.path.state :as st]
|
||||
[app.main.data.workspace.state-helpers :as wsh]
|
||||
[beicon.core :as rx]
|
||||
[potok.core :as ptk]))
|
||||
[beicon.v2.core :as rx]
|
||||
[potok.v2.core :as ptk]))
|
||||
|
||||
(defn generate-path-changes
|
||||
"Generates changes to update the new content of the shape"
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
(:require
|
||||
[app.common.schema :as sm]
|
||||
[app.main.data.workspace.path.state :as st]
|
||||
[potok.core :as ptk]))
|
||||
[potok.v2.core :as ptk]))
|
||||
|
||||
(def valid-commands
|
||||
#{:move-to
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user