mirror of
https://github.com/penpot/penpot.git
synced 2025-12-30 18:08:33 -05:00
Compare commits
50 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c2b13a6d5d | ||
|
|
6935d54870 | ||
|
|
65e8526ee2 | ||
|
|
202762027f | ||
|
|
d95551e651 | ||
|
|
c96fbfdcd6 | ||
|
|
6e5d64d403 | ||
|
|
3e0c2bf1a1 | ||
|
|
283cdee5d6 | ||
|
|
ab5e01e54a | ||
|
|
373248e304 | ||
|
|
80308ceafa | ||
|
|
f65518f865 | ||
|
|
f155042958 | ||
|
|
1dd23a3f47 | ||
|
|
1194e40222 | ||
|
|
05fac41534 | ||
|
|
3f85e89f62 | ||
|
|
ee0f8ad19a | ||
|
|
b7d7cf233a | ||
|
|
bd208c31e2 | ||
|
|
151dc352c8 | ||
|
|
ccbf17106d | ||
|
|
95c4d95fd3 | ||
|
|
a72c07b657 | ||
|
|
708492afeb | ||
|
|
1305ab3cc6 | ||
|
|
29cc6b4f9c | ||
|
|
cc7f0b145c | ||
|
|
e69c0c3e27 | ||
|
|
a209966427 | ||
|
|
d5abbd4220 | ||
|
|
70a23a14c4 | ||
|
|
93c81ea49c | ||
|
|
ddc41027ab | ||
|
|
4f931fbe6a | ||
|
|
b49a4734ff | ||
|
|
2aaa2f3033 | ||
|
|
202b9f3075 | ||
|
|
be0814cdac | ||
|
|
80d719353c | ||
|
|
fa3fc12594 | ||
|
|
422a9db07b | ||
|
|
a4145a30f5 | ||
|
|
e004671346 | ||
|
|
38e5c161e7 | ||
|
|
a7c1f7ba69 | ||
|
|
e9755d437e | ||
|
|
e5db66351e | ||
|
|
89153eef23 |
18
CHANGES.md
18
CHANGES.md
@@ -1,5 +1,22 @@
|
||||
# CHANGELOG
|
||||
|
||||
## 2.6.2
|
||||
|
||||
### :bug: Bugs fixed
|
||||
|
||||
- Increase the height of the right sidebar dropdowns [Taiga #10615](https://tree.taiga.io/project/penpot/issue/10615)
|
||||
- Fix scroll on token themes modal [Taiga #10745](https://tree.taiga.io/project/penpot/issue/10745)
|
||||
- Fix collapsing grouped sets in "edit Theme" closes the dialog [Taiga #10771](https://tree.taiga.io/project/penpot/issue/10771)
|
||||
- Fix unexpected exception on path editor on merge segments when undo stack is empty
|
||||
- Fix pricing CTA to be under a config flag [Taiga #10808](https://tree.taiga.io/project/penpot/issue/10808)
|
||||
- Fix allow moving a main component into another [Taiga #10818](https://tree.taiga.io/project/penpot/issue/10818)
|
||||
- Fix several issues with internal srepl helpers
|
||||
- Fix unexpected exception on template import from libraries
|
||||
- Fix incorrect uuid parsing from different parts of code
|
||||
- Fix update layout on component restore [Taiga #10637](https://tree.taiga.io/project/penpot/issue/10637)
|
||||
- Fix horizontal scroll in viewer [Github #6290](https://github.com/penpot/penpot/issues/6290)
|
||||
- Fix detach component in a particular case [Taiga #10837](https://tree.taiga.io/project/penpot/issue/10837)
|
||||
|
||||
## 2.6.1
|
||||
|
||||
### :bug: Bugs fixed
|
||||
@@ -58,6 +75,7 @@
|
||||
- Add character limitation to asset inputs [Taiga #10669](https://tree.taiga.io/project/penpot/issue/10669)
|
||||
- Fix Storybook link 'list of all available icons' wrong path [Taiga #10705](https://tree.taiga.io/project/penpot/issue/10705)
|
||||
|
||||
|
||||
## 2.5.4
|
||||
|
||||
### :heart: Community contributions (Thank you!)
|
||||
|
||||
@@ -30,7 +30,8 @@ export PENPOT_FLAGS="\
|
||||
enable-access-tokens \
|
||||
enable-tiered-file-data-storage \
|
||||
enable-file-validation \
|
||||
enable-file-schema-validation";
|
||||
enable-file-schema-validation \
|
||||
enable-subscriptions-old";
|
||||
|
||||
# Default deletion delay for devenv
|
||||
export PENPOT_DELETION_DELAY="24h"
|
||||
|
||||
@@ -23,7 +23,8 @@ export PENPOT_FLAGS="\
|
||||
enable-access-tokens \
|
||||
enable-tiered-file-data-storage \
|
||||
enable-file-validation \
|
||||
enable-file-schema-validation";
|
||||
enable-file-schema-validation \
|
||||
enable-subscriptions-old";
|
||||
|
||||
export OPTIONS="
|
||||
-A:jmx-remote -A:dev \
|
||||
|
||||
44
backend/src/app/binfile/cleaner.clj
Normal file
44
backend/src/app/binfile/cleaner.clj
Normal file
@@ -0,0 +1,44 @@
|
||||
;; 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.binfile.cleaner
|
||||
"A collection of helpers for perform cleaning of artifacts; mainly
|
||||
for recently imported shapes."
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.uuid :as uuid]))
|
||||
|
||||
(defn- fix-shape-shadow-color
|
||||
"Some shapes can come with invalid `id` property on shadow colors
|
||||
caused by incorrect uuid parsing bug that should be already fixed;
|
||||
this function removes the invalid id from the data structure."
|
||||
[shape]
|
||||
(let [fix-color
|
||||
(fn [{:keys [id] :as color}]
|
||||
(if (uuid? id)
|
||||
color
|
||||
(if (and (string? id)
|
||||
(re-matches uuid/regex id))
|
||||
(assoc color :id (uuid/uuid id))
|
||||
(dissoc color :id))))
|
||||
|
||||
fix-shadow
|
||||
(fn [shadow]
|
||||
(d/update-when shadow :color fix-color))
|
||||
|
||||
xform
|
||||
(map fix-shadow)]
|
||||
|
||||
(d/update-when shape :shadow
|
||||
(fn [shadows]
|
||||
(into [] xform shadows)))))
|
||||
|
||||
(defn clean-shape-post-decode
|
||||
"A shape procesor that expected to be executed after schema decoding
|
||||
process but before validation."
|
||||
[shape]
|
||||
(-> shape
|
||||
(fix-shape-shadow-color)))
|
||||
@@ -8,12 +8,14 @@
|
||||
"A ZIP based binary file exportation"
|
||||
(:refer-clojure :exclude [read])
|
||||
(:require
|
||||
[app.binfile.cleaner :as bfl]
|
||||
[app.binfile.common :as bfc]
|
||||
[app.binfile.migrations :as bfm]
|
||||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.exceptions :as ex]
|
||||
[app.common.features :as cfeat]
|
||||
[app.common.files.migrations :as-alias fmg]
|
||||
[app.common.json :as json]
|
||||
[app.common.logging :as l]
|
||||
[app.common.schema :as sm]
|
||||
@@ -594,16 +596,25 @@
|
||||
|
||||
(defn- read-file-components
|
||||
[{:keys [::bfc/input ::file-id ::entries]}]
|
||||
(->> (keep (match-component-entry-fn file-id) entries)
|
||||
(reduce (fn [result {:keys [id entry]}]
|
||||
(let [object (->> (read-entry input entry)
|
||||
(decode-component)
|
||||
(validate-component))]
|
||||
(if (= id (:id object))
|
||||
(assoc result id object)
|
||||
result)))
|
||||
{})
|
||||
(not-empty)))
|
||||
(let [clean-component-post-decode
|
||||
(fn [component]
|
||||
(d/update-when component :objects
|
||||
(fn [objects]
|
||||
(reduce-kv (fn [objects id shape]
|
||||
(assoc objects id (bfl/clean-shape-post-decode shape)))
|
||||
objects
|
||||
objects))))]
|
||||
(->> (keep (match-component-entry-fn file-id) entries)
|
||||
(reduce (fn [result {:keys [id entry]}]
|
||||
(let [object (->> (read-entry input entry)
|
||||
(decode-component)
|
||||
(clean-component-post-decode)
|
||||
(validate-component))]
|
||||
(if (= id (:id object))
|
||||
(assoc result id object)
|
||||
result)))
|
||||
{})
|
||||
(not-empty))))
|
||||
|
||||
(defn- read-file-typographies
|
||||
[{:keys [::bfc/input ::file-id ::entries]}]
|
||||
@@ -631,7 +642,9 @@
|
||||
(reduce (fn [result {:keys [id entry]}]
|
||||
(let [object (->> (read-entry input entry)
|
||||
(decode-shape)
|
||||
(bfl/clean-shape-post-decode)
|
||||
(validate-shape))]
|
||||
|
||||
(if (= id (:id object))
|
||||
(assoc result id object)
|
||||
result)))
|
||||
@@ -733,7 +746,14 @@
|
||||
(assoc :name file-name)
|
||||
(assoc :project-id project-id)
|
||||
(dissoc :options)
|
||||
(bfc/process-file))]
|
||||
(bfc/process-file)
|
||||
|
||||
;; NOTE: this is necessary because when we just
|
||||
;; creating a new file from imported artifact,
|
||||
;; there are no migrations registered on the
|
||||
;; database, so we need to persist all of them, not
|
||||
;; only the applied
|
||||
(vary-meta dissoc ::fmg/migrated))]
|
||||
|
||||
|
||||
(bfm/register-pending-migrations! cfg file)
|
||||
|
||||
@@ -155,9 +155,9 @@
|
||||
[["" {:middleware [[mw/server-timing]
|
||||
[mw/params]
|
||||
[mw/format-response]
|
||||
[mw/parse-request]
|
||||
[session/soft-auth cfg]
|
||||
[actoken/soft-auth cfg]
|
||||
[mw/parse-request]
|
||||
[mw/errors errors/handle]
|
||||
[mw/restrict-methods]]}
|
||||
|
||||
|
||||
@@ -273,7 +273,7 @@
|
||||
|
||||
(defn- http-handler
|
||||
[cfg {:keys [params ::session/profile-id] :as request}]
|
||||
(let [session-id (some-> params :session-id sm/parse-uuid)]
|
||||
(let [session-id (some-> params :session-id uuid/parse*)]
|
||||
(when-not (uuid? session-id)
|
||||
(ex/raise :type :validation
|
||||
:code :missing-session-id
|
||||
|
||||
@@ -292,7 +292,7 @@
|
||||
|
||||
(defn get-file-etag
|
||||
[{:keys [::rpc/profile-id]} {:keys [modified-at revn vern permissions]}]
|
||||
(str profile-id "/" revn "/" vern "/"
|
||||
(str profile-id "/" revn "/" vern "/" (hash fmg/available-migrations) "/"
|
||||
(dt/format-instant modified-at :iso)
|
||||
"/"
|
||||
(uri/map->query-string permissions)))
|
||||
|
||||
@@ -180,8 +180,7 @@
|
||||
(def ^:private
|
||||
schema:get-file-data-for-thumbnail
|
||||
[:map {:title "get-file-data-for-thumbnail"}
|
||||
[:file-id ::sm/uuid]
|
||||
[:features {:optional true} ::cfeat/features]])
|
||||
[:file-id ::sm/uuid]])
|
||||
|
||||
(def ^:private
|
||||
schema:partial-file
|
||||
@@ -211,7 +210,6 @@
|
||||
(fmg/migrate-file)))]
|
||||
|
||||
(-> (cfeat/get-team-enabled-features cf/flags team)
|
||||
(cfeat/check-client-features! (:features params))
|
||||
(cfeat/check-file-features! (:features file)))
|
||||
|
||||
{:file-id file-id
|
||||
|
||||
@@ -337,14 +337,23 @@
|
||||
(db/tx-run! main/system fsnap/create-file-snapshot! {:file-id file-id :label label})))
|
||||
|
||||
(defn restore-file-snapshot!
|
||||
[file-id label]
|
||||
(let [file-id (h/parse-uuid file-id)]
|
||||
[file-id & {:keys [label id]}]
|
||||
(let [file-id (h/parse-uuid file-id)
|
||||
snapshot-id (some-> id h/parse-uuid)]
|
||||
(db/tx-run! main/system
|
||||
(fn [{:keys [::db/conn] :as system}]
|
||||
(when-let [snapshot (->> (h/search-file-snapshots conn #{file-id} label)
|
||||
(map :id)
|
||||
(first))]
|
||||
(fsnap/restore-file-snapshot! system file-id (:id snapshot)))))))
|
||||
(cond
|
||||
(uuid? snapshot-id)
|
||||
(fsnap/restore-file-snapshot! system file-id snapshot-id)
|
||||
|
||||
(string? label)
|
||||
(->> (h/search-file-snapshots conn #{file-id} label)
|
||||
(map :id)
|
||||
(first)
|
||||
(fsnap/restore-file-snapshot! system file-id))
|
||||
|
||||
:else
|
||||
(throw (ex-info "snapshot id or label should be provided" {})))))))
|
||||
|
||||
(defn list-file-snapshots!
|
||||
[file-id & {:as _}]
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
|
||||
(defn undo
|
||||
[stack]
|
||||
(update stack :index dec))
|
||||
(update stack :index #(max 0 (dec %))))
|
||||
|
||||
(defn redo
|
||||
[{index :index items :items :as stack}]
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
[app.common.types.container :as ctn]
|
||||
[app.common.types.file :as ctf]
|
||||
[app.common.types.shape :as cts]
|
||||
[app.common.types.shape.interactions :as ctsi]
|
||||
[app.common.types.shape.shadow :as ctss]
|
||||
[app.common.uuid :as uuid]
|
||||
[clojure.set :as set]
|
||||
@@ -35,9 +36,7 @@
|
||||
|
||||
#?(:cljs (l/set-level! :info))
|
||||
|
||||
(declare ^:private available-migrations)
|
||||
(declare ^:private migration-up-index)
|
||||
(declare ^:private migration-down-index)
|
||||
(declare available-migrations)
|
||||
|
||||
(def version cfd/version)
|
||||
|
||||
@@ -49,7 +48,10 @@
|
||||
[file]
|
||||
(or (nil? (:version file))
|
||||
(not= cfd/version (:version file))
|
||||
(not= available-migrations (:migrations file))))
|
||||
(boolean
|
||||
(->> (:migrations file #{})
|
||||
(set/difference available-migrations)
|
||||
(not-empty)))))
|
||||
|
||||
(def xf:map-name
|
||||
(map :name))
|
||||
@@ -119,9 +121,9 @@
|
||||
(into [] shapes)
|
||||
shapes))))
|
||||
(update-page [page]
|
||||
(update page :objects update-vals update-object))]
|
||||
(update page :objects d/update-vals update-object))]
|
||||
|
||||
(update data :pages-index update-vals update-page)))
|
||||
(update data :pages-index d/update-vals update-page)))
|
||||
|
||||
(defmethod migrate-data "legacy-3"
|
||||
[data _]
|
||||
@@ -172,9 +174,9 @@
|
||||
(fix-empty-points)))
|
||||
|
||||
(update-page [page]
|
||||
(update page :objects update-vals update-object))]
|
||||
(update page :objects d/update-vals update-object))]
|
||||
|
||||
(update data :pages-index update-vals update-page)))
|
||||
(update data :pages-index d/update-vals update-page)))
|
||||
|
||||
;; Put the id of the local file in :component-file in instances of
|
||||
;; local components
|
||||
@@ -187,9 +189,9 @@
|
||||
object))
|
||||
|
||||
(update-page [page]
|
||||
(update page :objects update-vals update-object))]
|
||||
(update page :objects d/update-vals update-object))]
|
||||
|
||||
(update data :pages-index update-vals update-page)))
|
||||
(update data :pages-index d/update-vals update-page)))
|
||||
|
||||
;; Fixes issues with selrect/points for shapes with width/height =
|
||||
;; 0 (line-like paths)
|
||||
@@ -212,11 +214,11 @@
|
||||
shape))
|
||||
|
||||
(update-container [container]
|
||||
(update container :objects update-vals fix-line-paths))]
|
||||
(update container :objects d/update-vals fix-line-paths))]
|
||||
|
||||
(-> data
|
||||
(update :pages-index update-vals update-container)
|
||||
(update :components update-vals update-container))))
|
||||
(update :pages-index d/update-vals update-container)
|
||||
(d/update-when :components d/update-vals update-container))))
|
||||
|
||||
;; Remove interactions pointing to deleted frames
|
||||
(defmethod migrate-data "legacy-7"
|
||||
@@ -227,9 +229,9 @@
|
||||
(filterv #(get-in page [:objects (:destination %)]) interactions))))
|
||||
|
||||
(update-page [page]
|
||||
(update page :objects update-vals (partial update-object page)))]
|
||||
(update page :objects d/update-vals (partial update-object page)))]
|
||||
|
||||
(update data :pages-index update-vals update-page)))
|
||||
(update data :pages-index d/update-vals update-page)))
|
||||
|
||||
;; Remove groups without any shape, both in pages and components
|
||||
(defmethod migrate-data "legacy-8"
|
||||
@@ -269,8 +271,8 @@
|
||||
(assoc container :objects objects)))))]
|
||||
|
||||
(-> data
|
||||
(update :pages-index update-vals clean-container)
|
||||
(update :components update-vals clean-container))))
|
||||
(update :pages-index d/update-vals clean-container)
|
||||
(d/update-when :components d/update-vals clean-container))))
|
||||
|
||||
(defmethod migrate-data "legacy-9"
|
||||
[data _]
|
||||
@@ -304,7 +306,7 @@
|
||||
[data _]
|
||||
(letfn [(update-page [page]
|
||||
(d/update-in-when page [:objects uuid/zero] dissoc :points :selrect))]
|
||||
(update data :pages-index update-vals update-page)))
|
||||
(update data :pages-index d/update-vals update-page)))
|
||||
|
||||
(defmethod migrate-data "legacy-11"
|
||||
[data _]
|
||||
@@ -318,7 +320,7 @@
|
||||
(update page :objects (fn [objects]
|
||||
(update-vals objects (partial update-object objects)))))]
|
||||
|
||||
(update data :pages-index update-vals update-page)))
|
||||
(update data :pages-index d/update-vals update-page)))
|
||||
|
||||
(defmethod migrate-data "legacy-12"
|
||||
[data _]
|
||||
@@ -328,9 +330,9 @@
|
||||
(assoc :size nil)))
|
||||
|
||||
(update-page [page]
|
||||
(d/update-in-when page [:options :saved-grids] update-vals update-grid))]
|
||||
(d/update-in-when page [:options :saved-grids] d/update-vals update-grid))]
|
||||
|
||||
(update data :pages-index update-vals update-page)))
|
||||
(update data :pages-index d/update-vals update-page)))
|
||||
|
||||
;; Add rx and ry to images
|
||||
(defmethod migrate-data "legacy-13"
|
||||
@@ -348,9 +350,9 @@
|
||||
(fix-radius)))
|
||||
|
||||
(update-page [page]
|
||||
(update page :objects update-vals update-object))]
|
||||
(update page :objects d/update-vals update-object))]
|
||||
|
||||
(update data :pages-index update-vals update-page)))
|
||||
(update data :pages-index d/update-vals update-page)))
|
||||
|
||||
(defmethod migrate-data "legacy-14"
|
||||
[data _]
|
||||
@@ -380,8 +382,8 @@
|
||||
container))]
|
||||
|
||||
(-> data
|
||||
(update :pages-index update-vals update-container)
|
||||
(update :components update-vals update-container))))
|
||||
(update :pages-index d/update-vals update-container)
|
||||
(d/update-when :components d/update-vals update-container))))
|
||||
|
||||
(defmethod migrate-data "legacy-16"
|
||||
[data _]
|
||||
@@ -423,11 +425,11 @@
|
||||
(assign-fills)))
|
||||
|
||||
(update-container [container]
|
||||
(d/update-when container :objects update-vals update-object))]
|
||||
(d/update-when container :objects d/update-vals update-object))]
|
||||
|
||||
(-> data
|
||||
(update :pages-index update-vals update-container)
|
||||
(update :components update-vals update-container))))
|
||||
(update :pages-index d/update-vals update-container)
|
||||
(d/update-when :components d/update-vals update-container))))
|
||||
|
||||
(defmethod migrate-data "legacy-17"
|
||||
[data _]
|
||||
@@ -452,11 +454,11 @@
|
||||
(assoc :fills [])))
|
||||
|
||||
(update-container [container]
|
||||
(d/update-when container :objects update-vals update-object))]
|
||||
(d/update-when container :objects d/update-vals update-object))]
|
||||
|
||||
(-> data
|
||||
(update :pages-index update-vals update-container)
|
||||
(update :components update-vals update-container))))
|
||||
(update :pages-index d/update-vals update-container)
|
||||
(d/update-when :components d/update-vals update-container))))
|
||||
|
||||
;; Remove position-data to solve a bug with the text positioning
|
||||
(defmethod migrate-data "legacy-18"
|
||||
@@ -467,11 +469,11 @@
|
||||
(dissoc :position-data)))
|
||||
|
||||
(update-container [container]
|
||||
(d/update-when container :objects update-vals update-object))]
|
||||
(d/update-when container :objects d/update-vals update-object))]
|
||||
|
||||
(-> data
|
||||
(update :pages-index update-vals update-container)
|
||||
(update :components update-vals update-container))))
|
||||
(update :pages-index d/update-vals update-container)
|
||||
(d/update-when :components d/update-vals update-container))))
|
||||
|
||||
(defmethod migrate-data "legacy-19"
|
||||
[data _]
|
||||
@@ -483,11 +485,11 @@
|
||||
(dissoc :position-data)))
|
||||
|
||||
(update-container [container]
|
||||
(d/update-when container :objects update-vals update-object))]
|
||||
(d/update-when container :objects d/update-vals update-object))]
|
||||
|
||||
(-> data
|
||||
(update :pages-index update-vals update-container)
|
||||
(update :components update-vals update-container))))
|
||||
(update :pages-index d/update-vals update-container)
|
||||
(d/update-when :components d/update-vals update-container))))
|
||||
|
||||
(defmethod migrate-data "legacy-25"
|
||||
[data _]
|
||||
@@ -499,10 +501,10 @@
|
||||
(update :selrect grc/make-rect)
|
||||
(cts/create-shape))))
|
||||
(update-container [container]
|
||||
(d/update-when container :objects update-vals update-object))]
|
||||
(d/update-when container :objects d/update-vals update-object))]
|
||||
(-> data
|
||||
(update :pages-index update-vals update-container)
|
||||
(update :components update-vals update-container))))
|
||||
(update :pages-index d/update-vals update-container)
|
||||
(d/update-when :components d/update-vals update-container))))
|
||||
|
||||
(defmethod migrate-data "legacy-26"
|
||||
[data _]
|
||||
@@ -515,11 +517,11 @@
|
||||
(assoc :transform-inverse (gmt/matrix))))
|
||||
|
||||
(update-container [container]
|
||||
(d/update-when container :objects update-vals update-object))]
|
||||
(d/update-when container :objects d/update-vals update-object))]
|
||||
|
||||
(-> data
|
||||
(update :pages-index update-vals update-container)
|
||||
(update :components update-vals update-container))))
|
||||
(update :pages-index d/update-vals update-container)
|
||||
(d/update-when :components d/update-vals update-container))))
|
||||
|
||||
(defmethod migrate-data "legacy-27"
|
||||
[data _]
|
||||
@@ -546,11 +548,11 @@
|
||||
(dissoc :saved-component-root?))))
|
||||
|
||||
(update-container [container]
|
||||
(d/update-when container :objects update-vals update-object))]
|
||||
(d/update-when container :objects d/update-vals update-object))]
|
||||
|
||||
(-> data
|
||||
(update :pages-index update-vals update-container)
|
||||
(update :components update-vals update-container))))
|
||||
(update :pages-index d/update-vals update-container)
|
||||
(d/update-when :components d/update-vals update-container))))
|
||||
|
||||
(defmethod migrate-data "legacy-28"
|
||||
[data _]
|
||||
@@ -575,8 +577,8 @@
|
||||
(d/update-when container :objects #(update-vals % (partial update-object %))))]
|
||||
|
||||
(-> data
|
||||
(update :pages-index update-vals update-container)
|
||||
(update :components update-vals update-container))))
|
||||
(update :pages-index d/update-vals update-container)
|
||||
(d/update-when :components d/update-vals update-container))))
|
||||
|
||||
(defmethod migrate-data "legacy-29"
|
||||
[data _]
|
||||
@@ -607,11 +609,11 @@
|
||||
(update :content #(txt/transform-nodes invalid-node? fix-node %)))))
|
||||
|
||||
(update-container [container]
|
||||
(d/update-when container :objects update-vals update-object))]
|
||||
(d/update-when container :objects d/update-vals update-object))]
|
||||
|
||||
(-> data
|
||||
(update :pages-index update-vals update-container)
|
||||
(update :components update-vals update-container))))
|
||||
(update :pages-index d/update-vals update-container)
|
||||
(d/update-when :components d/update-vals update-container))))
|
||||
|
||||
(defmethod migrate-data "legacy-31"
|
||||
[data _]
|
||||
@@ -622,10 +624,10 @@
|
||||
(dissoc :use-for-thumbnail?))))
|
||||
|
||||
(update-container [container]
|
||||
(d/update-when container :objects update-vals update-object))]
|
||||
(d/update-when container :objects d/update-vals update-object))]
|
||||
(-> data
|
||||
(update :pages-index update-vals update-container)
|
||||
(update :components update-vals update-container))))
|
||||
(update :pages-index d/update-vals update-container)
|
||||
(d/update-when :components d/update-vals update-container))))
|
||||
|
||||
(defmethod migrate-data "legacy-32"
|
||||
[data _]
|
||||
@@ -640,11 +642,11 @@
|
||||
object)))
|
||||
|
||||
(update-container [container]
|
||||
(d/update-when container :objects update-vals update-object))]
|
||||
(d/update-when container :objects d/update-vals update-object))]
|
||||
|
||||
(-> data
|
||||
(update :pages-index update-vals update-container)
|
||||
(update :components update-vals update-container))))
|
||||
(update :pages-index d/update-vals update-container)
|
||||
(d/update-when :components d/update-vals update-container))))
|
||||
|
||||
(defmethod migrate-data "legacy-33"
|
||||
[data _]
|
||||
@@ -662,9 +664,9 @@
|
||||
object))
|
||||
|
||||
(update-container [container]
|
||||
(d/update-when container :objects update-vals update-object))]
|
||||
(d/update-when container :objects d/update-vals update-object))]
|
||||
(-> data
|
||||
(update :pages-index update-vals update-container))))
|
||||
(update :pages-index d/update-vals update-container))))
|
||||
|
||||
(defmethod migrate-data "legacy-34"
|
||||
[data _]
|
||||
@@ -674,10 +676,10 @@
|
||||
(dissoc object :x :y :width :height)
|
||||
object))
|
||||
(update-container [container]
|
||||
(d/update-when container :objects update-vals update-object))]
|
||||
(d/update-when container :objects d/update-vals update-object))]
|
||||
(-> data
|
||||
(update :pages-index update-vals update-container)
|
||||
(update :components update-vals update-container))))
|
||||
(update :pages-index d/update-vals update-container)
|
||||
(d/update-when :components d/update-vals update-container))))
|
||||
|
||||
(defmethod migrate-data "legacy-36"
|
||||
[data _]
|
||||
@@ -687,8 +689,8 @@
|
||||
(dissoc objects nil)
|
||||
objects))))]
|
||||
(-> data
|
||||
(update :pages-index update-vals update-container)
|
||||
(update :components update-vals update-container))))
|
||||
(update :pages-index d/update-vals update-container)
|
||||
(d/update-when :components d/update-vals update-container))))
|
||||
|
||||
(defmethod migrate-data "legacy-37"
|
||||
[data _]
|
||||
@@ -716,11 +718,11 @@
|
||||
shape)))
|
||||
|
||||
(update-container [container]
|
||||
(d/update-when container :objects update-vals update-shape))]
|
||||
(d/update-when container :objects d/update-vals update-shape))]
|
||||
|
||||
(-> data
|
||||
(update :pages-index update-vals update-container)
|
||||
(update :components update-vals update-container))))
|
||||
(update :pages-index d/update-vals update-container)
|
||||
(d/update-when :components d/update-vals update-container))))
|
||||
|
||||
(defmethod migrate-data "legacy-39"
|
||||
[data _]
|
||||
@@ -738,11 +740,11 @@
|
||||
shape))
|
||||
|
||||
(update-container [container]
|
||||
(d/update-when container :objects update-vals update-shape))]
|
||||
(d/update-when container :objects d/update-vals update-shape))]
|
||||
|
||||
(-> data
|
||||
(update :pages-index update-vals update-container)
|
||||
(update :components update-vals update-container))))
|
||||
(update :pages-index d/update-vals update-container)
|
||||
(d/update-when :components d/update-vals update-container))))
|
||||
|
||||
(defmethod migrate-data "legacy-40"
|
||||
[data _]
|
||||
@@ -762,11 +764,11 @@
|
||||
shape))
|
||||
|
||||
(update-container [container]
|
||||
(d/update-when container :objects update-vals update-shape))]
|
||||
(d/update-when container :objects d/update-vals update-shape))]
|
||||
|
||||
(-> data
|
||||
(update :pages-index update-vals update-container)
|
||||
(update :components update-vals update-container))))
|
||||
(update :pages-index d/update-vals update-container)
|
||||
(d/update-when :components d/update-vals update-container))))
|
||||
|
||||
(defmethod migrate-data "legacy-41"
|
||||
[data _]
|
||||
@@ -795,11 +797,11 @@
|
||||
shape))
|
||||
|
||||
(update-container [container]
|
||||
(d/update-when container :objects update-vals update-shape))]
|
||||
(d/update-when container :objects d/update-vals update-shape))]
|
||||
|
||||
(-> data
|
||||
(update :pages-index update-vals update-container)
|
||||
(update :components update-vals update-container))))
|
||||
(update :pages-index d/update-vals update-container)
|
||||
(d/update-when :components d/update-vals update-container))))
|
||||
|
||||
(defmethod migrate-data "legacy-42"
|
||||
[data _]
|
||||
@@ -812,11 +814,11 @@
|
||||
object))
|
||||
|
||||
(update-container [container]
|
||||
(d/update-when container :objects update-vals update-object))]
|
||||
(d/update-when container :objects d/update-vals update-object))]
|
||||
|
||||
(-> data
|
||||
(update :pages-index update-vals update-container)
|
||||
(update :components update-vals update-container))))
|
||||
(update :pages-index d/update-vals update-container)
|
||||
(d/update-when :components d/update-vals update-container))))
|
||||
|
||||
(def ^:private valid-fill?
|
||||
(sm/lazy-validator ::cts/fill))
|
||||
@@ -841,14 +843,11 @@
|
||||
object))
|
||||
|
||||
(update-container [container]
|
||||
(d/update-when container :objects update-vals update-object))]
|
||||
(d/update-when container :objects d/update-vals update-object))]
|
||||
|
||||
(-> data
|
||||
(update :pages-index update-vals update-container)
|
||||
(update :components update-vals update-container))))
|
||||
|
||||
(def ^:private valid-shadow?
|
||||
(sm/lazy-validator ::ctss/shadow))
|
||||
(update :pages-index d/update-vals update-container)
|
||||
(d/update-when :components d/update-vals update-container))))
|
||||
|
||||
(defmethod migrate-data "legacy-44"
|
||||
[data _]
|
||||
@@ -861,14 +860,14 @@
|
||||
|
||||
(update-object [object]
|
||||
(let [xform (comp (map fix-shadow)
|
||||
(filter valid-shadow?))]
|
||||
(filter ctss/valid-shadow?))]
|
||||
(d/update-when object :shadow #(into [] xform %))))
|
||||
|
||||
(update-container [container]
|
||||
(d/update-when container :objects update-vals update-object))]
|
||||
(d/update-when container :objects d/update-vals update-object))]
|
||||
(-> data
|
||||
(update :pages-index update-vals update-container)
|
||||
(update :components update-vals update-container))))
|
||||
(update :pages-index d/update-vals update-container)
|
||||
(d/update-when :components d/update-vals update-container))))
|
||||
|
||||
(defmethod migrate-data "legacy-45"
|
||||
[data _]
|
||||
@@ -881,9 +880,9 @@
|
||||
:parent-id parent-id)))
|
||||
|
||||
(update-container [container]
|
||||
(d/update-when container :objects update-vals fix-shape))]
|
||||
(d/update-when container :objects d/update-vals fix-shape))]
|
||||
(-> data
|
||||
(update :pages-index update-vals update-container))))
|
||||
(update :pages-index d/update-vals update-container))))
|
||||
|
||||
(defmethod migrate-data "legacy-46"
|
||||
[data _]
|
||||
@@ -891,10 +890,10 @@
|
||||
(dissoc object :thumbnail))
|
||||
|
||||
(update-container [container]
|
||||
(d/update-when container :objects update-vals update-object))]
|
||||
(d/update-when container :objects d/update-vals update-object))]
|
||||
(-> data
|
||||
(update :pages-index update-vals update-container)
|
||||
(update :components update-vals update-container))))
|
||||
(update :pages-index d/update-vals update-container)
|
||||
(d/update-when :components d/update-vals update-container))))
|
||||
|
||||
(defmethod migrate-data "legacy-47"
|
||||
[data _]
|
||||
@@ -915,9 +914,9 @@
|
||||
shape)))
|
||||
|
||||
(update-page [page]
|
||||
(d/update-when page :objects update-vals (partial fix-shape page)))]
|
||||
(d/update-when page :objects d/update-vals (partial fix-shape page)))]
|
||||
(-> data
|
||||
(update :pages-index update-vals update-page))))
|
||||
(update :pages-index d/update-vals update-page))))
|
||||
|
||||
(defmethod migrate-data "legacy-48"
|
||||
[data _]
|
||||
@@ -929,9 +928,9 @@
|
||||
shape)))
|
||||
|
||||
(update-page [page]
|
||||
(d/update-when page :objects update-vals fix-shape))]
|
||||
(d/update-when page :objects d/update-vals fix-shape))]
|
||||
(-> data
|
||||
(update :pages-index update-vals update-page))))
|
||||
(update :pages-index d/update-vals update-page))))
|
||||
|
||||
;; Remove hide-in-viewer for shapes that are origin or destination of an interaction
|
||||
(defmethod migrate-data "legacy-49"
|
||||
@@ -949,9 +948,9 @@
|
||||
(mapcat :interactions)
|
||||
(map :destination)
|
||||
(set))]
|
||||
(update page :objects update-vals (partial update-object destinations))))]
|
||||
(update page :objects d/update-vals (partial update-object destinations))))]
|
||||
|
||||
(update data :pages-index update-vals update-page)))
|
||||
(update data :pages-index d/update-vals update-page)))
|
||||
|
||||
;; This migration mainly fixes paths with curve-to segments
|
||||
;; without :c1x :c1y :c2x :c2y properties. Additionally, we found a
|
||||
@@ -994,11 +993,11 @@
|
||||
|
||||
update-container
|
||||
(fn [page]
|
||||
(d/update-when page :objects update-vals update-shape))]
|
||||
(d/update-when page :objects d/update-vals update-shape))]
|
||||
|
||||
(-> data
|
||||
(update :pages-index update-vals update-container)
|
||||
(update :components update-vals update-container))))
|
||||
(update :pages-index d/update-vals update-container)
|
||||
(d/update-when :components d/update-vals update-container))))
|
||||
|
||||
(def ^:private valid-color?
|
||||
(sm/lazy-validator ::ctc/color))
|
||||
@@ -1018,9 +1017,9 @@
|
||||
shape))
|
||||
|
||||
(update-page [page]
|
||||
(d/update-when page :objects update-vals update-shape))]
|
||||
(d/update-when page :objects d/update-vals update-shape))]
|
||||
|
||||
(update data :pages-index update-vals update-page)))
|
||||
(update data :pages-index d/update-vals update-page)))
|
||||
|
||||
|
||||
(defmethod migrate-data "legacy-53"
|
||||
@@ -1036,15 +1035,15 @@
|
||||
|
||||
(update-shape [shape]
|
||||
(let [xform (comp (map fix-shadow)
|
||||
(filter valid-shadow?))]
|
||||
(filter ctss/valid-shadow?))]
|
||||
(d/update-when shape :shadow #(into [] xform %))))
|
||||
|
||||
(update-container [container]
|
||||
(d/update-when container :objects update-vals update-shape))]
|
||||
(d/update-when container :objects d/update-vals update-shape))]
|
||||
|
||||
(-> data
|
||||
(update :pages-index update-vals update-container)
|
||||
(update :components update-vals update-container))))
|
||||
(update :pages-index d/update-vals update-container)
|
||||
(d/update-when :components d/update-vals update-container))))
|
||||
|
||||
;; This migration moves page options to the page level
|
||||
(defmethod migrate-data "legacy-55"
|
||||
@@ -1096,11 +1095,11 @@
|
||||
(update :content (partial txt/transform-nodes identity fix-fills)))))
|
||||
|
||||
(update-container [container]
|
||||
(d/update-when container :objects update-vals update-object))]
|
||||
(d/update-when container :objects d/update-vals update-object))]
|
||||
|
||||
(-> data
|
||||
(update :pages-index update-vals update-container)
|
||||
(update :components update-vals update-container))))
|
||||
(update :pages-index d/update-vals update-container)
|
||||
(d/update-when :components d/update-vals update-container))))
|
||||
|
||||
|
||||
(defmethod migrate-data "legacy-57"
|
||||
@@ -1127,7 +1126,7 @@
|
||||
(-> data
|
||||
(update :pages (fn [pages] (into [] (remove nil?) pages)))
|
||||
(update :pages-index dissoc nil)
|
||||
(update :pages-index update-vals update-page))))
|
||||
(update :pages-index d/update-vals update-page))))
|
||||
|
||||
(defmethod migrate-data "legacy-59"
|
||||
[data _]
|
||||
@@ -1138,11 +1137,11 @@
|
||||
(d/update-when shape :touched #(into #{} (map fix-touched) %)))
|
||||
|
||||
(update-container [container]
|
||||
(d/update-when container :objects update-vals update-shape))]
|
||||
(d/update-when container :objects d/update-vals update-shape))]
|
||||
|
||||
(-> data
|
||||
(update :pages-index update-vals update-container)
|
||||
(update :components update-vals update-container))))
|
||||
(update :pages-index d/update-vals update-container)
|
||||
(d/update-when :components d/update-vals update-container))))
|
||||
|
||||
(defmethod migrate-data "legacy-62"
|
||||
[data _]
|
||||
@@ -1175,7 +1174,7 @@
|
||||
;; so the relevant objects are inside the component
|
||||
(d/update-when component :objects remove-cycles))]
|
||||
|
||||
(update data :components update-vals update-component)))
|
||||
(d/update-when data :components d/update-vals update-component)))
|
||||
|
||||
(defmethod migrate-data "legacy-65"
|
||||
[data _]
|
||||
@@ -1186,14 +1185,14 @@
|
||||
update-page
|
||||
(fn [page]
|
||||
(-> (update-object page)
|
||||
(update :objects update-vals update-object)))]
|
||||
(update :objects d/update-vals update-object)))]
|
||||
|
||||
(-> data
|
||||
(update-object)
|
||||
(d/update-when :pages-index update-vals update-page)
|
||||
(d/update-when :colors update-vals update-object)
|
||||
(d/update-when :typographies update-vals update-object)
|
||||
(d/update-when :components update-vals update-object))))
|
||||
(update :pages-index d/update-vals update-page)
|
||||
(d/update-when :colors d/update-vals update-object)
|
||||
(d/update-when :typographies d/update-vals update-object)
|
||||
(d/update-when :components d/update-vals update-object))))
|
||||
|
||||
(defmethod migrate-data "legacy-66"
|
||||
[data _]
|
||||
@@ -1207,11 +1206,11 @@
|
||||
object))
|
||||
|
||||
(update-container [container]
|
||||
(d/update-when container :objects update-vals update-object))]
|
||||
(d/update-when container :objects d/update-vals update-object))]
|
||||
|
||||
(-> data
|
||||
(update :pages-index update-vals update-container)
|
||||
(update :components update-vals update-container))))
|
||||
(update :pages-index d/update-vals update-container)
|
||||
(d/update-when :components d/update-vals update-container))))
|
||||
|
||||
(defmethod migrate-data "legacy-67"
|
||||
[data _]
|
||||
@@ -1219,11 +1218,11 @@
|
||||
(d/update-when object :shadow #(into [] (reverse %))))
|
||||
|
||||
(update-container [container]
|
||||
(d/update-when container :objects update-vals update-object))]
|
||||
(d/update-when container :objects d/update-vals update-object))]
|
||||
|
||||
(-> data
|
||||
(update :pages-index update-vals update-container)
|
||||
(update :components update-vals update-container))))
|
||||
(update :pages-index d/update-vals update-container)
|
||||
(d/update-when :components d/update-vals update-container))))
|
||||
|
||||
(defmethod migrate-data "0001-remove-tokens-from-groups"
|
||||
[data _]
|
||||
@@ -1237,8 +1236,55 @@
|
||||
(dissoc :applied-tokens)))
|
||||
|
||||
(update-page [page]
|
||||
(d/update-when page :objects update-vals update-object))]
|
||||
(update data :pages-index update-vals update-page)))
|
||||
(d/update-when page :objects d/update-vals update-object))]
|
||||
|
||||
(update data :pages-index d/update-vals update-page)))
|
||||
|
||||
(defmethod migrate-data "0002-clean-shape-interactions"
|
||||
[data _]
|
||||
(let [decode-fn (sm/decoder ctsi/schema:interaction sm/json-transformer)
|
||||
validate-fn (sm/validator ctsi/schema:interaction)
|
||||
|
||||
xform
|
||||
(comp
|
||||
(map decode-fn)
|
||||
(filter validate-fn))
|
||||
|
||||
update-object
|
||||
(fn [object]
|
||||
(d/update-when object :interactions
|
||||
(fn [interactions]
|
||||
(into [] xform interactions))))
|
||||
|
||||
update-container
|
||||
(fn [container]
|
||||
(d/update-when container :objects d/update-vals update-object))]
|
||||
|
||||
(-> data
|
||||
(update :pages-index d/update-vals update-container)
|
||||
(d/update-when :components d/update-vals update-container))))
|
||||
|
||||
(defmethod migrate-data "0003-fix-root-shape"
|
||||
[data _]
|
||||
(letfn [(update-object [shape]
|
||||
(if (= (:id shape) uuid/zero)
|
||||
(-> shape
|
||||
(assoc :parent-id uuid/zero)
|
||||
(assoc :frame-id uuid/zero)
|
||||
;; We explicitly dissoc them and let the shape-setup
|
||||
;; to regenerate it with valid values.
|
||||
(dissoc :selrect)
|
||||
(dissoc :points)
|
||||
(cts/setup-shape))
|
||||
shape))
|
||||
|
||||
(update-container [container]
|
||||
(d/update-when container :objects d/update-vals update-object))]
|
||||
|
||||
(-> data
|
||||
(update :pages-index d/update-vals update-container)
|
||||
(d/update-when :components d/update-vals update-container)
|
||||
(d/without-nils))))
|
||||
|
||||
(def available-migrations
|
||||
(into (d/ordered-set)
|
||||
@@ -1294,4 +1340,6 @@
|
||||
"legacy-65"
|
||||
"legacy-66"
|
||||
"legacy-67"
|
||||
"0001-remove-tokens-from-groups"]))
|
||||
"0001-remove-tokens-from-groups"
|
||||
"0002-clean-shape-interactions"
|
||||
"0003-fix-root-shape"]))
|
||||
|
||||
@@ -124,7 +124,8 @@
|
||||
;; TODO: deprecate this flag and consolidate the code
|
||||
:export-file-v3
|
||||
:render-wasm-dpr
|
||||
:hide-release-modal})
|
||||
:hide-release-modal
|
||||
:subscriptions-old})
|
||||
|
||||
(def all-flags
|
||||
(set/union email login varia))
|
||||
|
||||
@@ -36,6 +36,8 @@
|
||||
;; Change this to :info :debug or :trace to debug this module, or :warn to reset to default
|
||||
(log/set-level! :warn)
|
||||
|
||||
;; Add uuids here to filter logs to only show specific shapes or containers (and all shapes
|
||||
;; contained in them).
|
||||
(def log-shape-ids #{})
|
||||
(def log-container-ids #{})
|
||||
|
||||
@@ -293,6 +295,7 @@
|
||||
|
||||
(declare generate-detach-recursive)
|
||||
(declare generate-advance-nesting-level)
|
||||
(declare generate-detach-immediate)
|
||||
|
||||
(defn generate-detach-instance
|
||||
"Generate changes to remove the links between a shape and all its children
|
||||
@@ -306,60 +309,84 @@
|
||||
(defn- generate-detach-recursive
|
||||
[changes container libraries shape-id first component-root?]
|
||||
(let [shape (ctn/get-shape container shape-id)]
|
||||
(shape-log :trace shape-id container
|
||||
:msg " Processing" :shape-id shape-id)
|
||||
(if (and (ctk/instance-head? shape) (not first))
|
||||
; Subinstances are not detached
|
||||
(cond-> changes
|
||||
component-root?
|
||||
; If the initial shape was component-root, first level subinstances are converted in top instances
|
||||
(pcb/update-shapes [shape-id] #(assoc % :component-root true))
|
||||
(pcb/update-shapes [shape-id] #(do (log/trace :msg " -> promote to root")
|
||||
(assoc % :component-root true)))
|
||||
|
||||
:always
|
||||
; First level subinstances of a detached component can't have swap-slot
|
||||
(pcb/update-shapes [shape-id] ctk/remove-swap-slot)
|
||||
(pcb/update-shapes [shape-id] #(do (log/trace :msg " -> remove swap-slot")
|
||||
(ctk/remove-swap-slot %)))
|
||||
|
||||
(nil? (ctk/get-swap-slot shape))
|
||||
; Near shape-refs need to be advanced one level (except if the head is already swapped)
|
||||
; Near shape-ref of shape and children need to be advanced one level
|
||||
; (except if the head is already swapped)
|
||||
(generate-advance-nesting-level nil container libraries (:id shape)))
|
||||
|
||||
;; Otherwise, detach the shape and all children
|
||||
(let [children-ids (:shapes shape)]
|
||||
(log/trace :msg " -> detach")
|
||||
(reduce #(generate-detach-recursive %1 container libraries %2 false component-root?)
|
||||
(pcb/update-shapes changes [(:id shape)] ctk/detach-shape)
|
||||
children-ids)))))
|
||||
|
||||
(defn- generate-advance-nesting-level
|
||||
[changes file container libraries shape-id]
|
||||
(let [children (cfh/get-children-with-self (:objects container) shape-id)
|
||||
skip-near (fn [changes shape]
|
||||
(let [ref-shape (ctf/find-ref-shape file container libraries shape {:include-deleted? true})]
|
||||
(cond-> changes
|
||||
(some? (:shape-ref ref-shape))
|
||||
(pcb/update-shapes [(:id shape)] #(assoc % :shape-ref (:shape-ref ref-shape)))
|
||||
(log/trace :msg " -> advance-nesting-level")
|
||||
(let [detached-ids (atom #{})
|
||||
children (cfh/get-children-with-self (:objects container) shape-id) ;; TODO: this function should be refactored to be a recursive tree traversal.
|
||||
skip-near (fn [changes shape] ;; this way we could shake the tree more easily when detaching shapes
|
||||
(shape-log :trace (:id shape) container ;; and perhaps even allow to recover nested instances that have been
|
||||
:msg " * advancing" :shape-id (:id shape)) ;; swapped and so we can access the main instance again.
|
||||
(if (contains? @detached-ids (:id shape))
|
||||
(do (log/trace :msg " (detached)")
|
||||
changes)
|
||||
(let [ref-shape (ctf/find-ref-shape file container libraries shape {:include-deleted? true})]
|
||||
(cond-> changes
|
||||
(some? (:shape-ref ref-shape))
|
||||
(pcb/update-shapes [(:id shape)] #(do (log/trace :msg " (advanced)")
|
||||
(assoc % :shape-ref (:shape-ref ref-shape))))
|
||||
|
||||
;; When advancing level, the normal touched groups (not swap slots) of the
|
||||
;; ref-shape must be merged into the current shape, because they refer to
|
||||
;; the new referenced shape.
|
||||
(some? ref-shape)
|
||||
(pcb/update-shapes
|
||||
[(:id shape)]
|
||||
#(assoc % :touched
|
||||
(clojure.set/union (:touched shape)
|
||||
(ctk/normal-touched-groups ref-shape))))
|
||||
;; When advancing level, the normal touched groups (not swap slots) of the
|
||||
;; ref-shape must be merged into the current shape, because they refer to
|
||||
;; the new referenced shape.
|
||||
(some? ref-shape)
|
||||
(pcb/update-shapes
|
||||
[(:id shape)]
|
||||
#(do (log/trace :msg " (merge touched)")
|
||||
(assoc % :touched
|
||||
(clojure.set/union (:touched shape)
|
||||
(ctk/normal-touched-groups ref-shape)))))
|
||||
|
||||
;; Swap slot must also be copied if the current shape has not any,
|
||||
;; except if this is the first level subcopy.
|
||||
(and (some? (ctk/get-swap-slot ref-shape))
|
||||
(nil? (ctk/get-swap-slot shape))
|
||||
(not= (:id shape) shape-id))
|
||||
(pcb/update-shapes [(:id shape)] #(ctk/set-swap-slot % (ctk/get-swap-slot ref-shape)))
|
||||
;; Swap slot must also be copied if the current shape has not any,
|
||||
;; except if this is the first level subcopy.
|
||||
(and (some? (ctk/get-swap-slot ref-shape))
|
||||
(nil? (ctk/get-swap-slot shape))
|
||||
(not= (:id shape) shape-id))
|
||||
(pcb/update-shapes [(:id shape)] #(do (log/trace :msg " (got swap-slot)")
|
||||
(ctk/set-swap-slot % (ctk/get-swap-slot ref-shape))))
|
||||
|
||||
;; If we can't get the ref-shape (e.g. it's in an external library not linked),
|
||||
;: we can't do a suitable advance. So it's better to detach the shape
|
||||
(nil? ref-shape)
|
||||
(pcb/update-shapes [(:id shape)] ctk/detach-shape))))]
|
||||
;; If we can't get the ref-shape (e.g. it's in an external library not linked),
|
||||
;: we can't do a suitable advance. So it's better to detach the shape and all its
|
||||
;; children (and add to detached-ids so they are not processed again).
|
||||
(nil? ref-shape)
|
||||
(generate-detach-immediate container (:id shape) detached-ids)))))]
|
||||
|
||||
(reduce skip-near changes children)))
|
||||
|
||||
(defn- generate-detach-immediate
|
||||
[changes container shape-id detached-ids]
|
||||
(let [shape-and-children (cfh/get-children-ids-with-self (:objects container) shape-id)]
|
||||
(log/trace :msg " (cannot advance; detach shape and children)")
|
||||
(swap! detached-ids #(into % shape-and-children))
|
||||
(pcb/update-shapes changes shape-and-children ctk/detach-shape)))
|
||||
|
||||
(defn prepare-restore-component
|
||||
([changes library-data component-id current-page]
|
||||
(let [component (ctkl/get-deleted-component library-data component-id)
|
||||
|
||||
@@ -390,14 +390,22 @@
|
||||
(register! :merge (mu/-merge))
|
||||
(register! :union (mu/-union))
|
||||
|
||||
(def uuid-rx
|
||||
#"^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$")
|
||||
|
||||
(defn parse-uuid
|
||||
(defn- parse-uuid
|
||||
[s]
|
||||
(if (string? s)
|
||||
(some->> (re-matches uuid-rx s) uuid/uuid)
|
||||
s))
|
||||
(if (uuid? s)
|
||||
s
|
||||
(if (str/empty? s)
|
||||
nil
|
||||
(try
|
||||
(uuid/parse s)
|
||||
(catch #?(:clj Exception :cljs :default) _cause
|
||||
s)))))
|
||||
|
||||
(defn- encode-uuid
|
||||
[v]
|
||||
(if (uuid? v)
|
||||
(str v)
|
||||
v))
|
||||
|
||||
(register!
|
||||
{:type ::uuid
|
||||
@@ -409,8 +417,8 @@
|
||||
:gen/gen (sg/uuid)
|
||||
:decode/string parse-uuid
|
||||
:decode/json parse-uuid
|
||||
:encode/string str
|
||||
:encode/json str
|
||||
:encode/string encode-uuid
|
||||
:encode/json encode-uuid
|
||||
::oapi/type "string"
|
||||
::oapi/format "uuid"}})
|
||||
|
||||
@@ -856,7 +864,7 @@
|
||||
choices))]
|
||||
{:pred pred
|
||||
:type-properties
|
||||
{:title "contains"
|
||||
{:title "contains any"
|
||||
:description "contains predicate"}}))})
|
||||
|
||||
(register!
|
||||
|
||||
@@ -60,6 +60,7 @@
|
||||
(let [smallest (-> params :shrunk :smallest vec)]
|
||||
(println)
|
||||
(println "Condition failed with the following params:")
|
||||
(println "Seed:" (:seed params))
|
||||
(println)
|
||||
(pp/pprint smallest)))
|
||||
|
||||
|
||||
@@ -543,14 +543,23 @@
|
||||
;; We can always move the children to the parent they already have.
|
||||
;; But if we are pasting, those are new items, so it is considered a change
|
||||
no-changes?
|
||||
(and (->> children (every? #(= parent-id (:parent-id %))))
|
||||
(and (every? #(= parent-id (:parent-id %)) children)
|
||||
(not pasting?))
|
||||
all-main?
|
||||
(->> children (every? #(ctk/main-instance? %)))]
|
||||
(every? ctk/main-instance? children)
|
||||
|
||||
any-main-descendant
|
||||
(some
|
||||
(fn [shape]
|
||||
(some ctk/main-instance? (cfh/get-children-with-self objects (:id shape))))
|
||||
children)]
|
||||
|
||||
(if (or no-changes?
|
||||
(and (not (invalid-structure-for-component? objects parent children pasting? libraries))
|
||||
;; If we are moving into a variant-container, all the items should be main
|
||||
(or all-main? (not (ctk/is-variant-container? parent)))))
|
||||
(or all-main? (not (ctk/is-variant-container? parent)))
|
||||
;; If we are moving into a main component, no descendant can be main
|
||||
(or (nil? any-main-descendant) (not (ctk/main-instance? parent)))))
|
||||
[parent-id (get-frame parent-id)]
|
||||
(recur (:parent-id parent) objects children pasting? libraries))))))
|
||||
|
||||
|
||||
@@ -211,7 +211,7 @@
|
||||
[:interactions {:optional true}
|
||||
[:vector {:gen/max 2} ::ctsi/interaction]]
|
||||
[:shadow {:optional true}
|
||||
[:vector {:gen/max 1} ::ctss/shadow]]
|
||||
[:vector {:gen/max 1} ctss/schema:shadow]]
|
||||
[:blur {:optional true} ::ctsb/blur]
|
||||
[:grow-type {:optional true}
|
||||
[::sm/one-of grow-types]]
|
||||
|
||||
@@ -109,13 +109,27 @@
|
||||
(def check-animation!
|
||||
(sm/check-fn schema:animation))
|
||||
|
||||
(def schema:interaction-attrs
|
||||
[:map {:title "InteractionAttrs"}
|
||||
[:action-type {:optional true} [::sm/one-of action-types]]
|
||||
[:event-type {:optional true} [::sm/one-of event-types]]
|
||||
[:destination {:optional true} [:maybe ::sm/uuid]]
|
||||
[:preserve-scroll {:optional true} :boolean]
|
||||
[:animation {:optional true} schema:animation]
|
||||
[:overlay-position {:optional true} ::gpt/point]
|
||||
[:overlay-pos-type {:optional true} [::sm/one-of overlay-positioning-types]]
|
||||
[:close-click-outside {:optional true} :boolean]
|
||||
[:background-overlay {:optional true} :boolean]
|
||||
[:position-relative-to {:optional true} [:maybe ::sm/uuid]]
|
||||
[:url {:optional true} :string]])
|
||||
|
||||
(def schema:navigate-interaction
|
||||
[:map
|
||||
[:action-type [:= :navigate]]
|
||||
[:event-type [::sm/one-of event-types]]
|
||||
[:destination {:optional true} [:maybe ::sm/uuid]]
|
||||
[:preserve-scroll {:optional true} :boolean]
|
||||
[:animation {:optional true} ::animation]])
|
||||
[:animation {:optional true} schema:animation]])
|
||||
|
||||
(def schema:open-overlay-interaction
|
||||
[:map
|
||||
@@ -126,7 +140,7 @@
|
||||
[:destination {:optional true} [:maybe ::sm/uuid]]
|
||||
[:close-click-outside {:optional true} :boolean]
|
||||
[:background-overlay {:optional true} :boolean]
|
||||
[:animation {:optional true} ::animation]
|
||||
[:animation {:optional true} schema:animation]
|
||||
[:position-relative-to {:optional true} [:maybe ::sm/uuid]]])
|
||||
|
||||
(def schema:toggle-overlay-interaction
|
||||
@@ -138,7 +152,7 @@
|
||||
[:destination {:optional true} [:maybe ::sm/uuid]]
|
||||
[:close-click-outside {:optional true} :boolean]
|
||||
[:background-overlay {:optional true} :boolean]
|
||||
[:animation {:optional true} ::animation]
|
||||
[:animation {:optional true} schema:animation]
|
||||
[:position-relative-to {:optional true} [:maybe ::sm/uuid]]])
|
||||
|
||||
(def schema:close-overlay-interaction
|
||||
@@ -146,7 +160,7 @@
|
||||
[:action-type [:= :close-overlay]]
|
||||
[:event-type [::sm/one-of event-types]]
|
||||
[:destination {:optional true} [:maybe ::sm/uuid]]
|
||||
[:animation {:optional true} ::animation]
|
||||
[:animation {:optional true} schema:animation]
|
||||
[:position-relative-to {:optional true} [:maybe ::sm/uuid]]])
|
||||
|
||||
(def schema:prev-scren-interaction
|
||||
@@ -161,21 +175,21 @@
|
||||
[:url :string]])
|
||||
|
||||
(def schema:interaction
|
||||
[:multi {:dispatch :action-type
|
||||
:title "Interaction"
|
||||
:gen/gen (sg/one-of (sg/generator schema:navigate-interaction)
|
||||
(sg/generator schema:open-overlay-interaction)
|
||||
(sg/generator schema:close-overlay-interaction)
|
||||
(sg/generator schema:toggle-overlay-interaction)
|
||||
(sg/generator schema:prev-scren-interaction)
|
||||
(sg/generator schema:open-url-interaction))
|
||||
:decode/json #(update % :action-type keyword)}
|
||||
[:navigate schema:navigate-interaction]
|
||||
[:open-overlay schema:open-overlay-interaction]
|
||||
[:toggle-overlay schema:toggle-overlay-interaction]
|
||||
[:close-overlay schema:close-overlay-interaction]
|
||||
[:prev-screen schema:prev-scren-interaction]
|
||||
[:open-url schema:open-url-interaction]])
|
||||
[:and {:title "Interaction"
|
||||
:gen/gen (sg/one-of (sg/generator schema:navigate-interaction)
|
||||
(sg/generator schema:open-overlay-interaction)
|
||||
(sg/generator schema:close-overlay-interaction)
|
||||
(sg/generator schema:toggle-overlay-interaction)
|
||||
(sg/generator schema:prev-scren-interaction)
|
||||
(sg/generator schema:open-url-interaction))}
|
||||
schema:interaction-attrs
|
||||
[:multi {:dispatch :action-type}
|
||||
[:navigate schema:navigate-interaction]
|
||||
[:open-overlay schema:open-overlay-interaction]
|
||||
[:toggle-overlay schema:toggle-overlay-interaction]
|
||||
[:close-overlay schema:close-overlay-interaction]
|
||||
[:prev-screen schema:prev-scren-interaction]
|
||||
[:open-url schema:open-url-interaction]]])
|
||||
|
||||
(sm/register! ::interaction schema:interaction)
|
||||
|
||||
|
||||
@@ -26,7 +26,9 @@
|
||||
[:hidden :boolean]
|
||||
[:color ::ctc/color]])
|
||||
|
||||
(sm/register! ::shadow schema:shadow)
|
||||
|
||||
(def check-shadow
|
||||
(sm/check-fn schema:shadow))
|
||||
|
||||
(def valid-shadow?
|
||||
(sm/validator schema:shadow))
|
||||
|
||||
|
||||
@@ -17,9 +17,14 @@
|
||||
java.util.UUID
|
||||
java.nio.ByteBuffer)))
|
||||
|
||||
(def regex
|
||||
#"^[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]-[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]-[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]-[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]-[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]$")
|
||||
|
||||
(defn uuid
|
||||
"Creates an UUID instance from string, expectes valid uuid strings,
|
||||
the existense of validation is implementation detail"
|
||||
the existense of validation is implementation detail.
|
||||
|
||||
UNSAFE: this can accept invalid uuids or incomplete uuids"
|
||||
[s]
|
||||
#?(:clj (UUID/fromString s)
|
||||
:cljs (c/uuid s)))
|
||||
@@ -27,8 +32,21 @@
|
||||
(defn parse
|
||||
"Parse string uuid representation into proper UUID instance, validates input"
|
||||
[s]
|
||||
#?(:clj (UUID/fromString s)
|
||||
:cljs (c/parse-uuid s)))
|
||||
(if (and (string? s) ^boolean (re-matches regex s))
|
||||
#?(:clj (UUID/fromString s)
|
||||
:cljs (uuid s))
|
||||
|
||||
(let [message (str "invalid string '" s "' for uuid")]
|
||||
(throw #?(:clj (IllegalArgumentException. message)
|
||||
:cljs (js/Error. message))))))
|
||||
|
||||
(defn parse*
|
||||
"Exception safe version of `parse`."
|
||||
[s]
|
||||
(try
|
||||
(parse s)
|
||||
(catch #?(:clj Exception :cljs :default) _cause
|
||||
nil)))
|
||||
|
||||
(defn next
|
||||
[]
|
||||
|
||||
@@ -156,10 +156,13 @@ http {
|
||||
}
|
||||
|
||||
location / {
|
||||
location ~ ^/github/penpot-files/(?<template_file>[a-zA-Z0-9\-\_\.]+) {
|
||||
proxy_pass https://raw.githubusercontent.com/penpot/penpot-files/main/$template_file;
|
||||
location ~ ^/github/penpot-files/(.+)$ {
|
||||
rewrite ^/github/penpot-files/(.+) /penpot/penpot-files/refs/heads/main/$1 break;
|
||||
proxy_pass https://raw.githubusercontent.com;
|
||||
|
||||
proxy_hide_header Access-Control-Allow-Origin;
|
||||
proxy_set_header User-Agent "curl/7.74.0";
|
||||
proxy_hide_header Cookies;
|
||||
proxy_set_header User-Agent "curl/8.5.0";
|
||||
proxy_set_header Host "raw.githubusercontent.com";
|
||||
proxy_set_header Accept "*/*";
|
||||
add_header Access-Control-Allow-Origin $http_origin;
|
||||
|
||||
@@ -135,10 +135,13 @@ http {
|
||||
}
|
||||
|
||||
location / {
|
||||
location ~ ^/github/penpot-files/(?<template_file>[a-zA-Z0-9\-\_\.]+) {
|
||||
proxy_pass https://raw.githubusercontent.com/penpot/penpot-files/main/$template_file;
|
||||
location ~ ^/github/penpot-files/(.+)$ {
|
||||
rewrite ^/github/penpot-files/(.+) /penpot/penpot-files/refs/heads/main/$1 break;
|
||||
proxy_pass https://raw.githubusercontent.com;
|
||||
|
||||
proxy_hide_header Access-Control-Allow-Origin;
|
||||
proxy_set_header User-Agent "curl/7.74.0";
|
||||
proxy_hide_header Cookies;
|
||||
proxy_set_header User-Agent "curl/8.5.0";
|
||||
proxy_set_header Host "raw.githubusercontent.com";
|
||||
proxy_set_header Accept "*/*";
|
||||
add_header Access-Control-Allow-Origin $http_origin;
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
---
|
||||
title: 1.1 Recommended Settings
|
||||
title: 1.1 Recommended storage
|
||||
---
|
||||
|
||||
# Recommended settings
|
||||
# Recommended storage
|
||||
|
||||
To self-host Penpot, you’ll need a server with the following specifications:
|
||||
Disk requirements depend on your usage, with the primary factors being database storage and user-uploaded files.
|
||||
|
||||
* **CPU:** 1-2 CPUs
|
||||
* **RAM:** 4 GiB of RAM
|
||||
* **Disk Space:** Disk requirements depend on your usage. Disk usage primarily involves the database and any files uploaded by users.
|
||||
As a rule of thumb, start with a **minimum** database size of **50GB** to **100GB** with elastic sizing capability — this configuration should adequately support up to 10 editors. For environments with **more than 10 users**, we recommend adding approximately **5GB** of capacity per additional editor.
|
||||
|
||||
This setup should be sufficient for a smooth experience with typical usage (your mileage may vary).
|
||||
Keep in mind that database size doesn't grow strictly proportionally with user count, as it depends heavily on how Penpot is used and the complexity of files created. Most organizations begin with this baseline and elastic sizing approach, then monitor usage patterns monthly until resource requirements stabilize.
|
||||
|
||||
@@ -511,9 +511,7 @@ test.describe("Tokens: Sets Tab", () => {
|
||||
// Creates nesting by renaming set with double click
|
||||
await tokenThemesSetsSidebar
|
||||
.getByRole("button", { name: "light-renamed" })
|
||||
.click({ button: "right" });
|
||||
await expect(tokenContextMenuForSet).toBeVisible();
|
||||
await tokenContextMenuForSet.getByText("Rename").click();
|
||||
.dblclick();
|
||||
await changeSetInput(tokenThemesSetsSidebar, "nested/light");
|
||||
|
||||
await assertSetsList(tokenThemesSetsSidebar, [
|
||||
@@ -558,6 +556,45 @@ test.describe("Tokens: Sets Tab", () => {
|
||||
]);
|
||||
});
|
||||
|
||||
test("User can create & edit sets and set groups with an identical name", async ({
|
||||
page,
|
||||
}) => {
|
||||
const { tokenThemesSetsSidebar, tokenContextMenuForSet } =
|
||||
await setupEmptyTokensFile(page);
|
||||
|
||||
const tokensTabButton = tokenThemesSetsSidebar
|
||||
.getByRole("button", { name: "Add set" })
|
||||
.click();
|
||||
|
||||
await createSet(tokenThemesSetsSidebar, "core/colors");
|
||||
await createSet(tokenThemesSetsSidebar, "core");
|
||||
await assertSetsList(tokenThemesSetsSidebar, ["core", "colors", "core"]);
|
||||
await tokenThemesSetsSidebar
|
||||
.getByRole("button", { name: "core" })
|
||||
.nth(0)
|
||||
.dblclick();
|
||||
await changeSetInput(tokenThemesSetsSidebar, "core-group-renamed");
|
||||
await assertSetsList(tokenThemesSetsSidebar, [
|
||||
"core-group-renamed",
|
||||
"colors",
|
||||
"core",
|
||||
]);
|
||||
|
||||
await page.keyboard.press(`ControlOrMeta+z`);
|
||||
await assertSetsList(tokenThemesSetsSidebar, ["core", "colors", "core"]);
|
||||
|
||||
await tokenThemesSetsSidebar
|
||||
.getByRole("button", { name: "core" })
|
||||
.nth(1)
|
||||
.dblclick();
|
||||
await changeSetInput(tokenThemesSetsSidebar, "core-set-renamed");
|
||||
await assertSetsList(tokenThemesSetsSidebar, [
|
||||
"core",
|
||||
"colors",
|
||||
"core-set-renamed",
|
||||
]);
|
||||
});
|
||||
|
||||
test("Fold/Unfold set", async ({ page }) => {
|
||||
const { tokenThemesSetsSidebar, tokenSetGroupItems } =
|
||||
await setupTokensFile(page);
|
||||
|
||||
@@ -239,15 +239,15 @@
|
||||
(str (:last-id file)))
|
||||
|
||||
(lookupShape [_ shape-id]
|
||||
(clj->js (fb/lookup-shape file (uuid/uuid shape-id))))
|
||||
(clj->js (fb/lookup-shape file (uuid/parse shape-id))))
|
||||
|
||||
(updateObject [_ id new-obj]
|
||||
(let [old-obj (fb/lookup-shape file (uuid/uuid id))
|
||||
(let [old-obj (fb/lookup-shape file (uuid/parse id))
|
||||
new-obj (d/deep-merge old-obj (parse-data new-obj))]
|
||||
(set! file (fb/update-object file old-obj new-obj))))
|
||||
|
||||
(deleteObject [_ id]
|
||||
(set! file (fb/delete-object file (uuid/uuid id))))
|
||||
(set! file (fb/delete-object file (uuid/parse id))))
|
||||
|
||||
(getId [_]
|
||||
(:id file))
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
[file ^string page-id]
|
||||
|
||||
;; Better to expose the api as a promise to be consumed from JS
|
||||
(let [page-id (uuid/uuid page-id)
|
||||
(let [page-id (uuid/parse page-id)
|
||||
file-data (.-file file)
|
||||
data (get-in file-data [:data :pages-index page-id])]
|
||||
(p/create
|
||||
|
||||
@@ -69,7 +69,7 @@
|
||||
"Retrieves the mentions in the content as an array of uuids"
|
||||
[content]
|
||||
(->> (re-seq r-mentions content)
|
||||
(mapv (fn [[_ _ id]] (uuid/uuid id)))))
|
||||
(mapv (fn [[_ _ id]] (uuid/parse id)))))
|
||||
|
||||
(defn update-mentions
|
||||
"Updates the params object with the mentiosn"
|
||||
|
||||
@@ -248,7 +248,7 @@
|
||||
|
||||
(defn fetch-comments
|
||||
[{:keys [thread-id]}]
|
||||
(dm/assert! (uuid thread-id))
|
||||
(assert (uuid? thread-id))
|
||||
(letfn [(fetched [comments state]
|
||||
(update state :comments assoc thread-id (d/index-by :id comments)))]
|
||||
(ptk/reify ::retrieve-comments
|
||||
@@ -413,7 +413,7 @@
|
||||
(watch [_ state _]
|
||||
(let [params (rt/get-params state)
|
||||
index (some-> params :index parse-long)
|
||||
page-id (some-> params :page-id parse-uuid)
|
||||
page-id (some-> params :page-id uuid/parse)
|
||||
|
||||
total (count (get-in state [:viewer :pages page-id :frames]))]
|
||||
|
||||
|
||||
@@ -369,7 +369,7 @@
|
||||
(rx/take 1)
|
||||
(rx/map dwc/set-workspace-visited))
|
||||
|
||||
(when-let [component-id (some-> rparams :component-id parse-uuid)]
|
||||
(when-let [component-id (some-> rparams :component-id uuid/parse)]
|
||||
(->> stream
|
||||
(rx/filter (ptk/type? ::workspace-initialized))
|
||||
(rx/observe-on :async)
|
||||
@@ -382,7 +382,7 @@
|
||||
(rx/take 1)
|
||||
(rx/map zoom-to-frame)))
|
||||
|
||||
(when-let [comment-id (some-> rparams :comment-id parse-uuid)]
|
||||
(when-let [comment-id (some-> rparams :comment-id uuid/parse)]
|
||||
(->> stream
|
||||
(rx/filter (ptk/type? ::workspace-initialized))
|
||||
(rx/observe-on :async)
|
||||
@@ -2445,13 +2445,6 @@
|
||||
(js/console.log "Copies no ref" (count copies-no-ref) (clj->js copies-no-ref))
|
||||
(js/console.log "Childs no ref" (count childs-no-ref) (clj->js childs-no-ref))))))
|
||||
|
||||
(defn set-shape-ref
|
||||
[id shape-ref]
|
||||
(ptk/reify ::set-shape-ref
|
||||
ptk/WatchEvent
|
||||
(watch [_ _ _]
|
||||
(rx/of (update-shape (uuid/uuid id) {:shape-ref (uuid/uuid shape-ref)})))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Exports
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
@@ -586,8 +586,13 @@
|
||||
ldata (dsh/lookup-file-data state library-id)
|
||||
|
||||
changes (-> (pcb/empty-changes it)
|
||||
(cll/generate-restore-component ldata component-id library-id page objects))]
|
||||
(rx/of (dch/commit-changes changes))))))
|
||||
(cll/generate-restore-component ldata component-id library-id page objects))
|
||||
|
||||
frames
|
||||
(->> changes :redo-changes (keep :frame-id))]
|
||||
|
||||
(rx/of (dch/commit-changes changes)
|
||||
(ptk/data-event :layout/update {:ids frames}))))))
|
||||
|
||||
|
||||
(defn restore-components
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
(ns app.main.ui
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.config :as cf]
|
||||
[app.main.data.common :as dcm]
|
||||
[app.main.data.team :as dtm]
|
||||
@@ -69,7 +70,7 @@
|
||||
(mf/defc dashboard-legacy-redirect*
|
||||
{::mf/props :obj
|
||||
::mf/private true}
|
||||
[{:keys [section team-id project-id search-term plugin-url template-url]}]
|
||||
[{:keys [section team-id project-id search-term plugin-url template]}]
|
||||
(let [section (case section
|
||||
:dashboard-legacy-search
|
||||
:dashboard-search
|
||||
@@ -97,7 +98,7 @@
|
||||
:project-id project-id
|
||||
:search-term search-term
|
||||
:plugin plugin-url
|
||||
:template-url template-url}]
|
||||
:template template}]
|
||||
(st/emit! (rt/nav section (d/without-nils params)))))
|
||||
|
||||
[:> loader*
|
||||
@@ -212,11 +213,11 @@
|
||||
:dashboard-webhooks
|
||||
:dashboard-settings)
|
||||
(let [params (get params :query)
|
||||
team-id (some-> params :team-id uuid)
|
||||
project-id (some-> params :project-id uuid)
|
||||
team-id (some-> params :team-id uuid/parse*)
|
||||
project-id (some-> params :project-id uuid/parse*)
|
||||
search-term (some-> params :search-term)
|
||||
plugin-url (some-> params :plugin)
|
||||
template-url (some-> params :template)]
|
||||
template (some-> params :template)]
|
||||
[:?
|
||||
#_[:& app.main.ui.releases/release-notes-modal {:version "2.5"}]
|
||||
#_[:& app.main.ui.onboarding/onboarding-templates-modal]
|
||||
@@ -243,13 +244,13 @@
|
||||
:search-term search-term
|
||||
:plugin-url plugin-url
|
||||
:project-id project-id
|
||||
:template-url template-url}]]])
|
||||
:template template}]]])
|
||||
|
||||
:workspace
|
||||
(let [params (get params :query)
|
||||
team-id (some-> params :team-id uuid)
|
||||
file-id (some-> params :file-id uuid)
|
||||
page-id (some-> params :page-id uuid)
|
||||
team-id (some-> params :team-id uuid/parse*)
|
||||
file-id (some-> params :file-id uuid/parse*)
|
||||
page-id (some-> params :page-id uuid/parse*)
|
||||
layout (some-> params :layout keyword)]
|
||||
[:? {}
|
||||
(when (cf/external-feature-flag "onboarding-03" "test")
|
||||
@@ -276,15 +277,15 @@
|
||||
:viewer
|
||||
(let [params (get params :query)
|
||||
index (some-> (:index params) parse-long)
|
||||
share-id (some-> (:share-id params) parse-uuid)
|
||||
share-id (some-> (:share-id params) uuid/parse*)
|
||||
section (or (some-> (:section params) keyword)
|
||||
:interactions)
|
||||
|
||||
file-id (some-> (:file-id params) parse-uuid)
|
||||
page-id (some-> (:page-id params) parse-uuid)
|
||||
file-id (some-> (:file-id params) uuid/parse*)
|
||||
page-id (some-> (:page-id params) uuid/parse*)
|
||||
imode (or (some-> (:interactions-mode params) keyword)
|
||||
:show-on-click)
|
||||
frame-id (some-> (:frame-id params) parse-uuid)
|
||||
frame-id (some-> (:frame-id params) uuid/parse*)
|
||||
share (:share params)]
|
||||
|
||||
[:? {}
|
||||
@@ -300,9 +301,9 @@
|
||||
|
||||
|
||||
:workspace-legacy
|
||||
(let [project-id (some-> params :path :project-id uuid)
|
||||
file-id (some-> params :path :file-id uuid)
|
||||
page-id (some-> params :query :page-id uuid)
|
||||
(let [project-id (some-> params :path :project-id uuid/parse*)
|
||||
file-id (some-> params :path :file-id uuid/parse*)
|
||||
page-id (some-> params :query :page-id uuid/parse*)
|
||||
layout (some-> params :query :layout keyword)]
|
||||
|
||||
[:> workspace-legacy-redirect*
|
||||
@@ -321,18 +322,18 @@
|
||||
:dashboard-legacy-team-invitations
|
||||
:dashboard-legacy-team-webhooks
|
||||
:dashboard-legacy-team-settings)
|
||||
(let [team-id (some-> params :path :team-id uuid)
|
||||
project-id (some-> params :path :project-id uuid)
|
||||
(let [team-id (some-> params :path :team-id uuid/parse*)
|
||||
project-id (some-> params :path :project-id uuid/parse*)
|
||||
search-term (some-> params :query :search-term)
|
||||
plugin-url (some-> params :query :plugin)
|
||||
template-url (some-> params :template)]
|
||||
template (some-> params :template)]
|
||||
[:> dashboard-legacy-redirect*
|
||||
{:team-id team-id
|
||||
:section section
|
||||
:project-id project-id
|
||||
:search-term search-term
|
||||
:plugin-url plugin-url
|
||||
:template-url template-url}])
|
||||
:template template}])
|
||||
|
||||
:viewer-legacy
|
||||
(let [{:keys [query-params path-params]} route
|
||||
|
||||
@@ -487,7 +487,7 @@
|
||||
(dom/stop-propagation event)
|
||||
(let [id (-> (dom/get-current-target event)
|
||||
(dom/get-data "user-id")
|
||||
(uuid/uuid))
|
||||
(uuid/parse))
|
||||
|
||||
user (d/seek #(= (:id %) id) members)]
|
||||
|
||||
|
||||
@@ -34,12 +34,10 @@
|
||||
[app.main.ui.workspace.plugins]
|
||||
[app.plugins.register :as preg]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.http :as http]
|
||||
[app.util.i18n :refer [tr]]
|
||||
[app.util.keyboard :as kbd]
|
||||
[app.util.object :as obj]
|
||||
[app.util.storage :as storage]
|
||||
[app.util.webapi :as wapi]
|
||||
[beicon.v2.core :as rx]
|
||||
[cuerdas.core :as str]
|
||||
[goog.events :as events]
|
||||
@@ -211,17 +209,22 @@
|
||||
(swap! storage/session dissoc :plugin-url))))))
|
||||
|
||||
(defn use-templates-import
|
||||
[can-edit? template-url project]
|
||||
[can-edit? template project]
|
||||
(let [project-id (get project :id)
|
||||
team-id (get project :team-id)]
|
||||
(mf/with-layout-effect [can-edit? template-url project-id team-id]
|
||||
(when (and (some? template-url)
|
||||
(mf/with-layout-effect [can-edit? template project-id team-id]
|
||||
(when (and (some? template)
|
||||
(some? project-id)
|
||||
(some? team-id))
|
||||
(if can-edit?
|
||||
(let [valid-url? (and (str/ends-with? template-url ".penpot")
|
||||
(str/starts-with? template-url cf/templates-uri))
|
||||
template-name (when valid-url? (subs template-url (count cf/templates-uri)))
|
||||
(let [valid-url? (str/ends-with? template ".penpot")
|
||||
|
||||
;; Backwards compatibility, ideally the template should be only the .penpot file name, not the full url
|
||||
template-name (if (str/starts-with? template "http")
|
||||
(subs template (count cf/templates-uri))
|
||||
template)
|
||||
|
||||
template-url (str "/github/penpot-files/" template-name)
|
||||
on-import #(st/emit! (dpj/fetch-files project-id)
|
||||
(dd/fetch-recent-files team-id)
|
||||
(dd/fetch-projects team-id)
|
||||
@@ -230,30 +233,22 @@
|
||||
:name template-name
|
||||
:url template-url}))]
|
||||
(if valid-url?
|
||||
(do
|
||||
(st/emit! (ptk/event ::ev/event {::ev/name "install-template-from-link" :name template-name :url template-url}))
|
||||
(->> (http/send! {:method :get
|
||||
:uri template-url
|
||||
:response-type :blob
|
||||
:omit-default-headers true})
|
||||
(rx/subs!
|
||||
(fn [result]
|
||||
(if (or (< (:status result) 200) (>= (:status result) 300))
|
||||
(st/emit! (notif/error (tr "dashboard.import.error")))
|
||||
(st/emit! (modal/show
|
||||
{:type :import
|
||||
:project-id project-id
|
||||
:entries [{:name template-name :uri (wapi/create-uri (:body result))}]
|
||||
:on-finish-import on-import})))))))
|
||||
(st/emit!
|
||||
(ptk/event ::ev/event {::ev/name "install-template-from-link" :name template-name :url template-url})
|
||||
(modal/show
|
||||
{:type :import
|
||||
:project-id project-id
|
||||
:entries [{:name template-name :uri template-url}]
|
||||
:on-finish-import on-import}))
|
||||
(st/emit! (notif/error (tr "dashboard.import.bad-url")))))
|
||||
(st/emit! (notif/error (tr "dashboard.import.no-perms"))))
|
||||
|
||||
(binding [storage/*sync* true]
|
||||
(swap! storage/session dissoc :template-url))))))
|
||||
(swap! storage/session dissoc :template))))))
|
||||
|
||||
(mf/defc dashboard*
|
||||
{::mf/props :obj}
|
||||
[{:keys [profile project-id team-id search-term plugin-url template-url section]}]
|
||||
[{:keys [profile project-id team-id search-term plugin-url template section]}]
|
||||
(let [team (mf/deref refs/team)
|
||||
projects (mf/deref refs/projects)
|
||||
|
||||
@@ -263,7 +258,7 @@
|
||||
(filterv #(= team-id (:team-id %)))))
|
||||
|
||||
can-edit? (dm/get-in team [:permissions :can-edit])
|
||||
template-url (or template-url (:template-url storage/session))
|
||||
template (or template (:template storage/session))
|
||||
plugin-url (or plugin-url (:plugin-url storage/session))
|
||||
|
||||
default-project
|
||||
@@ -289,7 +284,7 @@
|
||||
(events/unlistenByKey key))))
|
||||
|
||||
(use-plugin-register plugin-url team-id (:id default-project))
|
||||
(use-templates-import can-edit? template-url default-project)
|
||||
(use-templates-import can-edit? template default-project)
|
||||
|
||||
[:& (mf/provider ctx/current-project-id) {:value project-id}
|
||||
[:> modal-container*]
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
(:require
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.media :as cm]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.config :as cf]
|
||||
[app.main.data.fonts :as df]
|
||||
[app.main.data.modal :as modal]
|
||||
@@ -121,7 +122,7 @@
|
||||
(fn [event]
|
||||
(let [id (-> (dom/get-current-target event)
|
||||
(dom/get-data "id")
|
||||
(parse-uuid))
|
||||
(uuid/parse))
|
||||
item (get fonts id)]
|
||||
(on-upload* item))))
|
||||
|
||||
@@ -132,7 +133,7 @@
|
||||
(let [target (dom/get-current-target event)
|
||||
id (-> target
|
||||
(dom/get-data "id")
|
||||
(parse-uuid))
|
||||
(uuid/parse))
|
||||
name (dom/get-value target)]
|
||||
(when-not (str/blank? name)
|
||||
(swap! fonts* df/rename-and-regroup id name installed-fonts)))))
|
||||
@@ -143,7 +144,7 @@
|
||||
(let [target (dom/get-current-target event)
|
||||
id (-> target
|
||||
(dom/get-data "id")
|
||||
(parse-uuid))
|
||||
(uuid/parse))
|
||||
name (dom/get-value target)]
|
||||
(swap! fonts* update id assoc :font-family-tmp name))))
|
||||
|
||||
@@ -153,7 +154,7 @@
|
||||
(fn [event]
|
||||
(let [id (-> (dom/get-current-target event)
|
||||
(dom/get-data "id")
|
||||
(parse-uuid))]
|
||||
(uuid/parse))]
|
||||
(swap! fonts* dissoc id))))
|
||||
|
||||
on-upload-all
|
||||
@@ -344,7 +345,7 @@
|
||||
(fn [event]
|
||||
(let [id (-> (dom/get-current-target event)
|
||||
(dom/get-data "id")
|
||||
(parse-uuid))
|
||||
(uuid/parse))
|
||||
options {:type :confirm
|
||||
:title (tr "modals.delete-font-variant.title")
|
||||
:message (tr "modals.delete-font-variant.message")
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
[app.main.ui.dashboard.file-menu :refer [file-menu*]]
|
||||
[app.main.ui.dashboard.import :refer [use-import-file]]
|
||||
[app.main.ui.dashboard.inline-edition :refer [inline-edition]]
|
||||
[app.main.ui.dashboard.placeholder :refer [empty-placeholder loading-placeholder]]
|
||||
[app.main.ui.dashboard.placeholder :refer [empty-grid-placeholder* loading-placeholder*]]
|
||||
[app.main.ui.ds.product.loader :refer [loader*]]
|
||||
[app.main.ui.hooks :as h]
|
||||
[app.main.ui.icons :as i]
|
||||
@@ -89,14 +89,16 @@
|
||||
|
||||
(mf/with-effect [file-id revn visible? thumbnail-id]
|
||||
(when (and visible? (not thumbnail-id))
|
||||
(->> (ask-for-thumbnail file-id revn)
|
||||
(rx/subs! (fn [thumbnail-id]
|
||||
(st/emit! (dd/set-file-thumbnail file-id thumbnail-id)))
|
||||
(fn [cause]
|
||||
(log/error :hint "unable to render thumbnail"
|
||||
:file-if file-id
|
||||
:revn revn
|
||||
:message (ex-message cause)))))))
|
||||
(let [subscription
|
||||
(->> (ask-for-thumbnail file-id revn)
|
||||
(rx/subs! (fn [thumbnail-id]
|
||||
(st/emit! (dd/set-file-thumbnail file-id thumbnail-id)))
|
||||
(fn [cause]
|
||||
(log/error :hint "unable to render thumbnail"
|
||||
:file-if file-id
|
||||
:revn revn
|
||||
:message (ex-message cause)))))]
|
||||
(partial rx/dispose! subscription))))
|
||||
|
||||
[:div {:class (stl/css :grid-item-th)
|
||||
:style {:background-color bg-color}
|
||||
@@ -511,7 +513,7 @@
|
||||
:ref node-ref}
|
||||
(cond
|
||||
(nil? files)
|
||||
[:& loading-placeholder]
|
||||
[:> loading-placeholder*]
|
||||
|
||||
(seq files)
|
||||
(for [[index slice] (d/enumerate (partition-all limit files))]
|
||||
@@ -528,12 +530,13 @@
|
||||
:can-edit can-edit}])])
|
||||
|
||||
:else
|
||||
[:& empty-placeholder
|
||||
[:> empty-grid-placeholder*
|
||||
{:limit limit
|
||||
:can-edit can-edit
|
||||
:create-fn create-fn
|
||||
:origin origin
|
||||
:project-id project-id
|
||||
:team-id team-id
|
||||
:on-finish-import on-finish-import}])]))
|
||||
|
||||
(mf/defc line-grid-row
|
||||
@@ -645,7 +648,7 @@
|
||||
:on-drop on-drop}
|
||||
(cond
|
||||
(nil? files)
|
||||
[:& loading-placeholder]
|
||||
[:> loading-placeholder*]
|
||||
|
||||
(seq files)
|
||||
[:& line-grid-row {:files files
|
||||
@@ -656,10 +659,11 @@
|
||||
:limit limit}]
|
||||
|
||||
:else
|
||||
[:& empty-placeholder
|
||||
{:dragging? @dragging?
|
||||
[:> empty-grid-placeholder*
|
||||
{:is-dragging @dragging?
|
||||
:limit limit
|
||||
:can-edit can-edit
|
||||
:create-fn create-fn
|
||||
:project-id project-id
|
||||
:team-id team-id
|
||||
:on-finish-import on-finish-import}])]))
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
(:require-macros [app.main.style :as stl])
|
||||
(:require
|
||||
[app.main.data.event :as ev]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.dashboard.import :as udi]
|
||||
[app.main.ui.ds.product.empty-placeholder :refer [empty-placeholder*]]
|
||||
@@ -16,51 +15,92 @@
|
||||
[app.main.ui.icons :as i]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.i18n :as i18n :refer [tr]]
|
||||
[okulary.core :as l]
|
||||
[potok.v2.core :as ptk]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
(mf/defc empty-placeholder-projects*
|
||||
{::mf/wrap-props false}
|
||||
[{:keys [on-create on-finish-import project-id] :as props}]
|
||||
(mf/defc empty-project-placeholder*
|
||||
{::mf/private true}
|
||||
[{:keys [on-create on-finish-import project-id]}]
|
||||
(let [file-input (mf/use-ref nil)
|
||||
on-add-library (mf/use-fn
|
||||
(fn [_]
|
||||
(st/emit! (ptk/event ::ev/event {::ev/name "explore-libraries-click"
|
||||
::ev/origin "dashboard"
|
||||
:section "empty-placeholder-projects"}))
|
||||
(dom/open-new-window "https://penpot.app/penpothub/libraries-templates")))
|
||||
on-import-files (mf/use-fn #(dom/click (mf/ref-val file-input)))]
|
||||
|
||||
on-add-library
|
||||
(mf/use-fn
|
||||
(fn [_]
|
||||
(st/emit! (ptk/event ::ev/event {::ev/name "explore-libraries-click"
|
||||
::ev/origin "dashboard"
|
||||
:section "empty-placeholder-projects"}))
|
||||
(dom/open-new-window "https://penpot.app/penpothub/libraries-templates")))
|
||||
|
||||
on-import
|
||||
(mf/use-fn #(dom/click (mf/ref-val file-input)))]
|
||||
|
||||
[:div {:class (stl/css :empty-project-container)}
|
||||
[:div {:class (stl/css :empty-project-card) :on-click on-create :title (tr "dashboard.add-file")}
|
||||
[:div {:class (stl/css :empty-project-card-title)} (tr "dashboard.empty-project.create")]
|
||||
[:div {:class (stl/css :empty-project-card-subtitle)} (tr "dashboard.empty-project.start")]]
|
||||
[:div {:class (stl/css :empty-project-card)
|
||||
:on-click on-create
|
||||
:title (tr "dashboard.add-file")}
|
||||
[:div {:class (stl/css :empty-project-card-title)}
|
||||
(tr "dashboard.empty-project.create")]
|
||||
[:div {:class (stl/css :empty-project-card-subtitle)}
|
||||
(tr "dashboard.empty-project.start")]]
|
||||
|
||||
[:div {:class (stl/css :empty-project-card) :on-click on-import-files :title (tr "dashboard.empty-project.import")}
|
||||
[:div {:class (stl/css :empty-project-card-title)} (tr "dashboard.empty-project.import")]
|
||||
[:div {:class (stl/css :empty-project-card-subtitle)} (tr "dashboard.empty-project.import-penpot")]]
|
||||
[:div {:class (stl/css :empty-project-card)
|
||||
:on-click on-import
|
||||
:title (tr "dashboard.empty-project.import")}
|
||||
[:div {:class (stl/css :empty-project-card-title)}
|
||||
(tr "dashboard.empty-project.import")]
|
||||
[:div {:class (stl/css :empty-project-card-subtitle)}
|
||||
(tr "dashboard.empty-project.import-penpot")]]
|
||||
|
||||
[:div {:class (stl/css :empty-project-card) :on-click on-add-library :title (tr "dashboard.empty-project.go-to-libraries")}
|
||||
[:div {:class (stl/css :empty-project-card-title)} (tr "dashboard.empty-project.add-library")]
|
||||
[:div {:class (stl/css :empty-project-card-subtitle)} (tr "dashboard.empty-project.explore")]]
|
||||
[:div {:class (stl/css :empty-project-card)
|
||||
:on-click on-add-library
|
||||
:title (tr "dashboard.empty-project.go-to-libraries")}
|
||||
[:div {:class (stl/css :empty-project-card-title)}
|
||||
(tr "dashboard.empty-project.add-library")]
|
||||
[:div {:class (stl/css :empty-project-card-subtitle)}
|
||||
(tr "dashboard.empty-project.explore")]]
|
||||
|
||||
[:& udi/import-form {:ref file-input
|
||||
:project-id project-id
|
||||
:on-finish-import on-finish-import}]]))
|
||||
|
||||
(mf/defc empty-placeholder
|
||||
[{:keys [dragging? limit origin create-fn can-edit project-id on-finish-import]}]
|
||||
(defn- make-has-other-files-or-projects-ref
|
||||
"Return a ref that resolves to true or false if there are at least some
|
||||
file or some project (a part of the default) exists; this determines
|
||||
if we need to show a complete placeholder or the small one."
|
||||
[team-id]
|
||||
(l/derived (fn [state]
|
||||
(or (let [projects (get state :projects)]
|
||||
(some (fn [[_ project]]
|
||||
(and (= (:team-id project) team-id)
|
||||
(not (:is-default project))))
|
||||
projects))
|
||||
(let [files (get state :files)]
|
||||
(some (fn [[_ file]]
|
||||
(= (:team-id file) team-id))
|
||||
files))))
|
||||
st/state))
|
||||
|
||||
(mf/defc empty-grid-placeholder*
|
||||
[{:keys [is-dragging limit origin create-fn can-edit team-id project-id on-finish-import]}]
|
||||
(let [on-click
|
||||
(mf/use-fn
|
||||
(mf/deps create-fn)
|
||||
(fn [_]
|
||||
(create-fn "dashboard:empty-folder-placeholder")))
|
||||
show-text (mf/use-state nil)
|
||||
on-mouse-enter (mf/use-fn #(reset! show-text true))
|
||||
on-mouse-leave (mf/use-fn #(reset! show-text nil))
|
||||
files (mf/deref refs/files)]
|
||||
|
||||
show-text* (mf/use-state nil)
|
||||
show-text? (deref show-text*)
|
||||
|
||||
on-mouse-enter (mf/use-fn #(reset! show-text* true))
|
||||
on-mouse-leave (mf/use-fn #(reset! show-text* nil))
|
||||
|
||||
has-other* (mf/with-memo [team-id]
|
||||
(make-has-other-files-or-projects-ref team-id))
|
||||
has-other? (mf/deref has-other*)]
|
||||
|
||||
(cond
|
||||
(true? dragging?)
|
||||
(true? is-dragging)
|
||||
[:ul
|
||||
{:class (stl/css :grid-row :no-wrap)
|
||||
:style {:grid-template-columns (str "repeat(" limit ", 1fr)")}}
|
||||
@@ -80,18 +120,24 @@
|
||||
:tag-name "span"}])]
|
||||
|
||||
:else
|
||||
(if (= (count files) 0)
|
||||
[:> empty-placeholder-projects* {:on-create on-click :on-finish-import on-finish-import :project-id project-id}]
|
||||
(if-not has-other?
|
||||
[:> empty-project-placeholder*
|
||||
{:on-create on-click
|
||||
:on-finish-import on-finish-import
|
||||
:project-id project-id}]
|
||||
[:div {:class (stl/css :grid-empty-placeholder)}
|
||||
[:button {:class (stl/css :create-new)
|
||||
:on-click on-click
|
||||
:on-mouse-enter on-mouse-enter
|
||||
:on-mouse-leave on-mouse-leave}
|
||||
(if @show-text (tr "dashboard.empty-project.create") i/add)]]))))
|
||||
(if show-text?
|
||||
(tr "dashboard.empty-project.create")
|
||||
i/add)]]))))
|
||||
|
||||
(mf/defc loading-placeholder
|
||||
(mf/defc loading-placeholder*
|
||||
[]
|
||||
[:> loader* {:width 32
|
||||
:title (tr "labels.loading")
|
||||
:class (stl/css :placeholder-loader)}
|
||||
[:span {:class (stl/css :placeholder-text)} (tr "dashboard.loading-files")]])
|
||||
[:span {:class (stl/css :placeholder-text)}
|
||||
(tr "dashboard.loading-files")]])
|
||||
|
||||
@@ -371,6 +371,7 @@
|
||||
show-team-hero?
|
||||
can-invite))}
|
||||
(for [{:keys [id] :as project} projects]
|
||||
;; FIXME: refactor this, looks inneficient
|
||||
(let [files (when recent-map
|
||||
(->> (vals recent-map)
|
||||
(filterv #(= id (:project-id %)))
|
||||
|
||||
@@ -284,7 +284,6 @@
|
||||
(let [team-id (-> (dom/get-current-target event)
|
||||
(dom/get-data "value")
|
||||
(uuid/parse))]
|
||||
|
||||
(st/emit! (dcm/go-to-dashboard-recent :team-id team-id)))))
|
||||
|
||||
handle-select-default
|
||||
@@ -963,13 +962,14 @@
|
||||
(dom/open-new-window "https://penpot.app/pricing")))]
|
||||
|
||||
[:*
|
||||
[:button {:class (stl/css :upgrade-plan-section)
|
||||
:on-click on-power-up-click}
|
||||
[:div {:class (stl/css :penpot-free)}
|
||||
[:span (tr "dashboard.upgrade-plan.penpot-free")]
|
||||
[:span {:class (stl/css :no-limits)} (tr "dashboard.upgrade-plan.no-limits")]]
|
||||
[:div {:class (stl/css :power-up)}
|
||||
(tr "dashboard.upgrade-plan.power-up")]]
|
||||
(when (contains? cf/flags :subscriptions-old)
|
||||
[:button {:class (stl/css :upgrade-plan-section)
|
||||
:on-click on-power-up-click}
|
||||
[:div {:class (stl/css :penpot-free)}
|
||||
[:span (tr "dashboard.upgrade-plan.penpot-free")]
|
||||
[:span {:class (stl/css :no-limits)} (tr "dashboard.upgrade-plan.no-limits")]]
|
||||
[:div {:class (stl/css :power-up)}
|
||||
(tr "dashboard.upgrade-plan.power-up")]])
|
||||
(when (and team profile)
|
||||
[:& comments-section
|
||||
{:profile profile
|
||||
|
||||
@@ -82,7 +82,7 @@
|
||||
(binding [storage/*sync* true]
|
||||
(when (some? template)
|
||||
(swap! storage/session assoc
|
||||
:template-url template))
|
||||
:template template))
|
||||
(when (some? plugin)
|
||||
(swap! storage/session assoc
|
||||
:plugin-url plugin))))
|
||||
|
||||
@@ -202,9 +202,8 @@
|
||||
cancel-text])
|
||||
[:button {:on-click on-click} button-text]]]]))
|
||||
|
||||
(mf/defc request-access
|
||||
{::mf/props :obj}
|
||||
[{:keys [file-id team-id is-default workspace?]}]
|
||||
(mf/defc request-access*
|
||||
[{:keys [file-id team-id is-default is-workspace]}]
|
||||
(let [profile (mf/deref refs/profile)
|
||||
requested* (mf/use-state {:sent false :already-requested false})
|
||||
requested (deref requested*)
|
||||
@@ -227,11 +226,11 @@
|
||||
|
||||
on-request-access
|
||||
(mf/use-fn
|
||||
(mf/deps file-id team-id workspace?)
|
||||
(mf/deps file-id team-id is-workspace)
|
||||
(fn []
|
||||
(let [params (if (some? file-id)
|
||||
{:file-id file-id
|
||||
:is-viewer (not workspace?)}
|
||||
:is-viewer (not is-workspace)}
|
||||
{:team-id team-id})
|
||||
mdata {:on-success on-success
|
||||
:on-error on-error}]
|
||||
@@ -240,7 +239,7 @@
|
||||
|
||||
[:*
|
||||
(if (some? file-id)
|
||||
(if workspace?
|
||||
(if is-workspace
|
||||
[:div {:class (stl/css :workspace)}
|
||||
[:div {:class (stl/css :workspace-left)}
|
||||
i/logo-icon
|
||||
@@ -341,7 +340,7 @@
|
||||
[:div {:class (stl/css :sign-info)}
|
||||
[:button {:on-click handle-retry} (tr "labels.retry")]]]))
|
||||
|
||||
(mf/defc service-unavailable
|
||||
(mf/defc service-unavailable*
|
||||
[]
|
||||
(let [on-click (mf/use-fn #(st/emit! (rt/assign-exception nil)))]
|
||||
[:> error-container* {}
|
||||
@@ -350,58 +349,55 @@
|
||||
[:div {:class (stl/css :sign-info)}
|
||||
[:button {:on-click on-click} (tr "labels.retry")]]]))
|
||||
|
||||
(defn generate-report
|
||||
(defn- generate-report
|
||||
[data]
|
||||
(try
|
||||
(let [team-id (:current-team-id @st/state)
|
||||
profile-id (:profile-id @st/state)
|
||||
|
||||
trace (:app.main.errors/trace data)
|
||||
instance (:app.main.errors/instance data)
|
||||
content (with-out-str
|
||||
(println "Hint: " (or (:hint data) (ex-message instance) "--"))
|
||||
(println "Prof ID:" (str (or profile-id "--")))
|
||||
(println "Team ID:" (str (or team-id "--")))
|
||||
instance (:app.main.errors/instance data)]
|
||||
(with-out-str
|
||||
(println "Hint: " (or (:hint data) (ex-message instance) "--"))
|
||||
(println "Prof ID:" (str (or profile-id "--")))
|
||||
(println "Team ID:" (str (or team-id "--")))
|
||||
|
||||
(when-let [file-id (:file-id data)]
|
||||
(println "File ID:" (str file-id)))
|
||||
(when-let [file-id (:file-id data)]
|
||||
(println "File ID:" (str file-id)))
|
||||
|
||||
(println)
|
||||
(println)
|
||||
|
||||
(println "Data:")
|
||||
(loop [data data]
|
||||
(-> (d/without-qualified data)
|
||||
(dissoc :explain)
|
||||
(d/update-when :data (constantly "(...)"))
|
||||
(pp/pprint {:level 8 :length 10}))
|
||||
(println "Data:")
|
||||
(loop [data data]
|
||||
(-> (d/without-qualified data)
|
||||
(dissoc :explain)
|
||||
(d/update-when :data (constantly "(...)"))
|
||||
(pp/pprint {:level 8 :length 10}))
|
||||
|
||||
(println)
|
||||
(println)
|
||||
|
||||
(when-let [explain (:explain data)]
|
||||
(print explain))
|
||||
(when-let [explain (:explain data)]
|
||||
(print explain))
|
||||
|
||||
(when (and (= :server-error (:type data))
|
||||
(contains? data :data))
|
||||
(recur (:data data))))
|
||||
(when (and (= :server-error (:type data))
|
||||
(contains? data :data))
|
||||
(recur (:data data))))
|
||||
|
||||
(println "Trace:")
|
||||
(println trace)
|
||||
(println)
|
||||
(println "Trace:")
|
||||
(println trace)
|
||||
(println)
|
||||
|
||||
(println "Last events:")
|
||||
(pp/pprint @st/last-events {:length 200})
|
||||
(println "Last events:")
|
||||
(pp/pprint @st/last-events {:length 200})
|
||||
|
||||
(println))]
|
||||
(wapi/create-blob content "text/plain"))
|
||||
(println)))
|
||||
(catch :default cause
|
||||
(.error js/console "error on generating report.txt" cause)
|
||||
nil)))
|
||||
|
||||
(mf/defc internal-error*
|
||||
{::mf/props :obj}
|
||||
[{:keys [data on-reset] :as props}]
|
||||
[{:keys [on-reset report] :as props}]
|
||||
(let [report-uri (mf/use-ref nil)
|
||||
report (mf/use-memo (mf/deps data) #(generate-report data))
|
||||
on-reset (or on-reset #(st/emit! (rt/assign-exception nil)))
|
||||
|
||||
on-download
|
||||
@@ -413,8 +409,8 @@
|
||||
|
||||
(mf/with-effect [report]
|
||||
(when (some? report)
|
||||
|
||||
(let [uri (wapi/create-uri report)]
|
||||
(let [report (wapi/create-blob report "text/plain")
|
||||
uri (wapi/create-uri report)]
|
||||
(mf/set-ref-val! report-uri uri)
|
||||
(fn []
|
||||
(wapi/revoke-uri uri)))))
|
||||
@@ -455,6 +451,38 @@
|
||||
(rx/of default)
|
||||
(rx/throw cause)))))))
|
||||
|
||||
(mf/defc exception-section*
|
||||
{::mf/private true}
|
||||
[{:keys [data route] :as props}]
|
||||
(let [type (get data :type)
|
||||
report (mf/with-memo [data]
|
||||
(generate-report data))
|
||||
props (mf/spread-props props {:report report})]
|
||||
|
||||
(mf/with-effect [data route report]
|
||||
(let [params (:query-params route)
|
||||
params (u/map->query-string params)]
|
||||
(st/emit! (ptk/data-event ::ev/event
|
||||
{::ev/name "exception-page"
|
||||
:type (get data :type :unknown)
|
||||
:hint (get data :hint)
|
||||
:path (get route :path)
|
||||
:report report
|
||||
:params params}))))
|
||||
(case type
|
||||
:not-found
|
||||
[:> not-found* {}]
|
||||
|
||||
:authentication
|
||||
[:> not-found* {}]
|
||||
|
||||
:bad-gateway
|
||||
[:> bad-gateway* props]
|
||||
|
||||
:service-unavailable
|
||||
[:> service-unavailable*]
|
||||
|
||||
[:> internal-error* props])))
|
||||
|
||||
(mf/defc exception-page*
|
||||
{::mf/props :obj}
|
||||
@@ -477,42 +505,23 @@
|
||||
|
||||
request-access?
|
||||
(and
|
||||
(or (= (:type data) :not-found)
|
||||
(= (:type data) :authentication))
|
||||
(or (= type :not-found)
|
||||
(= type :authentication))
|
||||
(or workspace? dashboard? view?)
|
||||
(or (:file-id info)
|
||||
(:team-id info)))]
|
||||
|
||||
(mf/with-effect [type path params]
|
||||
(st/emit! (ptk/data-event ::ev/event
|
||||
{::ev/name "exception-page"
|
||||
:type type
|
||||
:path path
|
||||
:params (u/map->query-string params)})))
|
||||
|
||||
(mf/with-effect [params info]
|
||||
(when-not (:loaded info)
|
||||
(->> (load-info params)
|
||||
(rx/subs! (partial reset! info*)))))
|
||||
(rx/subs! (partial reset! info*)
|
||||
(partial reset! info* {:loaded true})))))
|
||||
|
||||
(when loaded?
|
||||
(if request-access?
|
||||
[:& request-access {:file-id (:file-id info)
|
||||
:team-id (:team-id info)
|
||||
:is-default (:team-default info)
|
||||
:workspace? workspace?}]
|
||||
[:> request-access* {:file-id (:file-id info)
|
||||
:team-id (:team-id info)
|
||||
:is-default (:team-default info)
|
||||
:is-workspace workspace?}]
|
||||
[:> exception-section* props]))))
|
||||
|
||||
(case (:type data)
|
||||
:not-found
|
||||
[:> not-found* {}]
|
||||
|
||||
:authentication
|
||||
[:> not-found* {}]
|
||||
|
||||
:bad-gateway
|
||||
[:> bad-gateway* props]
|
||||
|
||||
:service-unavailable
|
||||
[:& service-unavailable]
|
||||
|
||||
[:> internal-error* props])))))
|
||||
|
||||
@@ -117,6 +117,7 @@
|
||||
padding-right: 0 $s-8 $s-40 $s-8;
|
||||
transition: transform 400ms ease 300ms;
|
||||
z-index: $z-index-2;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.reset-button {
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.logging :as log]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.config :as cf]
|
||||
[app.main.data.common :as dc]
|
||||
[app.main.data.event :as ev]
|
||||
@@ -104,7 +105,7 @@
|
||||
(fn [event]
|
||||
(let [target (dom/get-target event)
|
||||
checked? (dom/checked? target)
|
||||
page-id (parse-uuid (dom/get-data target "page-id"))
|
||||
page-id (uuid/parse (dom/get-data target "page-id"))
|
||||
dif-pages? (not= page-id (first (:pages options)))
|
||||
no-one-page (< 1 (count (:pages options)))
|
||||
should-change? (or ^boolean no-one-page
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.types.color :as ctc]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.main.data.event :as ev]
|
||||
[app.main.data.workspace :as dw]
|
||||
[app.main.data.workspace.colors :as mdc]
|
||||
@@ -62,7 +63,7 @@
|
||||
(if (or (= event "recent")
|
||||
(= event "file"))
|
||||
(keyword event)
|
||||
(parse-uuid event)))))
|
||||
(uuid/parse event)))))
|
||||
|
||||
valid-color?
|
||||
(mf/use-fn
|
||||
|
||||
@@ -228,7 +228,7 @@
|
||||
(fn [event]
|
||||
(let [library-id (some-> (dom/get-current-target event)
|
||||
(dom/get-data "library-id")
|
||||
(parse-uuid))]
|
||||
(uuid/parse))]
|
||||
(reset! selected library-id)
|
||||
(st/emit! (dwl/link-file-to-library file-id library-id)))))
|
||||
|
||||
@@ -238,7 +238,7 @@
|
||||
(fn [event]
|
||||
(let [library-id (some-> (dom/get-current-target event)
|
||||
(dom/get-data "library-id")
|
||||
(parse-uuid))]
|
||||
(uuid/parse))]
|
||||
(when (= library-id @selected)
|
||||
(reset! selected :file))
|
||||
(st/emit! (dwl/unlink-file-from-library file-id library-id)
|
||||
@@ -451,7 +451,7 @@
|
||||
(when-not updating?
|
||||
(let [library-id (some-> (dom/get-target event)
|
||||
(dom/get-data "library-id")
|
||||
(parse-uuid))]
|
||||
(uuid/parse))]
|
||||
(st/emit!
|
||||
(dwl/set-updating-library true)
|
||||
(dwl/sync-file file-id library-id))))))]
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.main.data.event :as ev]
|
||||
[app.main.data.workspace :as dw]
|
||||
[app.main.data.workspace.colors :as mdc]
|
||||
@@ -87,7 +88,7 @@
|
||||
value (dom/get-attribute node "data-palette")]
|
||||
(on-select (if (or (= "file" value) (= "recent" value))
|
||||
(keyword value)
|
||||
(parse-uuid value))))))
|
||||
(uuid/parse value))))))
|
||||
|
||||
on-select-text-palette-menu
|
||||
(mf/use-fn
|
||||
|
||||
@@ -251,14 +251,14 @@
|
||||
(mf/deps index update-interaction)
|
||||
(fn [event]
|
||||
(let [value event
|
||||
value (when (not= value "") (uuid/uuid value))]
|
||||
value (when (not= value "") (uuid/parse value))]
|
||||
(update-interaction index #(ctsi/set-destination % value)))))
|
||||
|
||||
change-position-relative-to
|
||||
(mf/use-fn
|
||||
(mf/deps index update-interaction)
|
||||
(fn [event]
|
||||
(let [value (uuid/uuid event)]
|
||||
(let [value (uuid/parse event)]
|
||||
(update-interaction index #(ctsi/set-position-relative-to % value)))))
|
||||
|
||||
change-preserve-scroll
|
||||
|
||||
@@ -55,6 +55,7 @@
|
||||
.custom-select-dropdown {
|
||||
@extend .dropdown-wrapper;
|
||||
margin-top: $s-2;
|
||||
max-height: 70vh;
|
||||
width: $s-252;
|
||||
.dropdown-element {
|
||||
@extend .dropdown-element-base;
|
||||
|
||||
@@ -153,7 +153,7 @@
|
||||
(mf/deps on-pin-snapshot)
|
||||
(fn [event]
|
||||
(let [node (dom/get-current-target event)
|
||||
id (-> (dom/get-data node "id") uuid/uuid)]
|
||||
id (-> (dom/get-data node "id") uuid/parse)]
|
||||
(when on-pin-snapshot (on-pin-snapshot id)))))
|
||||
|
||||
handle-restore-snapshot
|
||||
@@ -161,7 +161,7 @@
|
||||
(mf/deps on-restore-snapshot)
|
||||
(fn [event]
|
||||
(let [node (dom/get-current-target event)
|
||||
id (-> (dom/get-data node "id") uuid/uuid)]
|
||||
id (-> (dom/get-data node "id") uuid/parse)]
|
||||
(when on-restore-snapshot (on-restore-snapshot id)))))
|
||||
|
||||
|
||||
|
||||
@@ -190,6 +190,7 @@
|
||||
border: $s-1 solid color-mix(in hsl, var(--color-foreground-secondary) 30%, transparent);
|
||||
border-radius: $s-8;
|
||||
overflow-y: auto;
|
||||
max-height: $s-452;
|
||||
}
|
||||
|
||||
.sets-count-empty-button {
|
||||
|
||||
@@ -65,6 +65,11 @@
|
||||
(st/emit! (ptk/data-event ::ev/event {::ev/name "create-token-set" :name name})
|
||||
(dt/create-token-set name))))
|
||||
|
||||
(defn group-edition-id
|
||||
"Prefix editing groups `edition-id` so it can be differentiated from sets with the same id."
|
||||
[edition-id]
|
||||
(str "group-" edition-id))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; COMPONENTS
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
@@ -166,16 +171,18 @@
|
||||
{:position (dom/get-client-position event)
|
||||
:is-group true
|
||||
:id id
|
||||
:edition-id (group-edition-id id)
|
||||
:path tree-path})))))
|
||||
|
||||
on-collapse-click
|
||||
(mf/use-fn
|
||||
(fn [event]
|
||||
(dom/prevent-default event)
|
||||
(dom/stop-propagation event)
|
||||
(on-toggle-collapse tree-path)))
|
||||
|
||||
on-double-click
|
||||
(mf/use-fn (mf/deps id) #(on-start-edition id))
|
||||
(mf/use-fn (mf/deps id) #(on-start-edition (group-edition-id id)))
|
||||
|
||||
on-checkbox-click
|
||||
(mf/use-fn
|
||||
@@ -267,6 +274,7 @@
|
||||
{:position (dom/get-client-position event)
|
||||
:is-group false
|
||||
:id id
|
||||
:edition-id id
|
||||
:path tree-path})))))
|
||||
|
||||
on-double-click
|
||||
@@ -398,7 +406,7 @@
|
||||
:is-active (is-token-set-group-active path)
|
||||
:is-selected false
|
||||
:is-draggable is-draggable
|
||||
:is-editing (= edition-id id)
|
||||
:is-editing (= edition-id (group-edition-id id))
|
||||
:is-collapsed (collapsed? path)
|
||||
:on-select on-select
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
|
||||
(mf/defc menu*
|
||||
{::mf/private true}
|
||||
[{:keys [is-group id path]}]
|
||||
[{:keys [is-group id edition-id path]}]
|
||||
(let [create-set-at-path
|
||||
(mf/use-fn (mf/deps path) #(st/emit! (dt/start-token-set-creation path)))
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
(mf/use-fn
|
||||
(mf/deps id)
|
||||
(fn []
|
||||
(st/emit! (dt/start-token-set-edition id))))
|
||||
(st/emit! (dt/start-token-set-edition edition-id))))
|
||||
|
||||
on-delete
|
||||
(mf/use-fn
|
||||
@@ -57,7 +57,7 @@
|
||||
|
||||
(mf/defc token-set-context-menu*
|
||||
[]
|
||||
(let [{:keys [position is-group id path]}
|
||||
(let [{:keys [position is-group id edition-id path]}
|
||||
(mf/deref ref:token-sets-context-menu)
|
||||
|
||||
position-top
|
||||
@@ -78,4 +78,5 @@
|
||||
:on-context-menu prevent-default}
|
||||
[:> menu* {:is-group is-group
|
||||
:id id
|
||||
:edition-id edition-id
|
||||
:path path}]]]))
|
||||
|
||||
@@ -468,10 +468,7 @@
|
||||
(fn [event]
|
||||
(dom/prevent-default event)
|
||||
(let [point (gpt/point (.-clientX event) (.-clientY event))
|
||||
viewport-coord (uwvv/point->viewport point)
|
||||
asset-id (-> (dnd/get-data event "text/asset-id") uuid/uuid)
|
||||
asset-name (dnd/get-data event "text/asset-name")
|
||||
asset-type (dnd/get-data event "text/asset-type")]
|
||||
viewport-coord (uwvv/point->viewport point)]
|
||||
(cond
|
||||
(dnd/has-type? event "penpot/shape")
|
||||
(let [shape (dnd/get-data event "penpot/shape")
|
||||
@@ -516,25 +513,6 @@
|
||||
(assoc params :blobs (map wapi/data-uri->blob data)))]
|
||||
(st/emit! (dwm/upload-media-workspace params)))
|
||||
|
||||
;; Will trigger when the user drags an SVG asset from the assets panel
|
||||
(and (dnd/has-type? event "text/asset-id") (= asset-type "image/svg+xml"))
|
||||
(let [path (cfg/resolve-file-media {:id asset-id})
|
||||
params {:file-id (:id file)
|
||||
:position viewport-coord
|
||||
:uris [path]
|
||||
:name asset-name
|
||||
:mtype asset-type}]
|
||||
(st/emit! (dwm/upload-media-workspace params)))
|
||||
|
||||
;; Will trigger when the user drags an image from the assets SVG
|
||||
(dnd/has-type? event "text/asset-id")
|
||||
(let [params {:file-id (:id file)
|
||||
:object-id asset-id
|
||||
:name asset-name}]
|
||||
(st/emit! (dwm/clone-media-object
|
||||
(with-meta params
|
||||
{:on-success #(st/emit! (dwm/image-uploaded % viewport-coord))}))))
|
||||
|
||||
;; Will trigger when the user drags a file from their file explorer into the viewport
|
||||
;; Or the user pastes an image
|
||||
;; Or the user uploads an image using the image tool
|
||||
|
||||
@@ -969,7 +969,7 @@
|
||||
|
||||
:else
|
||||
(let [file-id (:current-file-id @st/state)
|
||||
library-id (uuid/uuid library-id)]
|
||||
library-id (uuid/parse library-id)]
|
||||
(->> st/stream
|
||||
(rx/filter (ptk/type? ::dwl/attach-library-finished))
|
||||
(rx/take 1)
|
||||
|
||||
@@ -160,7 +160,7 @@
|
||||
(u/display-not-valid :getShapeById shape-id)
|
||||
|
||||
:else
|
||||
(let [shape-id (uuid/uuid shape-id)
|
||||
(let [shape-id (uuid/parse shape-id)
|
||||
shape (u/locate-shape file-id id shape-id)]
|
||||
(when (some? shape)
|
||||
(shape/shape-proxy plugin-id file-id id shape-id)))))
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
(defn parse-id
|
||||
[id]
|
||||
(when id (uuid/uuid id)))
|
||||
(when id (uuid/parse id)))
|
||||
|
||||
(defn parse-keyword
|
||||
[kw]
|
||||
|
||||
@@ -432,7 +432,7 @@
|
||||
(let [id (obj/get self "$id")
|
||||
value (mapv #(shadow-defaults (parser/parse-shadow %)) value)]
|
||||
(cond
|
||||
(not (sm/validate [:vector ::ctss/shadow] value))
|
||||
(not (sm/validate [:vector ctss/schema:shadow] value))
|
||||
(u/display-not-valid :shadows value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
|
||||
@@ -50,7 +50,8 @@
|
||||
[headers]
|
||||
(into {} (map vec) (seq (.entries ^js headers))))
|
||||
|
||||
(def default-headers
|
||||
(defn default-headers
|
||||
[]
|
||||
{"x-frontend-version" (:full cfg/version)})
|
||||
|
||||
(defn fetch
|
||||
@@ -74,7 +75,7 @@
|
||||
|
||||
headers (cond-> headers
|
||||
(not omit-default-headers)
|
||||
(d/merge default-headers))
|
||||
(merge (default-headers)))
|
||||
|
||||
headers (-update-headers body headers)
|
||||
|
||||
|
||||
@@ -43,14 +43,14 @@
|
||||
|
||||
(defn read-json-key
|
||||
[m]
|
||||
(or (sm/parse-uuid m)
|
||||
(or (uuid/parse m)
|
||||
(json/read-kebab-key m)))
|
||||
|
||||
(defn read-json-val
|
||||
[m]
|
||||
(cond
|
||||
(and (string? m)
|
||||
(re-matches sm/uuid-rx m))
|
||||
(re-matches uuid/regex m))
|
||||
(uuid/uuid m)
|
||||
|
||||
(and (string? m)
|
||||
@@ -521,8 +521,8 @@
|
||||
id (resolve old-id)
|
||||
path (get-in node [:attrs :penpot:path] "")
|
||||
type (parser/get-type content)
|
||||
main-instance-id (resolve (uuid (get-in node [:attrs :penpot:main-instance-id] "")))
|
||||
main-instance-page (resolve (uuid (get-in node [:attrs :penpot:main-instance-page] "")))
|
||||
main-instance-id (resolve (uuid/parse (get-in node [:attrs :penpot:main-instance-id] "")))
|
||||
main-instance-page (resolve (uuid/parse (get-in node [:attrs :penpot:main-instance-page] "")))
|
||||
data (-> (parser/parse-data type content)
|
||||
(assoc :path path)
|
||||
(assoc :id id)
|
||||
@@ -547,12 +547,12 @@
|
||||
old-id (parser/get-id node)
|
||||
id (resolve old-id)
|
||||
path (get-in node [:attrs :penpot:path] "")
|
||||
main-instance-id (resolve (uuid (get-in node [:attrs :penpot:main-instance-id] "")))
|
||||
main-instance-page (resolve (uuid (get-in node [:attrs :penpot:main-instance-page] "")))
|
||||
main-instance-id (resolve (uuid/parse (get-in node [:attrs :penpot:main-instance-id] "")))
|
||||
main-instance-page (resolve (uuid/parse (get-in node [:attrs :penpot:main-instance-page] "")))
|
||||
main-instance-x (-> (get-in node [:attrs :penpot:main-instance-x] "") (d/parse-double))
|
||||
main-instance-y (-> (get-in node [:attrs :penpot:main-instance-y] "") (d/parse-double))
|
||||
main-instance-parent (resolve (uuid (get-in node [:attrs :penpot:main-instance-parent] "")))
|
||||
main-instance-frame (resolve (uuid (get-in node [:attrs :penpot:main-instance-frame] "")))
|
||||
main-instance-parent (resolve (uuid/parse (get-in node [:attrs :penpot:main-instance-parent] "")))
|
||||
main-instance-frame (resolve (uuid/parse (get-in node [:attrs :penpot:main-instance-frame] "")))
|
||||
type (parser/get-type content)
|
||||
|
||||
data (-> (parser/parse-data type content)
|
||||
|
||||
@@ -20,9 +20,6 @@
|
||||
(def url-regex
|
||||
#"url\(#([^\)]*)\)")
|
||||
|
||||
(def uuid-regex
|
||||
#"\w{8}-\w{4}-\w{4}-\w{4}-\w{12}")
|
||||
|
||||
(def uuid-regex-prefix
|
||||
#"\w{8}-\w{4}-\w{4}-\w{4}-\w{12}-")
|
||||
|
||||
@@ -84,7 +81,7 @@
|
||||
(defn get-id
|
||||
[node]
|
||||
(let [attr-id (get-in node [:attrs :id])
|
||||
id (when (string? attr-id) (re-find uuid-regex attr-id))]
|
||||
id (when (string? attr-id) (re-find uuid/regex attr-id))]
|
||||
(when (some? id)
|
||||
(uuid/uuid id))))
|
||||
|
||||
@@ -189,10 +186,10 @@
|
||||
[m]
|
||||
(letfn [(convert [value]
|
||||
(cond
|
||||
(and (string? value) (re-matches uuid-regex value))
|
||||
(and (string? value) (re-matches uuid/regex value))
|
||||
(uuid/uuid value)
|
||||
|
||||
(and (keyword? value) (re-matches uuid-regex (d/name value)))
|
||||
(and (keyword? value) (re-matches uuid/regex (d/name value)))
|
||||
(uuid/uuid (d/name value))
|
||||
|
||||
(vector? value)
|
||||
@@ -429,11 +426,11 @@
|
||||
(defn add-library-refs
|
||||
[props node]
|
||||
|
||||
(let [stroke-color-ref-id (get-meta node :stroke-color-ref-id uuid/uuid)
|
||||
stroke-color-ref-file (get-meta node :stroke-color-ref-file uuid/uuid)
|
||||
component-id (get-meta node :component-id uuid/uuid)
|
||||
component-file (get-meta node :component-file uuid/uuid)
|
||||
shape-ref (get-meta node :shape-ref uuid/uuid)
|
||||
(let [stroke-color-ref-id (get-meta node :stroke-color-ref-id uuid/parse)
|
||||
stroke-color-ref-file (get-meta node :stroke-color-ref-file uuid/parse)
|
||||
component-id (get-meta node :component-id uuid/parse)
|
||||
component-file (get-meta node :component-file uuid/parse)
|
||||
shape-ref (get-meta node :shape-ref uuid/parse)
|
||||
component-root? (get-meta node :component-root str->bool)
|
||||
main-instance? (get-meta node :main-instance str->bool)
|
||||
touched (get-meta node :touched parse-touched)]
|
||||
@@ -463,8 +460,8 @@
|
||||
[props node svg-data]
|
||||
|
||||
(let [fill (:fill svg-data)
|
||||
fill-color-ref-id (get-meta node :fill-color-ref-id uuid/uuid)
|
||||
fill-color-ref-file (get-meta node :fill-color-ref-file uuid/uuid)
|
||||
fill-color-ref-id (get-meta node :fill-color-ref-id uuid/parse)
|
||||
fill-color-ref-file (get-meta node :fill-color-ref-file uuid/parse)
|
||||
meta-fill-color (get-meta node :fill-color)
|
||||
meta-fill-opacity (get-meta node :fill-opacity)
|
||||
meta-fill-color-gradient (if (str/starts-with? meta-fill-color "url#fill-color-gradient")
|
||||
@@ -627,7 +624,7 @@
|
||||
(let [attrs (-> node :attrs remove-penpot-prefix)]
|
||||
{:id (uuid/next)
|
||||
:name (-> attrs :name)
|
||||
:starting-frame (-> attrs :starting-frame uuid)}))
|
||||
:starting-frame (-> attrs :starting-frame uuid/parse)}))
|
||||
|
||||
(defn parse-flows [node]
|
||||
(let [flows-node (get-data node :penpot:flows)]
|
||||
@@ -638,7 +635,7 @@
|
||||
id (uuid/next)]
|
||||
[id
|
||||
{:id id
|
||||
:frame-id (when (:frame-id attrs) (-> attrs :frame-id uuid))
|
||||
:frame-id (when (:frame-id attrs) (-> attrs :frame-id uuid/parse))
|
||||
:axis (-> attrs :axis keyword)
|
||||
:position (-> attrs :position d/parse-double)}]))
|
||||
|
||||
@@ -775,8 +772,8 @@
|
||||
(parse-gradient node (get-meta fill-node :fill-color)))
|
||||
:fill-image (when fill-image-id
|
||||
(get images fill-image-id))
|
||||
:fill-color-ref-file (get-meta fill-node :fill-color-ref-file uuid/uuid)
|
||||
:fill-color-ref-id (get-meta fill-node :fill-color-ref-id uuid/uuid)
|
||||
:fill-color-ref-file (get-meta fill-node :fill-color-ref-file uuid/parse)
|
||||
:fill-color-ref-id (get-meta fill-node :fill-color-ref-id uuid/parse)
|
||||
:fill-opacity (get-meta fill-node :fill-opacity d/parse-double)})))
|
||||
(mapv d/without-nils)
|
||||
(filterv #(not= (:fill-color %) "none")))]
|
||||
@@ -800,8 +797,8 @@
|
||||
(parse-gradient node (get-meta stroke-node :stroke-color)))
|
||||
:stroke-image (when stroke-image-id
|
||||
(get images stroke-image-id))
|
||||
:stroke-color-ref-file (get-meta stroke-node :stroke-color-ref-file uuid/uuid)
|
||||
:stroke-color-ref-id (get-meta stroke-node :stroke-color-ref-id uuid/uuid)
|
||||
:stroke-color-ref-file (get-meta stroke-node :stroke-color-ref-file uuid/parse)
|
||||
:stroke-color-ref-id (get-meta stroke-node :stroke-color-ref-id uuid/parse)
|
||||
:stroke-opacity (get-meta stroke-node :stroke-opacity d/parse-double)
|
||||
:stroke-style (get-meta stroke-node :stroke-style keyword)
|
||||
:stroke-width (get-meta stroke-node :stroke-width d/parse-double)
|
||||
@@ -993,7 +990,7 @@
|
||||
align-self
|
||||
justify-self
|
||||
shapes]} (-> cell-node :attrs remove-penpot-prefix)
|
||||
id (uuid/uuid id)]
|
||||
id (uuid/parse id)]
|
||||
[id (d/without-nils
|
||||
{:id id
|
||||
:area-name area-name
|
||||
@@ -1006,7 +1003,7 @@
|
||||
:justify-self (keyword justify-self)
|
||||
:shapes (if (and (some? shapes) (d/not-empty? shapes))
|
||||
(->> (str/split shapes " ")
|
||||
(mapv uuid/uuid))
|
||||
(mapv uuid/parse))
|
||||
[])})])))
|
||||
(into {}))))
|
||||
|
||||
@@ -1154,7 +1151,7 @@
|
||||
(assoc :delay (get-meta node :delay d/parse-double))
|
||||
|
||||
(ctsi/has-destination interaction)
|
||||
(assoc :destination (get-meta node :destination uuid/uuid)
|
||||
(assoc :destination (get-meta node :destination uuid/parse)
|
||||
:preserve-scroll (get-meta node :preserve-scroll str->bool))
|
||||
|
||||
(ctsi/has-url interaction)
|
||||
|
||||
@@ -42,12 +42,11 @@
|
||||
:http-body body})))
|
||||
|
||||
(defn- request-data-for-thumbnail
|
||||
[file-id revn features]
|
||||
[file-id revn]
|
||||
(let [path "api/rpc/command/get-file-data-for-thumbnail"
|
||||
params {:file-id file-id
|
||||
:revn revn
|
||||
:strip-frames-with-thumbnails true
|
||||
:features features}
|
||||
:strip-frames-with-thumbnails true}
|
||||
request {:method :get
|
||||
:uri (u/join cf/public-uri path)
|
||||
:credentials "include"
|
||||
@@ -86,6 +85,6 @@
|
||||
nil)))
|
||||
|
||||
(defmethod impl/handler :thumbnails/generate-for-file
|
||||
[{:keys [file-id revn features] :as message} _]
|
||||
(->> (request-data-for-thumbnail file-id revn features)
|
||||
[{:keys [file-id revn] :as message} _]
|
||||
(->> (request-data-for-thumbnail file-id revn)
|
||||
(rx/map render-thumbnail)))
|
||||
|
||||
@@ -179,7 +179,7 @@
|
||||
[state name]
|
||||
(let [objects (dsh/lookup-page-objects state)
|
||||
result (or (d/seek (fn [shape] (= name (:name shape))) (vals objects))
|
||||
(get objects (uuid/uuid name)))]
|
||||
(get objects (uuid/parse name)))]
|
||||
result))
|
||||
|
||||
(defn ^:export dump-object
|
||||
@@ -222,12 +222,12 @@
|
||||
(defn ^:export select-by-object-id
|
||||
[object-id]
|
||||
(let [[_ page-id shape-id _] (str/split object-id #"/")]
|
||||
(st/emit! (dcm/go-to-workspace :page-id (uuid/uuid page-id)))
|
||||
(st/emit! (dws/select-shape (uuid/uuid shape-id)))))
|
||||
(st/emit! (dcm/go-to-workspace :page-id (uuid/parse page-id)))
|
||||
(st/emit! (dws/select-shape (uuid/parse shape-id)))))
|
||||
|
||||
(defn ^:export select-by-id
|
||||
[shape-id]
|
||||
(st/emit! (dws/select-shape (uuid/uuid shape-id))))
|
||||
(st/emit! (dws/select-shape (uuid/parse shape-id))))
|
||||
|
||||
(defn dump-tree'
|
||||
([state] (dump-tree' state false false false))
|
||||
@@ -255,7 +255,7 @@
|
||||
file (dsh/lookup-file state)
|
||||
libraries (get state :files)
|
||||
shape-id (if (some? shape-id)
|
||||
(uuid/uuid shape-id)
|
||||
(uuid/parse shape-id)
|
||||
(first (dsh/lookup-selected state)))]
|
||||
(if (some? shape-id)
|
||||
(ctf/dump-subtree file page-id shape-id libraries {:show-ids show-ids
|
||||
@@ -369,7 +369,7 @@
|
||||
(let [file (dsh/lookup-file @st/state)
|
||||
libraries (get @st/state :files)]
|
||||
(try
|
||||
(->> (if-let [shape-id (some-> shape-id parse-uuid)]
|
||||
(->> (if-let [shape-id (some-> shape-id uuid/parse)]
|
||||
(let [page (dm/get-in file [:data :pages-index (get @st/state :current-page-id)])]
|
||||
(cfv/validate-shape shape-id file page libraries))
|
||||
(cfv/validate-file file libraries))
|
||||
@@ -426,6 +426,15 @@
|
||||
[]
|
||||
(st/emit! (dw/find-components-norefs)))
|
||||
|
||||
(defn- set-shape-ref*
|
||||
[id shape-ref]
|
||||
(ptk/reify ::set-shape-ref
|
||||
ptk/WatchEvent
|
||||
(watch [_ _ _]
|
||||
(let [shape-id (uuid/parse id)
|
||||
shape-ref (uuid/parse shape-ref)]
|
||||
(rx/of (dw/update-shape shape-id {:shape-ref shape-ref}))))))
|
||||
|
||||
(defn ^:export set-shape-ref
|
||||
[id shape-ref]
|
||||
(st/emit! (dw/set-shape-ref id shape-ref)))
|
||||
(st/emit! (set-shape-ref* id shape-ref)))
|
||||
|
||||
Reference in New Issue
Block a user